import { v4 as uuidv4 } from "uuid";
import type { NoteIndexItem, NoteContent } from "./notes.types.js";
import { readIndex, writeIndex, readNoteContent, writeNoteContent, deleteNoteFile } from "./notes.storage.js";
import { uploadDocument, updateEmbeddings } from "../anythingllm/anythingllm.client.js";
import { config } from "../config.js";
import { logError } from "../utils/logger.js";
import type { CleanupQueue } from "../queue/queue.js";

function nowIso(): string {
  return new Date().toISOString();
}

function buildMetadata(title: string, content: string) {
  const description = content.replace(/\s+/g, " ").slice(0, 120);
  return {
    title,
    docAuthor: "Note App",
    description,
    docSource: "notes-app"
  };
}

export async function listNotes(): Promise<NoteIndexItem[]> {
  const index = await readIndex();
  return index.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
}

export async function getNote(id: string): Promise<NoteContent | null> {
  const index = await readIndex();
  const item = index.find((note) => note.id === id);
  if (!item) {
    return null;
  }
  return readNoteContent(item.id, item.filename);
}

export async function createNote(title: string, content: string, queue: CleanupQueue): Promise<NoteIndexItem> {
  const id = uuidv4();
  const filename = `${id}.md`;
  const timestamp = nowIso();

  await writeNoteContent(filename, title, content);

  const newItem: NoteIndexItem = {
    id,
    title,
    filename,
    createdAt: timestamp,
    updatedAt: timestamp,
    sync: {
      status: "syncing"
    }
  };

  const index = await readIndex();
  index.push(newItem);
  await writeIndex(index);

  void syncNote(id, queue);
  return newItem;
}

export async function updateNote(id: string, title: string, content: string, queue: CleanupQueue): Promise<NoteIndexItem | null> {
  const index = await readIndex();
  const item = index.find((note) => note.id === id);
  if (!item) {
    return null;
  }

  await writeNoteContent(item.filename, title, content);

  item.title = title;
  item.updatedAt = nowIso();
  item.sync.status = "syncing";
  item.sync.lastError = undefined;
  await writeIndex(index);

  void syncNote(id, queue);
  return item;
}

export async function deleteNote(id: string, queue: CleanupQueue): Promise<boolean> {
  const index = await readIndex();
  const itemIndex = index.findIndex((note) => note.id === id);
  if (itemIndex === -1) {
    return false;
  }
  const [item] = index.splice(itemIndex, 1);
  await writeIndex(index);
  await deleteNoteFile(item.filename);

  if (item.sync.anythingllmLocation) {
    try {
      await updateEmbeddings([], [item.sync.anythingllmLocation]);
      await queue.enqueue({
        id: item.id,
        oldLocation: item.sync.anythingllmLocation,
        attempts: 0,
        nextRunAt: new Date().toISOString()
      });
    } catch (error) {
      logError("Silme senkron hatasi", { error: (error as Error).message });
    }
  }

  return true;
}

export async function manualSync(id: string, queue: CleanupQueue): Promise<boolean> {
  const index = await readIndex();
  const item = index.find((note) => note.id === id);
  if (!item) {
    return false;
  }

  item.sync.status = "syncing";
  item.sync.lastError = undefined;
  await writeIndex(index);

  void syncNote(id, queue);
  return true;
}

async function syncNote(id: string, queue: CleanupQueue): Promise<void> {
  const index = await readIndex();
  const item = index.find((note) => note.id === id);
  if (!item) {
    return;
  }

  const oldLocation = item.sync.anythingllmLocation;

  try {
    const note = await readNoteContent(item.id, item.filename);
    const metadata = buildMetadata(note.title, note.content);

    const uploadResponse = await uploadDocument(`${config.NOTES_DIR}/${item.filename}`, metadata);
    const newDoc = uploadResponse.documents[0];
    if (!newDoc) {
      throw new Error("Upload cevabinda dokuman bulunamadi");
    }

    await updateEmbeddings([newDoc.location], oldLocation ? [oldLocation] : []);

    item.sync.status = "synced";
    item.sync.anythingllmLocation = newDoc.location;
    item.sync.anythingllmDocumentId = newDoc.id;
    item.sync.lastSyncedAt = nowIso();
    item.sync.cleanupPending = false;
    item.sync.lastError = undefined;

    await writeIndex(index);

    if (oldLocation) {
      item.sync.cleanupPending = true;
      await writeIndex(index);
      await queue.enqueue({
        id: item.id,
        oldLocation,
        attempts: 0,
        nextRunAt: new Date().toISOString()
      });
    }
  } catch (error) {
    logError("Senkronizasyon hatasi", {
      noteId: item.id,
      error: (error as Error).message,
      oldLocation: oldLocation ?? null
    });
    item.sync.status = "error";
    item.sync.lastError = (error as Error).message;
    await writeIndex(index);
  }
}
