import { useCallback, useEffect, useState } from 'react';
import {
  kanbanApi,
  type Board,
  type Column,
  type DocPage,
  type ReferenceType,
} from '../../lib/kanbanApi';

interface UseKanbanBoard {
  board: Board | null;
  pages: DocPage[];
  loading: boolean;
  /** Last mutation error, if any (cleared on next successful action/reload). */
  error: string | null;
  reload: () => Promise<void>;
  moveCard: (id: string, status: Column) => Promise<void>;
  addComment: (cardId: string, text: string, author: string) => Promise<void>;
  deleteComment: (commentId: string, cardId: string) => Promise<void>;
  addTag: (cardId: string, tag: string) => Promise<void>;
  removeTag: (cardId: string, tag: string) => Promise<void>;
  addReference: (
    cardId: string,
    ref: { label: string; type: ReferenceType; href: string },
  ) => Promise<void>;
  removeReference: (cardId: string, href: string) => Promise<void>;
}

export function useKanbanBoard(): UseKanbanBoard {
  const [board, setBoard] = useState<Board | null>(null);
  const [pages, setPages] = useState<DocPage[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const reload = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const [b, p] = await Promise.all([kanbanApi.getBoard(), kanbanApi.getPages()]);
      setBoard(b);
      setPages(p);
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to load board');
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    void reload();
  }, [reload]);

  /** Run a mutation, optimistic-local-updating via `apply`, then reconcile. */
  const run = useCallback(
    async (
      apply: () => void,
      remote: () => Promise<unknown>,
    ): Promise<void> => {
      setError(null);
      apply(); // optimistic local update
      try {
        await remote();
      } catch (e) {
        setError(e instanceof Error ? e.message : 'Mutation failed');
        await reload(); // reconcile on failure
      }
    },
    [reload],
  );

  const moveCard = useCallback(
    (id: string, status: Column) =>
      run(
        () =>
          setBoard((prev) =>
            prev
              ? { ...prev, cards: prev.cards.map((c) => (c.id === id ? { ...c, status } : c)) }
              : prev,
          ),
        () => kanbanApi.moveCard(id, status),
      ),
    [run],
  );

  const addComment = useCallback(
    async (cardId: string, text: string, author: string) => {
      setError(null);
      try {
        const comment = await kanbanApi.addComment(cardId, text, author);
        setBoard((prev) =>
          prev
            ? {
                ...prev,
                cards: prev.cards.map((c) =>
                  c.id === cardId ? { ...c, comments: [...c.comments, comment] } : c,
                ),
              }
            : prev,
        );
      } catch (e) {
        setError(e instanceof Error ? e.message : 'Failed to add comment');
      }
    },
    [],
  );

  const deleteComment = useCallback(
    (commentId: string, cardId: string) =>
      run(
        () =>
          setBoard((prev) =>
            prev
              ? {
                  ...prev,
                  cards: prev.cards.map((c) =>
                    c.id === cardId
                      ? { ...c, comments: c.comments.filter((cm) => cm.id !== commentId) }
                      : c,
                  ),
                }
              : prev,
          ),
        () => kanbanApi.deleteComment(commentId),
      ),
    [run],
  );

  const addTag = useCallback(
    (cardId: string, tag: string) =>
      run(
        () =>
          setBoard((prev) =>
            prev
              ? {
                  ...prev,
                  cards: prev.cards.map((c) =>
                    c.id === cardId && !c.tags.includes(tag)
                      ? { ...c, tags: [...c.tags, tag] }
                      : c,
                  ),
                }
              : prev,
          ),
        () => kanbanApi.addTag(cardId, tag),
      ),
    [run],
  );

  const removeTag = useCallback(
    (cardId: string, tag: string) =>
      run(
        () =>
          setBoard((prev) =>
            prev
              ? {
                  ...prev,
                  cards: prev.cards.map((c) =>
                    c.id === cardId ? { ...c, tags: c.tags.filter((t) => t !== tag) } : c,
                  ),
                }
              : prev,
          ),
        () => kanbanApi.removeTag(cardId, tag),
      ),
    [run],
  );

  const addReference = useCallback(
    (cardId: string, ref: { label: string; type: ReferenceType; href: string }) =>
      run(
        () =>
          setBoard((prev) =>
            prev
              ? {
                  ...prev,
                  cards: prev.cards.map((c) =>
                    c.id === cardId && !c.references.some((r) => r.href === ref.href)
                      ? { ...c, references: [...c.references, { ...ref, removable: true }] }
                      : c,
                  ),
                }
              : prev,
          ),
        () => kanbanApi.addReference(cardId, ref),
      ),
    [run],
  );

  const removeReference = useCallback(
    (cardId: string, href: string) =>
      run(
        () =>
          setBoard((prev) =>
            prev
              ? {
                  ...prev,
                  cards: prev.cards.map((c) =>
                    c.id === cardId
                      ? { ...c, references: c.references.filter((r) => r.href !== href) }
                      : c,
                  ),
                }
              : prev,
          ),
        () => kanbanApi.removeReference(cardId, href),
      ),
    [run],
  );

  return {
    board,
    pages,
    loading,
    error,
    reload,
    moveCard,
    addComment,
    deleteComment,
    addTag,
    removeTag,
    addReference,
    removeReference,
  };
}
