import { get, writable } from "svelte/store";
import {
  deleteEntry as deleteEntryCommand,
  listEntries as listEntriesCommand,
  loadEntry as loadEntryCommand,
  saveEntry as saveEntryCommand,
  searchEntries as searchEntriesCommand,
  type EntryListItemDto,
  type EntrySearchRequestDto,
} from "$lib/backend/entries";

export type EntryItem = {
  id: string;
  label: string;
  initialContent: string;
  filePath?: string;
  date?: string;
};

const initialEntries: EntryItem[] = [];

export const entriesStore = writable<EntryItem[]>(initialEntries);
export const entriesBusyStore = writable(false);

function toStoreId(filePath: string): string {
  return `entries/file/${encodeURIComponent(filePath)}`;
}

function toBackendPath(id: string): string | null {
  const prefix = "entries/file/";
  if (!id.startsWith(prefix)) return null;
  const encoded = id.slice(prefix.length).trim();
  if (!encoded) return null;

  try {
    const decoded = decodeURIComponent(encoded);
    return decoded || null;
  } catch {
    return null;
  }
}

function toLabel(fileName: string): string {
  const normalized = fileName.trim();
  if (!normalized) return "Untitled Entry";
  return normalized.replace(/\.md$/i, "");
}

function upsertById(items: EntryItem[], next: EntryItem): EntryItem[] {
  const idx = items.findIndex((item) => item.id === next.id);
  if (idx === -1) return [next, ...items];
  const clone = [...items];
  clone[idx] = next;
  return clone;
}

function fromListDto(dto: EntryListItemDto): EntryItem {
  return {
    id: toStoreId(dto.filePath),
    label: toLabel(dto.fileName),
    initialContent: "",
    filePath: dto.filePath,
  };
}

function fromLoadResult(
  result: Awaited<ReturnType<typeof loadEntryCommand>>,
): EntryItem {
  return {
    id: toStoreId(result.filePath),
    label: toLabel(result.fileName),
    initialContent: result.entry.rawContent,
    filePath: result.filePath,
    date: result.entry.date,
  };
}

export function getDefaultEntry(items: EntryItem[]): EntryItem | undefined {
  return items[0];
}

export function createEntryDraft(): EntryItem {
  const id = `entries/draft-${Date.now()}`;
  return {
    id,
    label: "Untitled Entry",
    initialContent: "# Untitled Entry\n\nStart writing...",
  };
}

export async function hydrateEntries(): Promise<void> {
  entriesBusyStore.set(true);
  try {
    const items = await listEntriesCommand();
    const mapped = items.map(fromListDto);
    entriesStore.set(mapped);
  } catch (error) {
    console.error("[entries] hydrate:error", error);
    throw error;
  } finally {
    entriesBusyStore.set(false);
  }
}

export async function loadEntryByStoreId(
  storeId: string,
): Promise<EntryItem | null> {
  const filePath = toBackendPath(storeId);
  if (!filePath) return null;

  try {
    const loaded = await loadEntryCommand(filePath);
    const item = fromLoadResult(loaded);
    entriesStore.update((items) => upsertById(items, item));
    return item;
  } catch (error) {
    console.error("[entries] load:error", { storeId, filePath, error });
    throw error;
  }
}

export async function saveEntryFromStore(
  storeId: string,
  content: string,
  mode?: string,
): Promise<EntryItem | null> {
  const trimmed = content?.trim();
  if (!trimmed) return null;

  const existingPath = toBackendPath(storeId);
  let payload: {
    content: string;
    filePath?: string;
    mode?: string;
    fileName?: string;
  };

  if (existingPath) {
    payload = { content: trimmed, filePath: existingPath, mode };
  } else {
    const draft = get(entriesStore).find((item) => item.id === storeId);
    payload = { content: trimmed, mode, fileName: draft?.label };
  }
  try {
    const saved = await saveEntryCommand(payload);
    const loaded = await loadEntryCommand(saved.filePath);
    const item = fromLoadResult(loaded);
    entriesStore.update((items) => {
      const filtered = existingPath
        ? items
        : items.filter((i) => i.id !== storeId);
      return upsertById(filtered, item);
    });
    return item;
  } catch (error) {
    console.error("[entries] save:error", { storeId, error });
    throw error;
  }
}

export async function searchEntriesAsItems(
  payload: EntrySearchRequestDto,
): Promise<EntryItem[]> {
  const results = await searchEntriesCommand(payload);
  const toSearchPath = (fileName: string): string =>
    `db://entry/${encodeURIComponent(fileName)}`;
  const mapped = results.map((result) => ({
    id: toStoreId(toSearchPath(result.fileName)),
    label: toLabel(result.fileName),
    initialContent: result.entry.rawContent,
    filePath: toSearchPath(result.fileName),
    date: result.entry.date,
  }));
  return mapped;
}

export async function deleteEntryByStoreId(storeId: string): Promise<boolean> {
  if (storeId.startsWith("entries/draft-")) {
    entriesStore.update((items) => items.filter((item) => item.id !== storeId));
    return true;
  }

  const filePath = toBackendPath(storeId);
  if (!filePath) return false;

  try {
    const ok = await deleteEntryCommand(filePath);
    if (!ok) return false;
    entriesStore.update((items) => items.filter((item) => item.id !== storeId));
    return true;
  } catch (error) {
    console.error("[entries] delete:error", { storeId, error });
    return false;
  }
}

export function hasEntry(storeId: string): boolean {
  return get(entriesStore).some((item) => item.id === storeId);
}
