// Concepts pane — unified view of taxonomy + glossary. The two layers share a
// single TaxonomyTerm table, so what looked like two panes was always one
// dataset rendered two ways. This pane gives the user a Tree/Alphabetical
// toggle and a single Analyze action that refreshes both layers.
//
// Re-running Analyze does NOT replace; it produces a *review*. Each row
// carries one of three statuses:
//   accepted   — confirmed, surfaces normally
//   suggested  — analyzer added in last run, awaits keep/discard
//   deprecated — analyzer didn't see in last run, awaits keep/discard
// Pending statuses show a NEW / DEPRECATED badge plus inline Keep / Discard
// buttons. A Pending filter narrows the list to just the changes awaiting
// review when there are many.
//
// Each row is also draggable onto the Model canvas (MIME
// `application/x-socrata-term`), and clicking a row opens TermDetail.

"use client";

import { useMemo, useState } from "react";
import { PaneFrame } from "../PaneFrame";
import { PaneViewTabs } from "../PaneControls";
import { PaneEmpty } from "../PaneEmpty";
import { PaneDrawer } from "../PaneDrawer";
import { StatusChip } from "../StatusChip";
import { useAnalysis, type ClientTerm } from "../../../lib/workspace/analysisStore";
import { useOpenPanes } from "../../../lib/workspace/openPanesStore";

type ViewMode = "tree" | "alpha";

interface ConceptsPaneProps {
  /** Position of this pane in the column stack (0 = root). */
  index: number;
  onClose: () => void;
}

export function ConceptsPane({ index, onClose }: ConceptsPaneProps) {
  const { terms, analyze, inFlight } = useAnalysis();
  const [mode, setMode] = useState<ViewMode>("tree");
  const running = inFlight.has("concepts") || inFlight.has("all");

  const kept = useMemo(() => terms.filter(t => t.status === "accepted"), [terms]);
  const pending = useMemo(() => terms.filter(t => t.status !== "accepted"), [terms]);
  const definedCount = kept.filter(t => t.definition && t.definition.length > 0).length;

  const subtitle =
    pending.length > 0
      ? `${kept.length} kept · ${definedCount} defined · ${pending.length} pending`
      : `${kept.length} terms · ${definedCount} defined`;

  const right = (
    <div className="pane-controls">
      <PaneViewTabs<ViewMode>
        value={mode}
        onChange={setMode}
        ariaLabel="Concepts view mode"
        tabs={[
          { value: "tree", label: "Tree", title: "Hierarchical view" },
          { value: "alpha", label: "A–Z", title: "Alphabetical view with definitions" },
        ]}
      />
      <button
        type="button"
        className="pane-action"
        onClick={() => void analyze("concepts")}
        disabled={running}
      >
        {running ? "Analyzing…" : "Analyze"}
      </button>
    </div>
  );

  return (
    <PaneFrame title="Concepts" subtitle={subtitle} right={right} onClose={onClose}>
      <div className="pane-drawer-stack">
        <div className="pane-drawer-main">
          {kept.length === 0 && pending.length === 0 ? (
            <PaneEmpty
              title="No concepts yet"
              hint={
                <>
                  Concepts are extracted from your prose. Write your idea in the editor on the right,
                  then run <strong>Analyze</strong>.
                </>
              }
              action={{
                label: running ? "Analyzing…" : "Run Analyze →",
                onClick: () => void analyze("concepts"),
                disabled: running,
              }}
            />
          ) : kept.length === 0 ? (
            <PaneEmpty
              title="Nothing kept yet"
              hint="Review the pending suggestions below to start building your concept list."
            />
          ) : mode === "tree" ? (
            <TreeView terms={kept} parentIndex={index} />
          ) : (
            <AlphaView terms={kept} parentIndex={index} />
          )}
        </div>

        {pending.length > 0 ? (
          <PaneDrawer
            title="Pending review"
            count={pending.length}
            tone="pending"
            defaultOpen
          >
            <AlphaView terms={pending} parentIndex={index} />
          </PaneDrawer>
        ) : null}
      </div>
    </PaneFrame>
  );
}

interface TreeNode {
  term: ClientTerm;
  children: TreeNode[];
}

function buildTree(terms: ClientTerm[]): TreeNode[] {
  const byId = new Map<string, TreeNode>(terms.map(t => [t.id, { term: t, children: [] }]));
  const roots: TreeNode[] = [];
  for (const t of terms) {
    const node = byId.get(t.id)!;
    if (t.parentId && byId.has(t.parentId)) {
      byId.get(t.parentId)!.children.push(node);
    } else {
      roots.push(node);
    }
  }
  return roots;
}

function TreeView({ terms, parentIndex }: { terms: ClientTerm[]; parentIndex: number }) {
  const tree = useMemo(() => buildTree(terms), [terms]);
  return (
    <ul className="term-list">
      {tree.map(node => (
        <TermNode key={node.term.id} node={node} depth={0} parentIndex={parentIndex} />
      ))}
    </ul>
  );
}

function AlphaView({ terms, parentIndex }: { terms: ClientTerm[]; parentIndex: number }) {
  const sorted = useMemo(() => [...terms].sort((a, b) => a.label.localeCompare(b.label)), [terms]);
  const { pushFrom, columns } = useOpenPanes();
  const activeId =
    columns[parentIndex + 1]?.kind === "term" ? columns[parentIndex + 1]!.id : null;
  return (
    <ul className="term-list">
      {sorted.map(t => (
        <li key={t.id}>
          <ConceptCard
            term={t}
            active={activeId === t.id}
            onOpen={() => pushFrom(parentIndex, { kind: "term", id: t.id })}
          />
        </li>
      ))}
    </ul>
  );
}

/** The shared visual unit for both Tree and A–Z views in Concepts. The
 * leading column is ALWAYS the chevron slot — Tree passes a real toggle
 * button, A–Z passes nothing and we render an empty placeholder of the
 * same width. That keeps the cards visually identical at every depth and
 * across views; only the chevron's behavior differs. */
function ConceptCard({
  term,
  active,
  onOpen,
  chevron,
}: {
  term: ClientTerm;
  active: boolean;
  onOpen: () => void;
  chevron?: React.ReactNode;
}) {
  return (
    <div
      className={`term-tile term-status-${term.status} ${active ? "row-active" : ""}`}
      draggable
      onDragStart={e => dragTerm(e, term)}
      onClick={onOpen}
      role="button"
      tabIndex={0}
      onKeyDown={e => {
        if (e.key === "Enter" || e.key === " ") {
          e.preventDefault();
          onOpen();
        }
      }}
    >
      {chevron}
      <div className="term-tile-content">
        <div className="term-tile-head">
          <span className="term-label">{term.label}</span>
          <StatusBadge term={term} />
        </div>
        {term.definition ? (
          <div className="term-def">{term.definition}</div>
        ) : (
          <div className="term-def term-def-empty">(no definition yet)</div>
        )}
        <DecisionRow term={term} />
      </div>
    </div>
  );
}

function TermNode({
  node,
  depth,
  parentIndex,
}: {
  node: TreeNode;
  depth: number;
  parentIndex: number;
}) {
  const { pushFrom, columns } = useOpenPanes();
  const [expanded, setExpanded] = useState(true);
  const t = node.term;
  const hasChildren = node.children.length > 0;
  const activeId =
    columns[parentIndex + 1]?.kind === "term" ? columns[parentIndex + 1]!.id : null;
  const open = activeId === t.id;

  // Only parents get a chevron; leaves render flush-left (matches A–Z view).
  const chevron = hasChildren ? (
    <button
      type="button"
      className="term-tile-chevron"
      onClick={e => {
        e.stopPropagation();
        setExpanded(v => !v);
      }}
      aria-label={expanded ? "Collapse" : "Expand"}
    >
      {expanded ? "▾" : "▸"}
    </button>
  ) : undefined;

  return (
    <li className="term-tree-node" style={{ "--tree-depth": depth } as React.CSSProperties}>
      <ConceptCard
        term={t}
        active={open}
        onOpen={() => pushFrom(parentIndex, { kind: "term", id: t.id })}
        chevron={chevron}
      />
      {hasChildren && expanded ? (
        <ul className="term-list term-list-children">
          {node.children.map(c => (
            <TermNode key={c.term.id} node={c} depth={depth + 1} parentIndex={parentIndex} />
          ))}
        </ul>
      ) : null}
    </li>
  );
}

function StatusBadge({ term }: { term: ClientTerm }) {
  if (term.status === "suggested") {
    return <StatusChip variant="suggested" title="Added by latest analyze" />;
  }
  if (term.status === "deprecated") {
    return <StatusChip variant="deprecated" title="Analyzer didn't see this in the latest run" />;
  }
  if (term.linkedBlockId) {
    return <span className="term-linked-dot" title="Has a linked block">●</span>;
  }
  return <span className="term-drag-hint">drag to canvas</span>;
}

function DecisionRow({ term }: { term: ClientTerm }) {
  const { decideTerm } = useAnalysis();
  if (term.status === "accepted") return null;

  const isSuggested = term.status === "suggested";
  return (
    <div className="term-decision-row" onClick={e => e.stopPropagation()}>
      <button
        type="button"
        className="term-decide term-decide-keep"
        onClick={e => {
          e.stopPropagation();
          void decideTerm(term.id, "keep");
        }}
        title={isSuggested ? "Accept this suggestion" : "Pin this term despite the analyzer dropping it"}
      >
        Keep
      </button>
      <button
        type="button"
        className="term-decide term-decide-discard"
        onClick={e => {
          e.stopPropagation();
          void decideTerm(term.id, "discard");
        }}
        title={isSuggested ? "Reject this suggestion" : "Remove this term"}
      >
        Discard
      </button>
    </div>
  );
}

function dragTerm(e: React.DragEvent, t: ClientTerm) {
  e.dataTransfer.setData(
    "application/x-socrata-term",
    JSON.stringify({ termId: t.id, label: t.label, definition: t.definition })
  );
  e.dataTransfer.effectAllowed = "move";
}
