import { get, writable } from "svelte/store";
import {
  createTodoItem as createTodoItemCommand,
  createTodoList as createTodoListCommand,
  deleteTodoItem as deleteTodoItemCommand,
  deleteTodoList as deleteTodoListCommand,
  listTodoLists,
  updateTodoItem as updateTodoItemCommand,
  updateTodoList as updateTodoListCommand,
  type TodoListDto,
} from "$lib/backend/todos";

// TodoItem keeps a numeric `id` for local array operations (used by EditorPanel)
// plus a `backendId` (guid string) for backend persistence.
export type TodoItem = {
  id: number;
  text: string;
  done: boolean;
  backendId?: string;
};
export type TodoListMeta = { id: string; label: string; backendId?: string };

export const todoListsStore = writable<TodoListMeta[]>([]);
export const todosStore = writable<Record<string, TodoItem[]>>({});
export const todosBusyStore = writable(false);

// ── ID helpers ───────────────────────────────────────────────────

function toStoreId(guid: string): string {
  return `todos/${guid}`;
}

function toBackendId(storeId: string): string | null {
  const prefix = "todos/";
  if (!storeId.startsWith(prefix)) return null;
  const backendId = storeId.slice(prefix.length).trim();
  return backendId || null;
}

export function createTodoId(): number {
  return Date.now() + Math.floor(Math.random() * 1000);
}

// ── DTO mapping ──────────────────────────────────────────────────

function dtoToMeta(dto: TodoListDto): TodoListMeta {
  return {
    id: toStoreId(dto.id),
    label: dto.label,
    backendId: dto.id,
  };
}

function dtoToItems(dto: TodoListDto): TodoItem[] {
  return dto.items.map((item, index) => ({
    id: createTodoId() + index,
    text: item.text,
    done: item.done,
    backendId: item.id,
  }));
}

// ── Hydration ────────────────────────────────────────────────────

export async function hydrateTodos(): Promise<void> {
  todosBusyStore.set(true);
  try {
    const lists = await listTodoLists();

    const metas: TodoListMeta[] = lists.map(dtoToMeta);
    const items: Record<string, TodoItem[]> = {};
    for (const dto of lists) {
      items[toStoreId(dto.id)] = dtoToItems(dto);
    }

    todoListsStore.set(metas);
    todosStore.set(items);
  } catch (error) {
    console.error("[todos] hydrate:error", error);
    throw error;
  } finally {
    todosBusyStore.set(false);
  }
}

// ── List CRUD ────────────────────────────────────────────────────

export async function createTodoListFromLabel(
  label: string,
): Promise<{ meta: TodoListMeta; items: TodoItem[] }> {
  const resolvedLabel = label.trim() || "New List";
  const created = await createTodoListCommand({ label: resolvedLabel });

  const meta = dtoToMeta(created);
  todoListsStore.update((metas) => [meta, ...metas]);
  todosStore.update((lists) => ({ ...lists, [meta.id]: [] }));
  return { meta, items: [] };
}

export async function deleteTodoListByStoreId(
  storeId: string,
): Promise<boolean> {
  const backendId = toBackendId(storeId);
  if (!backendId) return false;

  const ok = await deleteTodoListCommand(backendId);
  if (!ok) return false;

  todoListsStore.update((metas) => metas.filter((m) => m.id !== storeId));
  todosStore.update((lists) => {
    const { [storeId]: _, ...rest } = lists;
    return rest;
  });
  return true;
}

// ── Item CRUD (backend-backed) ───────────────────────────────────

export async function addTodoItemBackend(
  storeId: string,
  text: string,
): Promise<TodoItem | null> {
  const backendListId = toBackendId(storeId);
  if (!backendListId || !text.trim()) return null;

  const items = get(todosStore)[storeId] ?? [];
  const sortOrder = items.length;

  const created = await createTodoItemCommand({
    listId: backendListId,
    text: text.trim(),
    sortOrder,
  });

  const item: TodoItem = {
    id: createTodoId(),
    text: created.text,
    done: created.done,
    backendId: created.id,
  };

  todosStore.update((lists) => ({
    ...lists,
    [storeId]: [item, ...(lists[storeId] ?? [])],
  }));
  return item;
}

export async function toggleTodoItemBackend(
  storeId: string,
  localId: number,
): Promise<boolean> {
  const items = get(todosStore)[storeId];
  const todo = items?.find((t) => t.id === localId);
  if (!todo?.backendId) return false;

  const ok = await updateTodoItemCommand(todo.backendId, { done: !todo.done });
  if (!ok) return false;

  todosStore.update((lists) => ({
    ...lists,
    [storeId]: (lists[storeId] ?? []).map((t) =>
      t.id === localId ? { ...t, done: !t.done } : t,
    ),
  }));
  return true;
}

export async function updateTodoItemTextBackend(
  storeId: string,
  localId: number,
  text: string,
): Promise<boolean> {
  const items = get(todosStore)[storeId];
  const todo = items?.find((t) => t.id === localId);
  if (!todo?.backendId || !text.trim()) return false;

  const ok = await updateTodoItemCommand(todo.backendId, { text: text.trim() });
  if (!ok) return false;

  todosStore.update((lists) => ({
    ...lists,
    [storeId]: (lists[storeId] ?? []).map((t) =>
      t.id === localId ? { ...t, text: text.trim() } : t,
    ),
  }));
  return true;
}

export async function removeTodoItemBackend(
  storeId: string,
  localId: number,
): Promise<boolean> {
  const items = get(todosStore)[storeId];
  const todo = items?.find((t) => t.id === localId);
  if (!todo?.backendId) return false;

  const ok = await deleteTodoItemCommand(todo.backendId);
  if (!ok) return false;

  todosStore.update((lists) => ({
    ...lists,
    [storeId]: (lists[storeId] ?? []).filter((t) => t.id !== localId),
  }));
  return true;
}

// ── Pure helpers (used by EditorPanel for local state) ───────────

export function serializeTodoList(title: string, todos: TodoItem[]): string {
  const heading = title?.trim() ? `# ${title}` : "# To-Do List";
  const lines = todos.map(
    (todo) => `- [${todo.done ? "x" : " "}] ${todo.text}`,
  );
  return `${heading}\n\n${lines.join("\n")}`;
}

export function parseTodoList(content: string): TodoItem[] {
  const lines = content.replace(/\r\n/g, "\n").split("\n");
  const parsed: TodoItem[] = [];
  for (const line of lines) {
    const match = line.match(/^- \[( |x)\]\s+(.+)$/i);
    if (!match) continue;
    parsed.push({
      id: createTodoId(),
      text: match[2].trim(),
      done: match[1].toLowerCase() === "x",
    });
  }
  return parsed;
}

export function getOrCreateTodoList(
  lists: Record<string, TodoItem[]>,
  documentId: string,
  fallbackContent: string,
): { lists: Record<string, TodoItem[]>; todos: TodoItem[] } {
  const existing = lists[documentId];
  if (existing) {
    return { lists, todos: existing };
  }
  const parsed = parseTodoList(fallbackContent);
  return { lists: { ...lists, [documentId]: parsed }, todos: parsed };
}

export function setTodoList(
  lists: Record<string, TodoItem[]>,
  documentId: string,
  todos: TodoItem[],
): Record<string, TodoItem[]> {
  return { ...lists, [documentId]: todos };
}

export function addTodoItem(todos: TodoItem[], text: string): TodoItem[] {
  return [{ id: createTodoId(), text: text.trim(), done: false }, ...todos];
}

export function toggleTodoItem(todos: TodoItem[], id: number): TodoItem[] {
  return todos.map((todo) =>
    todo.id === id ? { ...todo, done: !todo.done } : todo,
  );
}

export function updateTodoItemText(
  todos: TodoItem[],
  id: number,
  text: string,
): TodoItem[] {
  return todos.map((todo) =>
    todo.id === id ? { ...todo, text: text.trim() } : todo,
  );
}

export function removeTodoItem(todos: TodoItem[], id: number): TodoItem[] {
  return todos.filter((todo) => todo.id !== id);
}

export function createTodoListDraft(): {
  meta: TodoListMeta;
  items: TodoItem[];
} {
  const id = `todos/draft-${Date.now()}`;
  return {
    meta: { id, label: "New List" },
    items: [],
  };
}
