// TipTap-backed narrative editor.
//
// After the pivot the text editor is the canonical surface. It loads its doc
// from /api/projects/[id]/document and saves on a 600ms debounce. Chips remain
// the rendered primitive but are no longer inserted via slash menu — the
// ChipSuggestionDecorator (added separately) suggests chip-ifying terms that
// match the project's taxonomy.

"use client";

import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { useEffect, useMemo, useRef, useState } from "react";
import { ChipNode } from "./ChipNode";
import { ChipFocusContext } from "./FocusContext";
import { fixtureToDoc } from "./fixtureToDoc";
import { ChipSuggestionExtension } from "./ChipSuggestionExtension";
import type { FixtureData } from "../../lib/fixtures/aristotle";
import type { MarkupStyle } from "./Chip";
import { useAnalysis } from "../../lib/workspace/analysisStore";

export type Density = "comfortable" | "compact";

interface TextCanvasProps {
  data: FixtureData;
  projectId: string;
  density?: Density;
  markupStyle?: MarkupStyle;
}

export function TextCanvas({ data, projectId, density = "comfortable", markupStyle = "color" }: TextCanvasProps) {
  const padY = density === "compact" ? 10 : 18;
  const padX = density === "compact" ? 22 : 36;
  const [focusBlockId, setFocusBlockId] = useState<string | null>(null);
  const [initialDoc, setInitialDoc] = useState<unknown | null>(null);
  const [loaded, setLoaded] = useState(false);
  const focusValue = useMemo(() => ({ focusBlockId, setFocusBlockId }), [focusBlockId, setFocusBlockId]);

  const { terms } = useAnalysis();

  // Load the saved doc once on mount; fall back to the fixture if there is none.
  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const res = await fetch(`/api/projects/${encodeURIComponent(projectId)}/document`);
        if (!res.ok) throw new Error(`load doc: ${res.status}`);
        const body = (await res.json()) as { doc: unknown };
        if (cancelled) return;
        setInitialDoc(body.doc ?? fixtureToDoc(data));
      } catch {
        if (!cancelled) setInitialDoc(fixtureToDoc(data));
      } finally {
        if (!cancelled) setLoaded(true);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [projectId, data]);

  const editor = useEditor(
    {
      immediatelyRender: false,
      extensions: [
        StarterKit.configure({
          heading: { levels: [1, 2] },
          codeBlock: false,
          blockquote: false,
          horizontalRule: false,
          bulletList: false,
          orderedList: false,
          listItem: false,
          strike: false,
          code: false,
          link: false,
        }),
        ChipNode,
        ChipSuggestionExtension,
      ],
      content: initialDoc ?? null,
      editorProps: {
        attributes: {
          class: "text-canvas tiptap",
          style: `padding: ${padY}px ${padX}px;`,
        },
      },
    },
    [initialDoc]
  );

  // Push markup style + terms into the editor storage so node-views read them.
  useEffect(() => {
    if (!editor) return;
    const storage = editor.storage as unknown as Record<string, unknown>;
    storage.markupStyle = markupStyle;
    storage.terms = terms;
    editor.view.dispatch(editor.state.tr.setMeta("force-update", true));
  }, [editor, markupStyle, terms]);

  // Debounced save on document change.
  const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  useEffect(() => {
    if (!editor) return;
    const onUpdate = () => {
      if (saveTimer.current) clearTimeout(saveTimer.current);
      saveTimer.current = setTimeout(() => {
        const doc = editor.getJSON();
        void fetch(`/api/projects/${encodeURIComponent(projectId)}/document`, {
          method: "PUT",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ doc }),
        }).catch(err => console.error("[TextCanvas] save failed:", err));
      }, 600);
    };
    editor.on("update", onUpdate);
    return () => {
      editor.off("update", onUpdate);
      if (saveTimer.current) clearTimeout(saveTimer.current);
    };
  }, [editor, projectId]);

  if (!loaded || !editor) {
    return (
      <div className="text-canvas" style={{ padding: `${padY}px ${padX}px` }}>
        <div style={{ color: "var(--muted)", fontFamily: "var(--font-mono)", fontSize: 12 }}>loading editor…</div>
      </div>
    );
  }

  return (
    <ChipFocusContext.Provider value={focusValue}>
      <EditorContent editor={editor} />
    </ChipFocusContext.Provider>
  );
}
