import { useEffect, useMemo, useRef, useState } from "react";
import { useShallow } from "zustand/react/shallow";
import { Search, Wand2, ClipboardPaste, Loader2, X, Check } from "lucide-react";
import { useStore, selectStats } from "../store";
import { fmtTime } from "../lib/time";
import { timelineToSource, wordTimelineStart, type Word } from "../lib/timeline-model";
import { ModelSwitcher } from "./ModelSwitcher";
import { aiHeaders } from "../lib/aiKeys";

const LENGTHS = ["~30 sec", "~1 minute", "~2 minutes"];
const TONES = ["Friendly", "Authoritative", "Cinematic", "Energetic"];

// Script tools: generate the project's script with AI, or paste your own. Either one
// REPLACES the transcript (estimated timing) and drives captions/timeline/preview.
function ScriptTools() {
  const setScript = useStore((s) => s.setScript);
  const model = useStore((s) => s.model);
  const projectName = useStore((s) => s.projectName);
  const hasWords = useStore((s) => s.words.length > 0);
  const [mode, setMode] = useState<null | "gen" | "paste">(null);
  const [prompt, setPrompt] = useState("");
  const [length, setLength] = useState(LENGTHS[1]);
  const [tone, setTone] = useState(TONES[0]);
  const [text, setText] = useState("");
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");

  async function generate() {
    setBusy(true); setErr("");
    try {
      const d = await fetch("/api/script", { method: "POST", headers: { "Content-Type": "application/json", ...aiHeaders() }, body: JSON.stringify({ prompt: prompt.trim() || projectName, model, length, tone }) }).then((r) => r.json());
      if (d.error || !d.script) throw new Error(d.error || "no script returned");
      setText(d.script); setMode("paste"); // drop into the editable box to review before applying
    } catch (e) { setErr(e instanceof Error ? e.message : String(e)); }
    setBusy(false);
  }
  function apply() {
    if (!text.trim()) return;
    const st = useStore.getState();
    if ((st.motion.length || st.videos.length) && !window.confirm("Replace the script? Your scenes and clips keep their positions but may no longer line up with the new timing.")) return;
    setScript(text.trim()); setMode(null); setText(""); setPrompt("");
  }

  return (
    <div className="border-b border-border">
      <div className="flex items-center gap-1.5 px-3 py-2">
        <button onClick={() => setMode(mode === "gen" ? null : "gen")} className={"flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-[12px] font-medium " + (mode === "gen" ? "bg-primary text-white" : "border border-border text-fg-muted hover:border-primary hover:text-fg")}><Wand2 size={13} /> Generate with AI</button>
        <button onClick={() => { setMode(mode === "paste" ? null : "paste"); }} className={"flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-[12px] font-medium " + (mode === "paste" ? "bg-primary text-white" : "border border-border text-fg-muted hover:border-primary hover:text-fg")}><ClipboardPaste size={13} /> Paste script</button>
        {mode && <button onClick={() => setMode(null)} className="ml-auto grid h-6 w-6 place-items-center rounded text-fg-subtle hover:bg-white/5"><X size={14} /></button>}
      </div>
      {mode === "gen" && (
        <div className="space-y-2 px-3 pb-3">
          <textarea value={prompt} onChange={(e) => setPrompt(e.target.value)} placeholder={`What's the video about? (defaults to “${projectName}”)`} className="h-14 w-full resize-none rounded-md border border-border bg-surface px-2.5 py-2 text-[12px] outline-none focus:border-primary" />
          <div className="flex flex-wrap gap-3">
            <Chips label="Length" value={length} set={setLength} options={LENGTHS} />
            <Chips label="Tone" value={tone} set={setTone} options={TONES} />
          </div>
          <div className="flex items-center gap-2">
            <ModelSwitcher />
            <div className="flex-1" />
            <button onClick={generate} disabled={busy} className="flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[12px] font-semibold text-white disabled:opacity-50">{busy ? <Loader2 size={13} className="animate-spin" /> : <Wand2 size={13} />} Generate</button>
          </div>
          {err && <div className="text-[11px] text-destructive">⚠️ {err}</div>}
        </div>
      )}
      {mode === "paste" && (
        <div className="space-y-2 px-3 pb-3">
          <textarea value={text} onChange={(e) => setText(e.target.value)} placeholder="Paste or write your script here…" className="h-40 w-full resize-none rounded-md border border-border bg-surface px-2.5 py-2 text-[12px] leading-relaxed outline-none focus:border-primary" />
          <button onClick={apply} disabled={!text.trim()} className="flex w-full items-center justify-center gap-1.5 rounded-md bg-primary py-2 text-[13px] font-semibold text-white disabled:opacity-40"><Check size={14} /> {hasWords ? "Replace script" : "Use this script"}</button>
          <p className="text-[10px] text-fg-subtle">Becomes the editable transcript with estimated timing — record/import a voiceover later to lock real timing.</p>
        </div>
      )}
    </div>
  );
}
function Chips({ label, value, set, options }: { label: string; value: string; set: (v: string) => void; options: string[] }) {
  return (
    <div>
      <div className="eyebrow mb-1 !text-[10px]">{label}</div>
      <div className="flex flex-wrap gap-1">{options.map((o) => <button key={o} onClick={() => set(o)} className={"rounded-full border px-2 py-0.5 text-[11px] " + (value === o ? "border-primary bg-primary/15 text-fg" : "border-border text-fg-muted hover:border-primary/50")}>{o}</button>)}</div>
    </div>
  );
}

// Subscribes to playheadMs ALONE and toggles the highlight class imperatively, so the
// 502-word list doesn't re-render ~30×/sec during playback. Renders nothing.
function ActiveHighlight({ edl }: { edl: ReturnType<typeof useStore.getState>["edl"] }) {
  const playheadMs = useStore((s) => s.playheadMs);
  const wordsById = useStore((s) => s.wordsById);
  const prev = useRef<string | null>(null);
  useEffect(() => {
    let id: string | undefined;
    const ts = timelineToSource(edl, playheadMs);
    if (ts) id = ts.clip.wordIds.map((i) => wordsById[i]).find((w) => w && ts.sourceMs >= w.startMs && ts.sourceMs < w.endMs)?.id;
    if (prev.current && prev.current !== id) document.getElementById("w-" + prev.current)?.classList.remove("tw-active");
    if (id) { document.getElementById("w-" + id)?.classList.add("tw-active"); prev.current = id; } else prev.current = null;
  }, [playheadMs, edl, wordsById]);
  return null;
}

export function TranscriptPanel() {
  const words = useStore((s) => s.words);
  const loading = useStore((s) => s.loading);
  const error = useStore((s) => s.error);
  const toggleWord = useStore((s) => s.toggleWord);
  const edl = useStore((s) => s.edl);
  const setPlayhead = useStore((s) => s.setPlayhead);
  const stats = useStore(useShallow(selectStats));
  const [query, setQuery] = useState("");
  const matchIdx = useRef(0);

  const q = query.trim().toLowerCase();
  const matches = useMemo(() => (q ? words.filter((w) => !w.deleted && w.text.toLowerCase().includes(q)) : []), [q, words]);
  useEffect(() => { matchIdx.current = 0; }, [matches]); // reset cycle when the match set changes

  const seekWord = (w: Word) => {
    const ms = wordTimelineStart(edl, w);
    if (ms != null) { setPlayhead(ms); useStore.getState().seekFn?.(ms); }
  };
  const onWordClick = (e: React.MouseEvent, w: Word) => {
    if (window.getSelection()?.toString()) return; // ignore text-selection drags
    if (w.deleted) return toggleWord(w.id);
    if (e.altKey || e.metaKey) return toggleWord(w.id);
    seekWord(w);
  };
  const jumpNext = () => {
    if (!matches.length) return;
    const m = matches[matchIdx.current % matches.length];
    matchIdx.current++;
    seekWord(m);
    document.getElementById("w-" + m.id)?.scrollIntoView({ block: "center", behavior: "smooth" });
  };

  return (
    <div className="flex h-full flex-col">
      <ActiveHighlight edl={edl} />
      <ScriptTools />
      <div className="flex items-center gap-2 border-b border-border px-3 py-1.5">
        <Search size={13} className="text-fg-subtle" />
        <input value={query} onChange={(e) => setQuery(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") jumpNext(); }}
          placeholder="Search transcript…" className="flex-1 bg-transparent text-[12px] outline-none placeholder:text-fg-subtle" />
        {q && <span className="num text-[11px] text-fg-subtle">{matches.length} match{matches.length === 1 ? "" : "es"}</span>}
      </div>
      <div className="flex items-center gap-3 px-3 py-1.5 text-[11px] text-fg-muted">
        {loading ? <span>Transcribing episode…</span> : error ? <span className="text-destructive">Error: {error}</span> : (
          <>
            <span><b className="text-fg">{stats.kept}</b> kept</span>
            <span>{stats.deleted} cut</span>
            <span className="num">{fmtTime(stats.durationMs)}</span>
          </>
        )}
      </div>
      <div className="min-h-0 flex-1 overflow-y-auto px-3 pb-4 text-[15px] leading-[1.9]">
        {words.length === 0 && !loading && <span className="text-fg-subtle">No transcript.</span>}
        {words.map((w) => {
          const match = q && !w.deleted && w.text.toLowerCase().includes(q);
          return (
            <span key={w.id} id={"w-" + w.id} onClick={(e) => onWordClick(e, w)}
              title={w.deleted ? "click to restore" : "click to jump · alt-click to cut"}
              className={"cursor-pointer rounded px-0.5 " + (w.deleted
                ? "text-fg-subtle line-through decoration-destructive/60 hover:text-fg-muted"
                : match ? "bg-yellow-300/60 text-fg"
                : "hover:bg-primary/10")}>
              {w.text}{" "}
            </span>
          );
        })}
      </div>
      <div className="border-t border-border px-3 py-2 text-[11px] text-fg-subtle">
        Click a word to jump there · <b>Alt-click</b> to cut · click a cut word to restore
      </div>
    </div>
  );
}
