import { useRef, useState } from "react";
import { Sparkles, Plus, ChevronRight, Film, Loader2, LayoutGrid, Trash2, Check, Copy, Pencil } from "lucide-react";
import { CHRONIXEL_SCENES } from "../remotion/chronixel";
import { MG_TEMPLATE_LIST, CATEGORY_ORDER, type TemplateDef } from "../remotion/mg/templates";
import { useStore } from "../store";
import { GenerateScenesModal } from "./GenerateScenesModal";
import { TemplateGallery, AutoPreview } from "./TemplateGallery";
import { aiHeaders } from "../lib/aiKeys";

const clone = <T,>(o: T): T => JSON.parse(JSON.stringify(o));

export function Library() {
  const motion = useStore((s) => s.motion);
  const projectName = useStore((s) => s.projectName);
  const selectMotion = useStore((s) => s.selectMotion);
  const toggleSelect = useStore((s) => s.toggleSelect);
  const selectRange = useStore((s) => s.selectRange);
  const removeSelected = useStore((s) => s.removeSelected);
  const removeMotion = useStore((s) => s.removeMotion);
  const duplicateMotion = useStore((s) => s.duplicateMotion);
  const clearSelection = useStore((s) => s.clearSelection);
  const selectedIds = useStore((s) => s.selectedIds);
  const inspectId = useStore((s) => s.inspectId); // set when a scene is OPEN (single) → distinguishes "inspecting" from "multi-select mode"
  const addGeneratedScene = useStore((s) => s.addGeneratedScene);
  const addMotion = useStore((s) => s.addMotion);
  const [genOpen, setGenOpen] = useState(false);
  const [galleryOpen, setGalleryOpen] = useState(false);
  const [showStock, setShowStock] = useState(false);
  // collapsible template categories (persisted) — keeps the long picker compact + organized
  const [openCats, setOpenCats] = useState<Set<string>>(() => { try { const s = JSON.parse(localStorage.getItem("ed.libCats") || "null"); return new Set<string>(Array.isArray(s) ? s : [CATEGORY_ORDER[0]]); } catch { return new Set<string>([CATEGORY_ORDER[0]]); } });
  const toggleCat = (c: string) => setOpenCats((prev) => { const n = new Set(prev); n.has(c) ? n.delete(c) : n.add(c); try { localStorage.setItem("ed.libCats", JSON.stringify([...n])); } catch { /* ignore */ } return n; });
  const [filling, setFilling] = useState(0);
  const [hoverTpl, setHoverTpl] = useState<TemplateDef | null>(null);
  // hovering one of THIS project's scenes previews its real content + look (props/theme/accent/bg/anim/font)
  const [hoverScene, setHoverScene] = useState<{ id: string; template: string; props: Record<string, unknown>; theme: string; accent: string; bg?: string; anim?: string; font?: string; durationInFrames: number } | null>(null);
  // what the preview box plays: a hovered project scene wins, else the hovered starter (its sample)
  const pv = hoverScene ?? (hoverTpl ? { id: hoverTpl.id, template: hoverTpl.id, props: hoverTpl.sample as Record<string, unknown>, theme: "dark", accent: "#E7723B", bg: undefined, anim: undefined, font: undefined, durationInFrames: Math.max(1, Math.round(hoverTpl.defaultDurationSec * 30)) } : null);
  // a stable key for "which thing is hovered" — remounts AutoPreview (which seeks+plays) once per hover
  const pvKey = hoverScene ? "s:" + hoverScene.id : hoverTpl ? "t:" + hoverTpl.id : "";

  // drag a box over the scene rows to MULTI-SELECT them (without opening the editor). Mirrors the
  // timeline marquee. A drag suppresses the row's click-to-open; a plain click still opens the editor.
  const setSelection = useStore((s) => s.setSelection);
  const setMarqueeing = useStore((s) => s.setMarqueeing);
  const listRef = useRef<HTMLDivElement>(null);
  const [listBox, setListBox] = useState<{ left: number; top: number; w: number; h: number } | null>(null);
  const suppressClick = useRef(false);
  const startListMarquee = (e: React.PointerEvent) => {
    if (e.button !== 0) return;
    if ((e.target as HTMLElement).closest("button")) return; // checkbox / row actions handle their own clicks
    const cont = listRef.current; if (!cont) return;
    const additive = e.shiftKey || e.metaKey || e.ctrlKey;
    const x0 = e.clientX, y0 = e.clientY;
    let moved = false;
    document.body.style.userSelect = "none";
    const apply = (ev: PointerEvent) => {
      const x1 = ev.clientX, y1 = ev.clientY;
      if (!moved && (Math.abs(x1 - x0) > 4 || Math.abs(y1 - y0) > 4)) { moved = true; setMarqueeing(true); } // keep the list mounted for the rest of the drag
      if (!moved) return;
      const cr = cont.getBoundingClientRect();
      setListBox({ left: Math.min(x0, x1) - cr.left, top: Math.min(y0, y1) - cr.top, w: Math.abs(x1 - x0), h: Math.abs(y1 - y0) });
      const bl = Math.min(x0, x1), br = Math.max(x0, x1), bt = Math.min(y0, y1), bb = Math.max(y0, y1);
      const ids: string[] = [];
      cont.querySelectorAll<HTMLElement>("[data-mid]").forEach((el) => {
        const r = el.getBoundingClientRect();
        if (!(r.right < bl || r.left > br || r.bottom < bt || r.top > bb)) ids.push(el.dataset.mid!);
      });
      setSelection(ids, additive);
    };
    const onUp = () => {
      window.removeEventListener("pointermove", apply);
      window.removeEventListener("pointerup", onUp);
      window.removeEventListener("pointercancel", onUp);
      document.body.style.userSelect = "";
      setListBox(null);
      setMarqueeing(false); // release → the panel may now swap to the bulk-style view for the final selection
      if (moved) { suppressClick.current = true; setTimeout(() => (suppressClick.current = false), 0); }
    };
    window.addEventListener("pointermove", apply);
    window.addEventListener("pointerup", onUp);
    window.addEventListener("pointercancel", onUp);
  };

  const generated = motion.filter((m) => m.template);
  // "custom" (the AI-designed scene) is omitted here — it's created by asking the agent to "generate a
  // new scene" (design_scene), not added from the library. Dropping it empties the "More" bucket → hidden.
  const cats = CATEGORY_ORDER.map((c) => ({ cat: c, items: MG_TEMPLATE_LIST.filter((t) => t.category === c && t.id !== "custom") })).filter((g) => g.items.length);

  // add a template (with the chosen look), then auto-write its content from the script (best-effort)
  const addStarter = async (tplId: string, theme = "dark", accent = "#E7723B") => {
    const def = MG_TEMPLATE_LIST.find((t) => t.id === tplId);
    if (!def) return;
    const id = addGeneratedScene({ template: def.id, props: clone(def.sample), label: def.label, durationSec: def.defaultDurationSec, theme, accent });
    selectMotion(id);
    const projectAtStart = useStore.getState().projectId;
    const words = useStore.getState().words.filter((w) => !w.deleted);
    if (!words.length) return; // nothing to draw content from → keep the sample
    setFilling((n) => n + 1);
    try {
      const transcript = words.map((w) => w.text).join(" ").slice(0, 1600);
      const r = await fetch("/api/fill-scene", { method: "POST", headers: { "Content-Type": "application/json", ...aiHeaders() }, body: JSON.stringify({ template: def.id, topic: projectName, transcript, lang: useStore.getState().lang }) });
      const d = await r.json();
      const s = useStore.getState();
      // only apply if we're still on the same project AND the scene still exists (user may have switched/deleted)
      if (r.ok && d.props && typeof d.props === "object" && s.projectId === projectAtStart && s.motion.some((m) => m.id === id)) s.updateMotionProps(id, d.props);
    } catch { /* keep sample */ }
    setFilling((n) => Math.max(0, n - 1));
  };

  return (
    <div className="flex h-full flex-col">
      <GenerateScenesModal open={genOpen} onClose={() => setGenOpen(false)} />
      <TemplateGallery open={galleryOpen} onClose={() => setGalleryOpen(false)} onAdd={(id, th, ac) => addStarter(id, th, ac)} />
      <div className="min-h-0 flex-1 overflow-y-auto p-3">
        <div className="mb-2 flex items-center gap-1.5 text-[11px] text-fg-subtle">Motion graphics for <span className="font-medium text-fg-muted">{projectName}</span></div>

        <button onClick={() => setGenOpen(true)} className="mb-3 flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-[13px] font-semibold text-white hover:brightness-105">
          <Sparkles size={15} /> Generate motion graphics
        </button>
        {filling > 0 && <div className="mb-3 flex items-center gap-2 rounded-md border border-primary/30 bg-primary/5 px-2.5 py-1.5 text-[12px] text-fg"><Loader2 size={13} className="animate-spin text-primary" /> Writing content from your script…</div>}

        {/* sticky preview "monitor" — pinned to the top while you scroll, so hovering ANY scene
            (below) OR template starter (further down) always plays here where you can see it */}
        <div className="sticky top-0 z-20 mb-3 bg-surface pb-3 pt-0.5">
          <div className="overflow-hidden rounded-md border border-border bg-black shadow-sm">
            {pv ? (
              <AutoPreview key={pvKey} template={pv.template} props={pv.props} theme={pv.theme} accent={pv.accent} bg={pv.bg} anim={pv.anim} font={pv.font} durationInFrames={pv.durationInFrames} />
            ) : (
              <div className="grid aspect-video place-items-center text-[11px] text-white/40">Hover a scene or template to preview · click to add</div>
            )}
          </div>
        </div>

        {/* this project's own generated scenes — multi-select with the checkbox or ⌘/shift-click */}
        <div className="mb-2 flex items-center gap-2">
          <div className="eyebrow">This project's scenes {generated.length ? `· ${generated.length}` : ""}</div>
          <div className="flex-1" />
          {selectedIds.length >= 1 ? (
            <>
              <button onClick={removeSelected} className="flex items-center gap-1 rounded bg-destructive px-2 py-0.5 text-[11px] font-semibold text-white hover:brightness-110"><Trash2 size={11} /> Delete {selectedIds.length}</button>
              <button onClick={clearSelection} className="text-[11px] text-fg-subtle hover:text-fg">Clear</button>
            </>
          ) : (
            <button onClick={() => setGalleryOpen(true)} title="Add a scene from the template gallery" className="flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[11px] font-medium text-fg-muted hover:border-primary hover:text-fg"><Plus size={11} /> Add scene</button>
          )}
        </div>
        {generated.length === 0 ? (
          <div className="mb-4 flex flex-col items-center gap-2.5 rounded-md border border-dashed border-border px-3 py-4 text-center">
            <span className="grid h-9 w-9 place-items-center rounded-full bg-primary/10"><Film size={17} className="text-primary" /></span>
            <div className="text-[12px] text-fg-subtle">No scenes yet — each one is auto-written from your script.</div>
            <div className="flex gap-2">
              <button onClick={() => setGenOpen(true)} className="flex items-center gap-1 rounded-md bg-primary px-2.5 py-1.5 text-[12px] font-semibold text-white hover:brightness-105"><Sparkles size={12} /> Generate scenes</button>
              <button onClick={() => setGalleryOpen(true)} className="flex items-center gap-1 rounded-md border border-border px-2.5 py-1.5 text-[12px] font-medium text-fg-muted hover:border-primary hover:text-fg"><Plus size={12} /> Browse templates</button>
            </div>
          </div>
        ) : (
          <div ref={listRef} onPointerDown={startListMarquee} className="relative mb-4 space-y-1.5">
            {listBox && <div className="pointer-events-none absolute z-10 rounded-sm border border-primary bg-primary/20" style={{ left: listBox.left, top: listBox.top, width: listBox.w, height: listBox.h }} />}
            {generated.map((m) => {
              const sel = selectedIds.includes(m.id);
              return (
                <div key={m.id} data-mid={m.id} onClick={(e) => {
                    if (suppressClick.current) return;
                    if (e.metaKey || e.ctrlKey) return toggleSelect(m.id);
                    if (e.shiftKey) return selectRange(m.id);
                    // once you're in selection mode (a checkbox is ticked, nothing open) a plain click
                    // ADDS/REMOVES from the selection; otherwise it opens the scene in the inspector
                    if (inspectId == null && selectedIds.length > 0) toggleSelect(m.id);
                    else selectMotion(m.id);
                  }}
                  onMouseEnter={() => m.template && setHoverScene({ id: m.id, template: m.template, props: (m.props as Record<string, unknown>) || {}, theme: m.theme || "dark", accent: m.accent || "#E7723B", bg: m.bg, anim: m.anim, font: m.font, durationInFrames: Math.max(1, Math.round((m.durationMs / 1000) * 30)) })}
                  onMouseLeave={() => setHoverScene(null)}
                  className={"group flex w-full cursor-pointer select-none items-center gap-2 rounded-md border px-2.5 py-2 text-left " + (sel ? "border-primary bg-primary/10" : "border-border hover:border-primary/50")}>
                  <button onClick={(e) => { e.stopPropagation(); toggleSelect(m.id); }} title="Select" className={"grid h-4 w-4 shrink-0 place-items-center rounded border " + (sel ? "border-primary bg-primary text-white" : "border-border bg-surface text-transparent hover:border-primary")}><Check size={11} /></button>
                  <span className="grid h-8 w-8 shrink-0 place-items-center rounded bg-[var(--color-tl-motion)]/15 text-[var(--color-tl-motion)]"><Film size={15} /></span>
                  <span className="min-w-0 flex-1"><span className="block truncate text-[12px] font-medium text-fg">{m.label}</span><span className="block truncate text-[10px] text-fg-subtle">{m.template} · {(m.durationMs / 1000).toFixed(1)}s</span></span>
                  {/* per-row actions appear on hover so deleting/duplicating needs no drill-in */}
                  <ChevronRight size={14} className="shrink-0 text-fg-subtle group-hover:hidden" />
                  <div className="hidden shrink-0 items-center gap-0.5 group-hover:flex">
                    <RowAction title="Edit scene" onClick={(e) => { e.stopPropagation(); selectMotion(m.id); }}><Pencil size={13} /></RowAction>
                    <RowAction title="Duplicate scene" onClick={(e) => { e.stopPropagation(); duplicateMotion(m.id); }}><Copy size={13} /></RowAction>
                    <RowAction title="Delete scene" danger onClick={(e) => { e.stopPropagation(); removeMotion(m.id); }}><Trash2 size={13} /></RowAction>
                  </div>
                </div>
              );
            })}
          </div>
        )}

        {/* template starters, grouped by category */}
        <div className="eyebrow mb-2">Add a scene <span className="normal-case text-fg-subtle">— auto-written from your script</span></div>
        <button onClick={() => setGalleryOpen(true)} className="mb-2 flex w-full items-center justify-center gap-2 rounded-md border border-primary/40 bg-primary/5 py-2.5 text-[13px] font-semibold text-primary hover:bg-primary/10">
          <LayoutGrid size={15} /> Browse {MG_TEMPLATE_LIST.length} templates with live previews
        </button>

        <div className="space-y-1.5">
          {cats.map((g) => {
            const open = openCats.has(g.cat);
            return (
              <div key={g.cat} className="overflow-hidden rounded-md border border-border/60">
                <button onClick={() => toggleCat(g.cat)} className={"flex w-full items-center gap-1.5 px-2 py-1.5 text-[10px] font-semibold uppercase tracking-wider hover:text-fg " + (open ? "text-fg" : "text-fg-subtle")}>
                  <ChevronRight size={13} className={"shrink-0 transition " + (open ? "rotate-90 text-primary" : "")} />
                  <span className="flex-1 text-left">{g.cat}</span>
                  <span className="num rounded bg-white/5 px-1.5 py-px text-[9px] text-fg-subtle">{g.items.length}</span>
                </button>
                {open && (
                  <div className="grid grid-cols-2 gap-2 px-2 pb-2 pt-0.5">
                    {g.items.map((t) => (
                      <button key={t.id} onClick={() => addStarter(t.id)} onMouseEnter={() => setHoverTpl(t)} title={t.blurb} className="flex flex-col gap-0.5 rounded-md border border-border p-2 text-left hover:border-primary">
                        <span className="flex items-center gap-1 text-[12px] font-medium text-fg"><Plus size={12} className="text-primary" /> {t.label}</span>
                        <span className="truncate text-[10px] text-fg-subtle">{t.blurb}</span>
                      </button>
                    ))}
                  </div>
                )}
              </div>
            );
          })}
        </div>

        {/* stock Chronixel scenes — demoted */}
        <button onClick={() => setShowStock((v) => !v)} className="mb-2 mt-4 flex w-full items-center gap-1 text-[11px] font-semibold uppercase tracking-wider text-fg-subtle hover:text-fg">
          <ChevronRight size={13} className={"transition " + (showStock ? "rotate-90" : "")} /> Stock scenes (Obsidian Orbit)
        </button>
        {showStock && (
          <div className="grid grid-cols-2 gap-2">
            {CHRONIXEL_SCENES.map((s) => (
              <button key={s.id} onClick={() => addMotion(s.id, s.label, s.durationInFrames)} title={`Add "${s.label}" at the playhead`} className="group overflow-hidden rounded-md border border-border bg-surface-raised text-left transition hover:border-primary">
                <div className="relative grid aspect-video place-items-center bg-[#0d0d0f] text-[10px] font-semibold tracking-wider text-white/30">CHRONIXEL</div>
                <div className="px-2 py-1.5"><div className="truncate text-[11px] font-medium text-fg">{s.label}</div><div className="num text-[10px] text-fg-subtle">{Math.round(s.durationInFrames / 30)}s</div></div>
              </button>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function RowAction({ children, title, onClick, danger }: { children: React.ReactNode; title: string; onClick: (e: React.MouseEvent) => void; danger?: boolean }) {
  return (
    <button title={title} onClick={onClick}
      className={"grid h-6 w-6 place-items-center rounded text-fg-subtle hover:bg-white/10 " + (danger ? "hover:text-destructive" : "hover:text-fg")}>
      {children}
    </button>
  );
}
