"use client";

import { useEffect, useState, useCallback, useMemo, useRef } from "react";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Placeholder from "@tiptap/extension-placeholder";
import type { NoteDetail } from "@/lib/types";
import { Card, Input } from "@/components/ui/Primitives";
import { useDebounce } from "@/lib/use-debounce";
import { copilotTransform, type CopilotAction, type CopilotTone } from "@/lib/copilot-client";
import { toast } from "@/lib/toast";

const TOOLBAR_BTN: React.CSSProperties = {
  border: "none",
  borderRadius: "var(--nl-radius-sm)",
  padding: "4px 8px",
  background: "transparent",
  color: "var(--nl-text-secondary)",
  fontSize: "var(--nl-fs-sm)",
  cursor: "pointer",
};

const TOOLBAR_BTN_ACTIVE: React.CSSProperties = {
  ...TOOLBAR_BTN,
  background: "var(--nl-accent-muted-strong)",
  color: "var(--nl-text-primary)",
};

function ToolbarButton({ active, onClick, label, ariaLabel }: { active: boolean; onClick: () => void; label: string; ariaLabel: string }) {
  return (
    <button type="button" style={active ? TOOLBAR_BTN_ACTIVE : TOOLBAR_BTN} onClick={onClick} title={label} aria-label={ariaLabel}>
      {label}
    </button>
  );
}

export function NoteEditor({
  note,
  onSave,
  isSaving = false,
  autoSave = true,
  autoSaveDelayMs = 1500,
  copilotNoteId,
  copilotWorkspaceId,
}: {
  note: NoteDetail;
  onSave: (updates: { title: string; body: string }, options?: { quiet?: boolean }) => Promise<void>;
  isSaving?: boolean;
  autoSave?: boolean;
  autoSaveDelayMs?: number;
  copilotNoteId?: string;
  copilotWorkspaceId?: string;
}) {
  const [title, setTitle] = useState(note.title);
  const [, setBodyTick] = useState(0);
  const [copilotBusy, setCopilotBusy] = useState(false);
  const [toneMenuOpen, setToneMenuOpen] = useState(false);
  const [explainResult, setExplainResult] = useState<string | null>(null);
  const toneMenuRef = useRef<HTMLDivElement>(null);
  const onSaveRef = useRef(onSave);

  // Keep the latest onSave handler in a ref so the debounced auto-save
  // effect always invokes the current closure. Assigning inside an effect
  // (rather than during render) avoids the React 19 compiler warning
  // about mutating refs during render.
  useEffect(() => {
    onSaveRef.current = onSave;
  }, [onSave]);

  useEffect(() => {
    if (!toneMenuOpen) return;
    const handler = (e: MouseEvent) => {
      if (toneMenuRef.current && !toneMenuRef.current.contains(e.target as Node)) {
        setToneMenuOpen(false);
      }
    };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [toneMenuOpen]);

  const editor = useEditor({
    extensions: [
      StarterKit.configure({
        heading: { levels: [1, 2, 3] },
      }),
      Placeholder.configure({ placeholder: "Start writing…" }),
    ],
    content: note.body,
    editorProps: {
      attributes: {
        class: "notelett-tiptap rounded-[var(--nl-radius-md)] border border-[color:var(--nl-border-default)] bg-[color:var(--nl-input-bg)] p-3",
        style: "min-height:360px;outline:none;line-height:1.7;",
      },
    },
    onUpdate: () => setBodyTick((t) => t + 1),
  });

  const bodyHtml = editor?.getHTML() ?? note.body;

  useEffect(() => {
    setTitle(note.title);
    if (editor && note.body !== editor.getHTML()) {
      editor.commands.setContent(note.body);
    }
  }, [note.title, note.body, editor]);

  const debouncedTitle = useDebounce(title, autoSaveDelayMs);
  const debouncedBody = useDebounce(bodyHtml, autoSaveDelayMs);

  useEffect(() => {
    if (!autoSave || !editor || isSaving) return;
    if (debouncedTitle === note.title && debouncedBody === note.body) return;
    void onSaveRef.current({ title: debouncedTitle, body: debouncedBody }, { quiet: true });
  }, [autoSave, autoSaveDelayMs, debouncedTitle, debouncedBody, note.title, note.body, note.id, editor, isSaving]);

  const handleSave = useCallback(() => {
    if (!editor) return;
    void onSave({ title, body: editor.getHTML() }, { quiet: false });
  }, [editor, title, onSave]);

  const dirty = useMemo(
    () => title !== note.title || bodyHtml !== note.body,
    [title, bodyHtml, note.title, note.body],
  );

  const runCopilot = useCallback(
    async (action: CopilotAction, tone?: CopilotTone) => {
      if (!editor || !copilotNoteId || !copilotWorkspaceId) return;
      const { from, to } = editor.state.selection;
      const selected = editor.state.doc.textBetween(from, to, "\n").trim();

      // "continue" uses all text before cursor, not selection
      if (action === "continue") {
        const fullText = editor.state.doc.textBetween(0, editor.state.selection.to, "\n").trim();
        if (!fullText) { toast.error("Place cursor in the editor first"); return; }
        setCopilotBusy(true);
        try {
          const out = await copilotTransform(copilotNoteId, copilotWorkspaceId, action, fullText);
          const escaped = out.split("\n").map((l) => l.replace(/</g, "&lt;").replace(/>/g, "&gt;")).join("</p><p>");
          editor.chain().focus().insertContent(`<p>${escaped}</p>`).run();
          toast.success("Continuation inserted — review and save");
        } catch (e) { toast.error(e instanceof Error ? e.message : "Continue failed"); }
        finally { setCopilotBusy(false); }
        return;
      }

      // "explain" shows result in a tooltip, doesn't replace text
      if (action === "explain") {
        if (!selected) { toast.error("Select text to explain"); return; }
        setCopilotBusy(true);
        try {
          const out = await copilotTransform(copilotNoteId, copilotWorkspaceId, action, selected);
          setExplainResult(out);
        } catch (e) { toast.error(e instanceof Error ? e.message : "Explain failed"); }
        finally { setCopilotBusy(false); }
        return;
      }

      if (!selected) {
        toast.error("Select text in the editor first");
        return;
      }
      setCopilotBusy(true);
      try {
        const out = await copilotTransform(copilotNoteId, copilotWorkspaceId, action, selected, tone);
        const escaped = out
          .split("\n")
          .map((line) => line.replace(/</g, "&lt;").replace(/>/g, "&gt;"))
          .join("</p><p>");
        editor.chain().focus().deleteSelection().insertContent(`<p>${escaped}</p>`).run();
        toast.success("Copilot insert applied — review and save");
      } catch (e) {
        toast.error(e instanceof Error ? e.message : "Copilot failed");
      } finally {
        setCopilotBusy(false);
      }
    },
    [editor, copilotNoteId, copilotWorkspaceId],
  );

  return (
    <Card padding="lg" className="grid gap-4">
      <form
        className="grid gap-4"
        onSubmit={(e) => {
          e.preventDefault();
          handleSave();
        }}
      >
        <Input
          id="note-title"
          label="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />

        <div style={{ display: "grid", gap: "var(--nl-space-2)" }}>
          <label style={{ color: "var(--nl-text-secondary)" }}>Body</label>

          {editor && (
            <div style={{ display: "flex", gap: 4, flexWrap: "wrap", padding: "4px 0" }}>
              <ToolbarButton active={editor.isActive("bold")} onClick={() => editor.chain().focus().toggleBold().run()} label="B" ariaLabel="Bold" />
              <ToolbarButton active={editor.isActive("italic")} onClick={() => editor.chain().focus().toggleItalic().run()} label="I" ariaLabel="Italic" />
              <ToolbarButton active={editor.isActive("strike")} onClick={() => editor.chain().focus().toggleStrike().run()} label="S" ariaLabel="Strikethrough" />
              <span style={{ width: 1, background: "var(--nl-border-default)", margin: "0 4px" }} />
              <ToolbarButton active={editor.isActive("heading", { level: 1 })} onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()} label="H1" ariaLabel="Heading level 1" />
              <ToolbarButton active={editor.isActive("heading", { level: 2 })} onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} label="H2" ariaLabel="Heading level 2" />
              <ToolbarButton active={editor.isActive("heading", { level: 3 })} onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} label="H3" ariaLabel="Heading level 3" />
              <span style={{ width: 1, background: "var(--nl-border-default)", margin: "0 4px" }} />
              <ToolbarButton active={editor.isActive("bulletList")} onClick={() => editor.chain().focus().toggleBulletList().run()} label="•" ariaLabel="Bullet list" />
              <ToolbarButton active={editor.isActive("orderedList")} onClick={() => editor.chain().focus().toggleOrderedList().run()} label="1." ariaLabel="Numbered list" />
              <ToolbarButton active={editor.isActive("codeBlock")} onClick={() => editor.chain().focus().toggleCodeBlock().run()} label="<>" ariaLabel="Code block" />
              <ToolbarButton active={editor.isActive("blockquote")} onClick={() => editor.chain().focus().toggleBlockquote().run()} label="❝" ariaLabel="Block quote" />
            </div>
          )}

          {editor && copilotNoteId && copilotWorkspaceId ? (
            <div style={{ display: "flex", gap: 6, flexWrap: "wrap", alignItems: "center", padding: "4px 0" }}>
              <span style={{ color: "var(--nl-text-secondary)", fontSize: "var(--nl-fs-sm)", marginRight: 4 }}>Copilot</span>
              {(["shorten", "bulletize", "expand", "grammar"] as const).map((a) => (
                <button
                  key={a}
                  type="button"
                  disabled={copilotBusy}
                  style={{ ...TOOLBAR_BTN, opacity: copilotBusy ? 0.5 : 1 }}
                  onClick={() => void runCopilot(a)}
                >
                  {a}
                </button>
              ))}
              <span style={{ width: 1, background: "var(--nl-border-default)", margin: "0 4px", alignSelf: "stretch" }} />
              <span style={{ color: "var(--nl-text-secondary)", fontSize: "var(--nl-fs-sm)", marginRight: 4 }}>AI</span>
              <button type="button" disabled={copilotBusy} style={{ ...TOOLBAR_BTN, opacity: copilotBusy ? 0.5 : 1 }} onClick={() => void runCopilot("fix-rewrite")}>Fix & Rewrite</button>
              <div ref={toneMenuRef} style={{ position: "relative" }}>
                <button type="button" disabled={copilotBusy} style={{ ...TOOLBAR_BTN, opacity: copilotBusy ? 0.5 : 1 }} onClick={() => setToneMenuOpen(!toneMenuOpen)}>Change Tone ▾</button>
                {toneMenuOpen && (
                  <div style={{ position: "absolute", top: "100%", left: 0, zIndex: 50, background: "var(--nl-surface-card)", border: "1px solid var(--nl-border-default)", borderRadius: "var(--nl-radius-md)", padding: 4, display: "grid", gap: 2, minWidth: 120 }}>
                    {(["formal", "casual", "professional", "friendly"] as const).map((t) => (
                      <button key={t} type="button" style={{ ...TOOLBAR_BTN, textAlign: "left", width: "100%" }} onClick={() => { setToneMenuOpen(false); void runCopilot("change-tone", t); }}>{t}</button>
                    ))}
                  </div>
                )}
              </div>
              <button type="button" disabled={copilotBusy} style={{ ...TOOLBAR_BTN, opacity: copilotBusy ? 0.5 : 1 }} onClick={() => void runCopilot("continue")}>Continue ✍</button>
              <button type="button" disabled={copilotBusy} style={{ ...TOOLBAR_BTN, opacity: copilotBusy ? 0.5 : 1 }} onClick={() => void runCopilot("explain")}>Explain</button>
            </div>
          ) : null}

          {explainResult && (
            <div style={{ background: "var(--nl-surface-card)", border: "1px solid var(--nl-border-default)", borderRadius: "var(--nl-radius-md)", padding: "var(--nl-space-3)", fontSize: "var(--nl-fs-sm)", color: "var(--nl-text-secondary)", display: "flex", gap: 8, alignItems: "flex-start" }}>
              <div style={{ flex: 1 }}>{explainResult}</div>
              <button type="button" style={{ ...TOOLBAR_BTN, fontSize: 12 }} onClick={() => setExplainResult(null)} aria-label="Dismiss explanation">✕</button>
            </div>
          )}

          <EditorContent editor={editor} />
        </div>

        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: "var(--nl-space-3)" }}>
          <div style={{ fontSize: "var(--nl-fs-sm)", color: "var(--nl-text-secondary)" }} aria-live="polite">
            {isSaving ? "Saving…" : dirty ? "Unsaved changes (auto-save on pause)" : "All changes saved"}
          </div>
          <button
            type="submit"
            disabled={isSaving}
            style={{
              border: "none",
              borderRadius: "var(--nl-radius-md)",
              padding: "10px 14px",
              background: "var(--nl-accent-primary)",
              color: "var(--nl-text-primary)",
              fontWeight: 600,
            }}
          >
            {isSaving ? "Saving…" : "Save now"}
          </button>
        </div>
      </form>
    </Card>
  );
}
