"use client";

import { useEffect, useState } from "react";
import { get, put } from "@/lib/api";
import { Notice, Spinner } from "@/components/ui";
import { DataGrid, type ColDef } from "@/components/DataGrid";

type Node = { code: string; level: string; parentCode: string | null; label: string; hasRule: boolean; version: number | null };
type Attr = { code: string; label: string; includeInResearch?: boolean };
type Effective = { requirementOverrides: Record<string, string>; freeText: string; versionLabels: string[]; videoMode: string; videoModes: string[] };
type RuleDetail = {
  code: string;
  rule: { version: number; requirementOverrides: Record<string, string>; freeTextRules: string; videoMode: string; videoModes: string[] } | null;
  effective: Effective;
};

const OVERRIDE_VALUES = ["Inherit", "Required", "Optional", "Skip"];

// Per-category video styles (Veo). A category either inherits from its parent, or sets an
// explicit selection — any subset of these styles (empty = no video). Mirrors
// CategoryRule::VIDEO_GENERATABLE on the backend.
const VIDEO_STYLES: { value: string; label: string }[] = [
  { value: "studio_3d", label: "Studio 3D — turntable / orbit clip" },
  { value: "lifestyle", label: "Lifestyle shot — product in a scene" },
];
const STYLE_LABEL: Record<string, string> = Object.fromEntries(VIDEO_STYLES.map((s) => [s.value, s.label]));
// Render a resolved/raw value ("inherit" | "none" | CSV of styles) as a human label.
const videoLabel = (v: string) => {
  if (!v || v === "none") return "No video";
  if (v === "inherit") return "Inherit from parent";
  return v.split(",").map((s) => STYLE_LABEL[s.trim()] ?? s.trim()).join(" + ");
};

export default function CategoryRulesPanel() {
  const [nodes, setNodes] = useState<Node[] | null>(null);
  const [attrs, setAttrs] = useState<Attr[]>([]);
  const [selected, setSelected] = useState<string | null>(null);
  const [detail, setDetail] = useState<RuleDetail | null>(null);
  const [overrides, setOverrides] = useState<Record<string, string>>({});
  const [freeText, setFreeText] = useState("");
  const [videoSource, setVideoSource] = useState<"inherit" | "custom">("inherit");
  const [videoStyles, setVideoStyles] = useState<Set<string>>(new Set());
  const [error, setError] = useState<string | null>(null);
  const [msg, setMsg] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);
  const [expanded, setExpanded] = useState<Set<string>>(new Set()); // collapsed by default

  useEffect(() => {
    Promise.all([get<{ categoryRules: Node[] }>("category-rules"), get<{ attributes: Attr[] }>("attributes")])
      .then(([c, a]) => {
        setNodes(c.categoryRules);
        setAttrs(a.attributes);
        if (c.categoryRules.length) selectNode(c.categoryRules[0].code);
      })
      .catch((e) => setError((e as Error).message));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  async function selectNode(code: string) {
    setSelected(code);
    setMsg(null);
    const d = await get<RuleDetail>(`category-rules/${code}`);
    setDetail(d);
    setOverrides(d.rule?.requirementOverrides ?? {});
    setFreeText(d.rule?.freeTextRules ?? "");
    setVideoSource((d.rule?.videoMode ?? "inherit") === "inherit" ? "inherit" : "custom");
    setVideoStyles(new Set(d.rule?.videoModes ?? []));
  }

  async function save() {
    if (!selected) return;
    setBusy(true);
    setMsg(null);
    try {
      const cleaned = Object.fromEntries(Object.entries(overrides).filter(([, v]) => v && v !== "Inherit"));
      const videoMode = videoSource === "inherit" ? "inherit" : ([...videoStyles].join(",") || "none");
      const res = await put<{ effective: Effective }>(`category-rules/${selected}`, {
        requirementOverrides: cleaned,
        freeTextRules: freeText,
        videoMode,
      });
      setDetail((d) => (d ? { ...d, effective: res.effective } : d));
      setMsg("Saved as a new active version.");
      const list = await get<{ categoryRules: Node[] }>("category-rules");
      setNodes(list.categoryRules);
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setBusy(false);
    }
  }

  if (error) return <Notice kind="error">Could not load category rules: {error}</Notice>;
  if (!nodes) return <Spinner label="Loading category tree…" />;

  // Per-Category Rules only apply to attributes enabled for research.
  const researchAttrs = attrs.filter((a) => a.includeInResearch !== false);

  // Build the category tree from the flat node list (group children by parent).
  const childrenOf: Record<string, Node[]> = {};
  for (const n of nodes) {
    (childrenOf[n.parentCode ?? "__root__"] ??= []).push(n);
  }
  const toggle = (code: string) =>
    setExpanded((prev) => {
      const next = new Set(prev);
      next.has(code) ? next.delete(code) : next.add(code);
      return next;
    });

  const renderTree = (list: Node[], depth: number) =>
    list.map((n) => {
      const kids = childrenOf[n.code] ?? [];
      const open = expanded.has(n.code);
      return (
        <li key={n.code}>
          <div className="flex items-center gap-1" style={{ paddingLeft: `${depth * 0.75}rem` }}>
            {kids.length > 0 ? (
              <button
                type="button"
                onClick={() => toggle(n.code)}
                aria-expanded={open}
                aria-label={`${open ? "Collapse" : "Expand"} ${n.label}`}
                className="flex h-5 w-5 shrink-0 items-center justify-center rounded-sm border border-slate-300 text-sm leading-none text-slate-500 hover:bg-slate-100 hover:text-slate-800"
              >
                {open ? "–" : "+"}
              </button>
            ) : (
              <span className="h-5 w-5 shrink-0" aria-hidden />
            )}
            <button
              type="button"
              onClick={() => selectNode(n.code)}
              aria-current={selected === n.code ? "true" : undefined}
              className={`min-w-0 flex-1 truncate rounded px-2 py-1 text-left text-sm ${depth === 0 ? "font-semibold" : ""} ${
                selected === n.code ? "bg-brand text-white" : "hover:bg-slate-100"
              }`}
            >
              {n.label}
              {n.hasRule && <span className="ml-1 text-xs opacity-70">· v{n.version}</span>}
            </button>
          </div>
          {kids.length > 0 && open && <ul>{renderTree(kids, depth + 1)}</ul>}
        </li>
      );
    });

  return (
    <>
      <p className="mb-4 text-sm text-slate-600">
        Requirement overrides + free-text rules per Category Tree node. Effective rules merge Main → Parent → Child
        (most-specific wins); the preview shows exactly what the AI receives.
      </p>
      <div className="grid gap-6 lg:grid-cols-[16rem_1fr]">
        <nav aria-label="Category Tree" className="card max-h-144 overflow-auto">
          <div className="mb-2 flex items-center justify-between gap-2">
            <span className="text-xs font-medium uppercase tracking-wide text-slate-400">Categories</span>
            <button
              type="button"
              onClick={() => setExpanded((prev) => (prev.size > 0 ? new Set() : new Set(nodes.filter((n) => (childrenOf[n.code] ?? []).length > 0).map((n) => n.code))))}
              className="text-xs text-brand hover:underline"
            >
              {expanded.size > 0 ? "Collapse all" : "Expand all"}
            </button>
          </div>
          <ul>{renderTree(childrenOf["__root__"] ?? [], 0)}</ul>
        </nav>

        {detail && (
          <div className="space-y-6">
            {msg && <Notice kind="success">{msg}</Notice>}

            <section className="card" aria-labelledby="ov-h">
              <h2 id="ov-h" className="mb-3 font-semibold text-slate-900">Requirement overrides — {detail.code}</h2>
              <p className="mb-3 text-xs text-slate-500">
                Lists the {researchAttrs.length} attribute{researchAttrs.length === 1 ? "" : "s"} enabled for research
                {attrs.length !== researchAttrs.length ? ` (of ${attrs.length}; toggle the rest on under Services → Attributes)` : ""}.
                Double-click the Override cell to change it, then Save new version below.
              </p>
              <DataGrid
                autoHeight
                rowData={researchAttrs.map((a) => ({ code: a.code, label: a.label, override: overrides[a.code] ?? "Inherit" }))}
                getRowId={(p: any) => String(p.data.code)}
                onCellValueChanged={(e: any) => setOverrides((o) => ({ ...o, [e.data.code]: e.data.override }))}
                columnDefs={
                  [
                    { field: "label", headerName: "Attribute", flex: 1, minWidth: 200, editable: false },
                    {
                      field: "override",
                      headerName: "Override",
                      width: 220,
                      editable: true,
                      cellEditor: "agSelectCellEditor",
                      cellEditorParams: { values: OVERRIDE_VALUES },
                    },
                  ] as ColDef[]
                }
              />
            </section>

            <section className="card" aria-labelledby="vid-h">
              <h2 id="vid-h" className="mb-1 font-semibold text-slate-900">Video assets</h2>
              <p className="mb-3 text-xs text-slate-500">
                Which AI-generated videos (Vertex&nbsp;Veo) products in this category get. Resolves Main → Parent →
                Child like the overrides. Requires the <strong>Video generation</strong> service to be enabled (Admin
                Settings → Services).
              </p>
              <select
                className="input max-w-md"
                value={videoSource}
                onChange={(e) => setVideoSource(e.target.value as "inherit" | "custom")}
                aria-label="Video source"
              >
                <option value="inherit">Inherit from parent</option>
                <option value="custom">Custom selection…</option>
              </select>
              {videoSource === "custom" && (
                <div className="mt-3 space-y-1.5">
                  {VIDEO_STYLES.map((s) => (
                    <label key={s.value} className="flex cursor-pointer items-center gap-2 text-sm">
                      <input
                        type="checkbox"
                        checked={videoStyles.has(s.value)}
                        onChange={() =>
                          setVideoStyles((prev) => {
                            const n = new Set(prev);
                            n.has(s.value) ? n.delete(s.value) : n.add(s.value);
                            return n;
                          })
                        }
                      />
                      <span className="text-slate-700">{s.label}</span>
                    </label>
                  ))}
                  <p className="text-xs text-slate-500">
                    {videoStyles.size === 0
                      ? "No styles selected = no video for this category (overrides the parent)."
                      : `${videoStyles.size} style(s) — each produces its own clip.`}
                  </p>
                </div>
              )}
            </section>

            <section className="card">
              <label htmlFor="freeText" className="label">Free-text rules (injected into the AI prompt)</label>
              <textarea id="freeText" rows={5} value={freeText} onChange={(e) => setFreeText(e.target.value)} className="input" />
              <button className="btn-primary mt-3" onClick={save} disabled={busy}>{busy ? "Saving…" : "Save new version"}</button>
            </section>

            <section className="card bg-slate-50" aria-labelledby="eff-h">
              <h2 id="eff-h" className="mb-2 font-semibold text-slate-900">Effective rules preview (what the AI receives)</h2>
              <p className="text-xs text-slate-500">Versions: {detail.effective.versionLabels.join(", ") || "—"}</p>
              <p className="mt-2 text-sm"><strong>Overrides:</strong> {Object.entries(detail.effective.requirementOverrides).map(([k, v]) => `${k}=${v}`).join(", ") || "none"}</p>
              <p className="mt-1 text-sm"><strong>Video:</strong> {videoLabel(detail.effective.videoMode)}</p>
              <pre className="mt-2 whitespace-pre-wrap rounded-sm bg-white p-3 text-sm text-slate-700">{detail.effective.freeText || "(no free-text rules)"}</pre>
            </section>
          </div>
        )}
      </div>
    </>
  );
}
