"use client";

import { useEffect, useMemo, useState, type ReactNode } from "react";
import { Check, X, Edit2, ChevronDown, ChevronLeft, AlertTriangle, Clock, RotateCcw, Info, Search } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Skeleton } from "@/components/ui/skeleton";
import { Textarea } from "@/components/ui/textarea";
import { CorroborationBadge } from "./corroboration-badge";
import { practiceAreaLabel } from "./practice-area";
import {
  useHalachotPending, useHalachotByStatus, useUpdateHalacha, useBatchReviewHalachot,
  useLibraryStats, isExtractionFixItem, useCanonicalInstances,
  type Halacha, type CanonicalInstance,
} from "@/lib/api/precedent-library";
import { AuthorityBadge, ruleTypeLabel } from "./halacha-meta";
import { formatDateShort as formatDate } from "@/lib/format-date";

/** #81 strict-rubric flags — why an item was held back from auto-approval. */
const QUALITY_FLAG_LABELS: Record<string, string> = {
  non_decision: "אי-הכרעה",
  truncated_quote: "ציטוט קטוע",
  thin_restatement: "ניסוח דק",
  quote_unverified: "ציטוט לא מאומת",
  nli_unsupported: "כלל לא נגזר מהציטוט",
  application: "יישום תלוי-עובדות",
  near_duplicate: "כפילות-קרובה",
  nevo_preamble_leak: "דליפת רציו נבו",
};

/* Strip Unicode bidi marks that render as zero-width but shift visual position. */
function cleanCitation(s: string | null | undefined): string {
  if (!s) return "—";
  return s.replace(/[‎‏‪-‮⁦-⁩]/g, "").trim();
}

type EditState = {
  rule_statement: string;
  reasoning_summary: string;
  supporting_quote: string;  // #133 — editing this re-verifies vs the source
};

// ─── Panel deliberation (#133/FU-2) ───────────────────────────────────────────
// Surfaces the 3-judge panel's vote+rationale inside the chair's review card so
// her decision — the gold label the active-learning loop learns from — is informed
// by WHY the panel split. Display-only; never affects review_status (INV-G10).

const JUDGE_META: Record<string, { name: string; lineage: string }> = {
  claude: { name: "Claude", lineage: "Anthropic" },
  deepseek: { name: "DeepSeek", lineage: "DeepSeek" },
  gemini: { name: "Gemini", lineage: "Google" },
};

const VERDICT_META: Record<string, { label: string; cls: string }> = {
  unanimous_yes: { label: "פה-אחד", cls: "bg-success text-white" },
  unanimous_no: { label: "פה-אחד נגד", cls: "bg-danger text-white" },
  split: { label: "פיצול", cls: "bg-warn text-white" },
  incomplete: { label: "חלקי", cls: "bg-ink-muted text-white" },
};

function PanelDeliberation({ round }: { round: NonNullable<Halacha["panel_round"]> }) {
  const isEntail = round.question === "entailed";
  const yesLabel = isEntail ? "נתמך" : "לשמירה";
  const noLabel = isEntail ? "הכלל חורג" : "לפסילה";
  const question = isEntail
    ? "האם הציטוט תומך בכלל ואינו מרחיב מעבר לו?"
    : "האם זו הלכה בת-הכללה לשמירה?";
  const votes = round.judges.map((j) => j.vote).filter((v): v is boolean => v !== null);
  const yes = votes.filter(Boolean).length;
  const no = votes.length - yes;
  const v = VERDICT_META[round.verdict] ?? VERDICT_META.incomplete;
  const ratio = round.verdict === "split" ? ` ${Math.max(yes, no)}:${Math.min(yes, no)}` : "";
  // The strict-vs-gist explainer only makes sense on a split over entailment.
  const showHint = round.verdict === "split" && isEntail;
  return (
    <div className="rounded-md border border-rule overflow-hidden">
      <div className="flex items-center gap-2 flex-wrap bg-navy text-parchment px-3 py-2">
        <span className="text-[0.72rem] font-bold">התלבטות הפאנל</span>
        <span className={`rounded-full text-[0.62rem] font-bold px-2 py-0.5 ${v.cls}`}>
          {v.label}{ratio}
        </span>
        <span className="ms-auto text-[0.66rem] text-parchment/70" dir="rtl">
          השאלה: ״{question}״
        </span>
      </div>
      {round.judges.map((j) => {
        const yesVote = j.vote === true;
        const noVote = j.vote === false;
        const meta = JUDGE_META[j.model] ?? { name: j.model, lineage: "" };
        return (
          <div key={j.model}
            className="grid grid-cols-[5.5rem_6.5rem_1fr] gap-2 items-start px-3 py-2 border-t border-rule-soft">
            <div className="text-[0.72rem] font-semibold text-navy leading-tight">
              {meta.name}
              <span className="block text-[0.6rem] text-ink-muted font-medium">{meta.lineage}</span>
            </div>
            <span className={`inline-flex items-center justify-self-start rounded-md text-[0.66rem] font-semibold px-2 py-0.5 whitespace-nowrap ${
              yesVote ? "bg-success-bg text-success"
              : noVote ? "bg-danger-bg text-danger"
              : "bg-rule-soft text-ink-muted"
            }`}>
              {j.vote === null ? "— ללא" : yesVote ? `✓ ${yesLabel}` : `✗ ${noLabel}`}
            </span>
            <p className="text-[0.72rem] text-ink-soft leading-snug" dir="rtl">{j.reason || "—"}</p>
          </div>
        );
      })}
      {showHint && (
        <div className="flex gap-2 items-start bg-info-bg text-[0.68rem] leading-relaxed px-3 py-2 border-t border-rule-soft" dir="rtl">
          <Info className="w-3.5 h-3.5 mt-0.5 shrink-0 text-info" />
          <p>
            <b className="text-info">שורש המחלוקת:</b>{" "}
            סטנדרט קפדני (כל פרט בכלל חייב להופיע בציטוט) מול סטנדרט-תמצית (הגרעין המשפטי נתמך).
            הכרעתך קובעת איזה סטנדרט הפאנל יאמץ.
          </p>
        </div>
      )}
      {/* seedline (mockup 18): the chair's call IS the gold label the
          active-learning loop trains on — surfaced on every deliberation. */}
      <p
        className="border-t border-rule-soft bg-gold-wash/40 px-3 py-1.5 text-[0.64rem] text-ink-muted leading-snug"
        dir="rtl"
      >
        הכרעתך תיקלט כתווית-הזהב שממנה לומדת לולאת-הלמידה-הפעילה.
      </p>
    </div>
  );
}

// ─── V41: Canonical section (principle statement + instances accordion) ──────

const INSTANCE_TYPE_LABELS: Record<string, string> = {
  original: "עיקרון מקורי",
  citation: "ציטוט",
  application: "יישום",
};
const INSTANCE_TYPE_CLS: Record<string, string> = {
  original: "bg-navy text-parchment",
  citation: "bg-info text-white",
  application: "bg-ink-muted text-white",
};

function CanonicalSection({
  h, onSaveCanonical,
}: {
  h: Halacha;
  onSaveCanonical: (stmt: string) => Promise<void>;
}) {
  const [editingCanon, setEditingCanon] = useState(false);
  const [canonDraft, setCanonDraft] = useState(h.canonical_statement ?? "");
  const [showInstances, setShowInstances] = useState(false);
  const { data: instances, isLoading: instLoading } = useCanonicalInstances(
    showInstances ? h.canonical_id : null,
  );

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setCanonDraft(h.canonical_statement ?? "");
  }, [h.canonical_id, h.canonical_statement]);

  const handleSave = async () => {
    await onSaveCanonical(canonDraft);
    setEditingCanon(false);
  };

  const instanceCount = h.instance_count ?? 1;

  return (
    <div className="rounded-lg border border-[#d4cdef] bg-[#f0ecfb] p-3 space-y-2">
      <div className="flex items-center gap-2 flex-wrap">
        <span className="w-4 h-4 rounded-sm bg-[#6d5fa6] text-white text-[0.6rem] font-bold flex items-center justify-center flex-shrink-0">כ</span>
        <span className="text-[0.7rem] font-bold text-[#6d5fa6]">ניסוח קנוני — העיקרון הרחב (V41)</span>
        <span className="text-[0.65rem] text-[#9089b8] ms-auto">
          {instanceCount > 1 ? `מאחד ${instanceCount} פסיקות` : "instance יחיד"}
          {h.review_status && ` · ${h.review_status}`}
        </span>
      </div>

      {editingCanon ? (
        <>
          <Textarea
            value={canonDraft}
            onChange={(e) => setCanonDraft(e.target.value)}
            rows={3}
            dir="rtl"
            className="bg-white/85 border-[#6d5fa6]/50 text-[0.82rem]"
          />
          <div className="flex items-center gap-2 justify-end">
            <button
              type="button"
              onClick={() => setEditingCanon(false)}
              className="text-[0.72rem] text-ink-muted hover:text-navy"
            >
              ביטול
            </button>
            <button
              type="button"
              onClick={handleSave}
              className="rounded-md bg-[#6d5fa6] text-white text-[0.72rem] font-semibold px-3 py-1"
            >
              שמור ניסוח קנוני
            </button>
          </div>
        </>
      ) : (
        <div className="flex items-start gap-2">
          <p className="text-[0.82rem] text-ink font-medium leading-relaxed flex-1 bg-white/65 rounded px-2 py-1.5 border border-[#d4cdef]" dir="rtl">
            {h.canonical_statement || <span className="text-ink-muted italic">— pending synthesis —</span>}
          </p>
          <button
            type="button"
            onClick={() => setEditingCanon(true)}
            className="text-[0.65rem] text-[#6d5fa6] hover:underline flex-shrink-0 mt-1"
          >
            ערוך
          </button>
        </div>
      )}

      {instanceCount > 1 && (
        <div className="rounded-md border border-[#d4cdef] overflow-hidden">
          <button
            type="button"
            onClick={() => setShowInstances((v) => !v)}
            className="w-full flex items-center gap-2 px-3 py-2 text-[0.7rem] text-[#6d5fa6] font-medium hover:bg-[#e9e4f8] transition-colors"
            aria-expanded={showInstances}
          >
            {showInstances ? <ChevronDown className="w-3 h-3" /> : <ChevronLeft className="w-3 h-3" />}
            <span>אינסטנסים — {instanceCount} פסיקות שמאזכרות עיקרון זה</span>
          </button>
          {showInstances && (
            <div className="bg-white p-2 space-y-1">
              {instLoading && <p className="text-[0.7rem] text-ink-muted px-2">טוען...</p>}
              {instances?.map((inst: CanonicalInstance) => (
                <div key={inst.id}
                  className="flex items-center gap-2 rounded px-2 py-1.5 border border-rule-soft text-[0.72rem]">
                  <span className="font-semibold text-navy min-w-[80px]">{inst.case_number}</span>
                  <span className="text-ink-soft flex-1 truncate">{inst.case_name}</span>
                  <Badge className={`text-[0.6rem] font-bold border-0 rounded-full px-2 ${INSTANCE_TYPE_CLS[inst.instance_type] ?? "bg-rule text-ink"}`}>
                    {INSTANCE_TYPE_LABELS[inst.instance_type] ?? inst.instance_type}
                  </Badge>
                  {inst.confidence > 0 && (
                    <span className="text-ink-muted tabular-nums">{inst.confidence.toFixed(2)}</span>
                  )}
                </div>
              ))}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ─── Pending-queue card (full interactions) ───────────────────────────────────

function HalachaCard({
  h, focused, onApprove, onReject, onDefer, onSave,
}: {
  h: Halacha & { variants?: Halacha[] };
  focused: boolean;
  onApprove: () => void;
  onReject: () => void;
  onDefer: () => void;
  onSave: (patch: Partial<EditState>) => Promise<void>;
}) {
  const variants = h.variants ?? [];
  const equivalents = h.equivalents ?? [];
  const [showVariants, setShowVariants] = useState(false);
  const [showEquiv, setShowEquiv] = useState(false);
  const [editing, setEditing] = useState(false);
  const [draft, setDraft] = useState<EditState>({
    rule_statement: h.rule_statement,
    reasoning_summary: h.reasoning_summary,
    supporting_quote: h.supporting_quote,
  });

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setDraft({
      rule_statement: h.rule_statement,
      reasoning_summary: h.reasoning_summary,
      supporting_quote: h.supporting_quote,
    });
  }, [h.id, h.rule_statement, h.reasoning_summary, h.supporting_quote]);

  const onSubmitEdit = async () => {
    // Only send the quote when it actually changed — that triggers server-side
    // re-verification + flag sync (#133); unchanged edits keep the old behavior.
    const patch: Partial<EditState> = {
      rule_statement: draft.rule_statement,
      reasoning_summary: draft.reasoning_summary,
    };
    if (draft.supporting_quote !== h.supporting_quote) {
      patch.supporting_quote = draft.supporting_quote;
    }
    await onSave(patch);
    setEditing(false);
  };

  return (
    <div
      data-halacha-id={h.id}
      className={`
        rounded-lg border p-4 space-y-3 transition-colors
        ${h.panel_round ? "bg-gold-wash" : "bg-surface"}
        ${focused ? "border-gold ring-2 ring-gold/40 shadow-md" : "border-rule"}
      `}
    >
      <div className="flex items-start gap-2 text-[0.78rem] text-ink-muted flex-wrap">
        {/* gold "הלכה" tag opening the meta row (mockup 19 `.b-hal`) */}
        <Badge className="rounded bg-gold text-white border-0 text-[0.62rem] font-bold tracking-wide">
          הלכה
        </Badge>
        {/* V41: instance_type badge */}
        {h.instance_type && (
          <Badge className={`rounded border-0 text-[0.62rem] font-bold ${INSTANCE_TYPE_CLS[h.instance_type] ?? "bg-rule text-ink"}`}>
            {INSTANCE_TYPE_LABELS[h.instance_type] ?? h.instance_type}
          </Badge>
        )}
        {/* V41: instance count pill */}
        {(h.instance_count ?? 0) > 1 && (
          <Badge variant="outline" className="text-[0.62rem] border-[#d4cdef] text-[#6d5fa6] bg-[#f0ecfb]">
            מוזכר ב-{h.instance_count} פסיקות
          </Badge>
        )}
        {h.page_reference && (
          <span className="text-[0.7rem]">{h.page_reference}</span>
        )}
        <span className="ms-auto flex items-center gap-2">
          {h.quote_verified ? (
            <Badge variant="outline" className="text-[0.65rem] bg-gold-wash text-gold-deep border-gold/40">
              <Check className="w-3 h-3 me-1" /> ציטוט מאומת
            </Badge>
          ) : (
            <Badge variant="outline" className="text-[0.65rem] bg-danger-bg text-danger border-danger/40">
              <AlertTriangle className="w-3 h-3 me-1" /> ציטוט לא מאומת
            </Badge>
          )}
          <Badge variant="outline" className="text-[0.65rem] tabular-nums">
            ביטחון {h.confidence.toFixed(2)}
          </Badge>
          <Badge variant="outline" className="text-[0.65rem]">
            {ruleTypeLabel(h.rule_type)}
          </Badge>
          <AuthorityBadge authority={h.authority} />
          {variants.length > 0 && (
            <Badge variant="outline"
              className="text-[0.65rem] bg-navy-soft/30 text-navy border-navy/30">
              +{variants.length} וריאנטים
            </Badge>
          )}
          {equivalents.length > 0 && (
            <Badge variant="outline"
              className="text-[0.65rem] bg-gold-wash text-gold-deep border-gold/40">
              עיקרון מקביל ב-{equivalents.length}
            </Badge>
          )}
          <CorroborationBadge halacha={h} />
        </span>
      </div>

      {h.quality_flags && h.quality_flags.length > 0 && (
        <div className="flex items-center gap-1.5 flex-wrap">
          {h.quality_flags.map((f) => (
            <Badge key={f} variant="outline"
              className="text-[0.65rem] bg-danger-bg text-danger border-danger/40">
              <AlertTriangle className="w-3 h-3 me-1" />
              {QUALITY_FLAG_LABELS[f] ?? f}
            </Badge>
          ))}
        </div>
      )}

      <div className="grid md:grid-cols-2 gap-3">
        <div>
          <div className="text-[0.7rem] text-ink-muted mb-1">ניסוח הכלל</div>
          {editing ? (
            <Textarea
              value={draft.rule_statement} rows={4} dir="rtl"
              onChange={(e) => setDraft({ ...draft, rule_statement: e.target.value })}
              className="bg-gold-wash/50 border-gold/30"
            />
          ) : (
            <p className="text-navy font-medium leading-relaxed" dir="rtl">
              {h.rule_statement}
            </p>
          )}
        </div>
        <div>
          <div className="text-[0.7rem] text-ink-muted mb-1">ציטוט תומך</div>
          {editing ? (
            <>
              <Textarea
                value={draft.supporting_quote} rows={4} dir="rtl"
                onChange={(e) => setDraft({ ...draft, supporting_quote: e.target.value })}
                className="bg-gold-wash/50 border-gold/30"
              />
              <p className="text-[0.66rem] text-info mt-1 leading-snug">
                השמירה מאמתת את הציטוט מול טקסט-המקור; אם תואם — הדגל ״ציטוט לא-מאומת״ יוסר.
              </p>
            </>
          ) : (
            <blockquote className="text-ink-soft text-sm leading-relaxed border-r-2 border-gold pr-3" dir="rtl">
              &ldquo;{h.supporting_quote}&rdquo;
            </blockquote>
          )}
        </div>
      </div>

      {h.panel_round && <PanelDeliberation round={h.panel_round} />}

      {(editing || h.reasoning_summary) && (
        <div>
          <div className="text-[0.7rem] text-ink-muted mb-1">תמצית ההיגיון</div>
          {editing ? (
            <Textarea
              value={draft.reasoning_summary} rows={2} dir="rtl"
              onChange={(e) => setDraft({ ...draft, reasoning_summary: e.target.value })}
              className="bg-gold-wash/50 border-gold/30"
            />
          ) : (
            <p className="text-ink-soft text-sm leading-relaxed" dir="rtl">
              {h.reasoning_summary}
            </p>
          )}
        </div>
      )}

      <div className="flex items-center gap-2 flex-wrap">
        {h.practice_areas?.map((p) => (
          <Badge key={p} variant="outline" className="text-[0.65rem] bg-navy-soft/30 text-navy">
            {practiceAreaLabel(p)}
          </Badge>
        ))}
        {h.subject_tags?.map((t) => (
          <Badge key={t} variant="outline" className="text-[0.65rem]">
            {t}
          </Badge>
        ))}
      </div>

      {variants.length > 0 && (
        <div className="rounded-md border border-navy/20 bg-navy-soft/10">
          <button
            type="button"
            onClick={() => setShowVariants((v) => !v)}
            className="w-full flex items-center gap-2 px-3 py-2 text-[0.72rem] text-navy hover:bg-navy-soft/20 transition-colors"
            aria-expanded={showVariants}
          >
            {showVariants ? <ChevronDown className="w-3.5 h-3.5" /> : <ChevronLeft className="w-3.5 h-3.5" />}
            <span className="font-medium">
              {variants.length} ניסוחים כמעט-זהים של אותו עיקרון
            </span>
            <span className="me-auto text-ink-muted">החלטה תחול על כולם</span>
          </button>
          {showVariants && (
            <ul className="px-4 pb-3 pt-1 space-y-2">
              {variants.map((v) => (
                <li key={v.id} className="text-[0.78rem] text-ink-soft leading-relaxed border-r-2 border-navy/20 pr-3" dir="rtl">
                  {v.rule_statement}
                  <span className="text-[0.65rem] text-ink-muted tabular-nums ms-2">
                    (ביטחון {v.confidence.toFixed(2)})
                  </span>
                </li>
              ))}
            </ul>
          )}
        </div>
      )}

      {equivalents.length > 0 && (
        <div className="rounded-md border border-gold/30 bg-gold-wash/40">
          <button
            type="button"
            onClick={() => setShowEquiv((v) => !v)}
            className="w-full flex items-center gap-2 px-3 py-2 text-[0.72rem] text-gold-deep hover:bg-gold-wash/70 transition-colors"
            aria-expanded={showEquiv}
          >
            {showEquiv ? <ChevronDown className="w-3.5 h-3.5" /> : <ChevronLeft className="w-3.5 h-3.5" />}
            <span className="font-medium">
              עיקרון מקביל ב-{equivalents.length} החלטות אחרות (אסמכתה מקבילה)
            </span>
            <span className="me-auto text-ink-muted">לא ציטוט — הישנות עצמאית</span>
          </button>
          {showEquiv && (
            <ul className="px-4 pb-3 pt-1 space-y-2">
              {equivalents.map((e) => (
                <li key={e.halacha_id} className="text-[0.78rem] text-ink-soft leading-relaxed border-r-2 border-gold/30 pr-3" dir="rtl">
                  <span className="font-semibold text-navy">{cleanCitation(e.case_number)}</span>
                  {" — "}{e.rule_statement}
                  {e.cosine != null && (
                    <span className="text-[0.65rem] text-ink-muted tabular-nums ms-2">
                      (דמיון {e.cosine.toFixed(2)})
                    </span>
                  )}
                </li>
              ))}
            </ul>
          )}
        </div>
      )}

      {/* V41: canonical statement + instances accordion */}
      {h.canonical_id && (
        <CanonicalSection
          h={h}
          onSaveCanonical={async (stmt) => {
            await onSave({ canonical_statement: stmt } as Parameters<typeof onSave>[0]);
          }}
        />
      )}

      <div className="flex items-center gap-2 justify-end pt-1 border-t border-rule-soft">
        {editing ? (
          <>
            <Button size="sm" variant="ghost" onClick={() => setEditing(false)}>
              ביטול
            </Button>
            <Button size="sm" onClick={onSubmitEdit}
              className="bg-navy text-parchment hover:bg-navy-soft">
              שמור שינויים
            </Button>
          </>
        ) : (
          <>
            <Button size="sm" variant="ghost" onClick={() => setEditing(true)}>
              <Edit2 className="w-3.5 h-3.5 me-1" />
              ערוך (E)
            </Button>
            <Button size="sm" variant="ghost" onClick={onDefer}
              className="text-ink-muted hover:text-navy">
              <Clock className="w-3.5 h-3.5 me-1" />
              דחה למועד (D)
            </Button>
            <Button size="sm" variant="ghost"
              onClick={onReject}
              className="text-danger hover:text-danger hover:bg-danger-bg">
              <X className="w-3.5 h-3.5 me-1" />
              דחה (R)
            </Button>
            <Button size="sm" onClick={onApprove}
              className="bg-gold text-navy hover:bg-gold-deep">
              <Check className="w-3.5 h-3.5 me-1" />
              אשר (A)
            </Button>
          </>
        )}
      </div>
    </div>
  );
}

// ─── Restore card (rejected / approved tabs) ──────────────────────────────────

function HalachaRestoreCard({
  h,
  primaryLabel,
  primaryAction,
  secondaryLabel,
  secondaryAction,
}: {
  h: Halacha;
  primaryLabel: string;
  primaryAction: () => void;
  secondaryLabel: string;
  secondaryAction: () => void;
}) {
  return (
    <div className="rounded-lg border border-rule bg-surface p-4 space-y-3">
      <div className="flex items-start gap-2 text-[0.78rem] text-ink-muted flex-wrap">
        {h.page_reference && <span className="text-[0.7rem]">{h.page_reference}</span>}
        <span className="ms-auto flex items-center gap-2">
          <Badge variant="outline" className="text-[0.65rem] tabular-nums">
            ביטחון {h.confidence.toFixed(2)}
          </Badge>
          <Badge variant="outline" className="text-[0.65rem]">
            {ruleTypeLabel(h.rule_type)}
          </Badge>
          <AuthorityBadge authority={h.authority} />
          <CorroborationBadge halacha={h} />
        </span>
      </div>

      <div className="grid md:grid-cols-2 gap-3">
        <div>
          <div className="text-[0.7rem] text-ink-muted mb-1">ניסוח הכלל</div>
          <p className="text-navy font-medium leading-relaxed" dir="rtl">
            {h.rule_statement}
          </p>
        </div>
        <div>
          <div className="text-[0.7rem] text-ink-muted mb-1">ציטוט תומך</div>
          <blockquote className="text-ink-soft text-sm leading-relaxed border-r-2 border-gold pr-3" dir="rtl">
            &ldquo;{h.supporting_quote}&rdquo;
          </blockquote>
        </div>
      </div>

      <div className="flex items-center gap-2 flex-wrap">
        {h.practice_areas?.map((p) => (
          <Badge key={p} variant="outline" className="text-[0.65rem] bg-navy-soft/30 text-navy">
            {practiceAreaLabel(p)}
          </Badge>
        ))}
      </div>

      {h.panel_round && <PanelDeliberation round={h.panel_round} />}

      <div className="flex items-center gap-2 justify-end pt-1 border-t border-rule-soft">
        <Button size="sm" variant="ghost"
          onClick={secondaryAction}
          className="text-ink-muted hover:text-navy">
          <RotateCcw className="w-3.5 h-3.5 me-1" />
          {secondaryLabel}
        </Button>
        <Button size="sm" onClick={primaryAction}
          className="bg-gold text-navy hover:bg-gold-deep">
          <Check className="w-3.5 h-3.5 me-1" />
          {primaryLabel}
        </Button>
      </div>
    </div>
  );
}

// ─── Shared group type ────────────────────────────────────────────────────────

/** #84.2 — a review card: the canonical halacha plus its near-duplicate
 * variants (empty for singletons). Acting on the card acts on the whole set. */
type ReviewItem = Halacha & { variants: Halacha[] };

type Group = {
  caseLawId: string;
  caseNumber: string;
  court: string;
  decisionDate: string | null;
  precedentLevel: string;
  items: ReviewItem[];   // canonicals (clusters collapsed)
  pendingTotal: number;  // total halachot incl. variants (for the count badge)
};

/** All halacha ids represented by a review item (canonical + its variants). */
function itemIds(it: ReviewItem): string[] {
  return [it.id, ...it.variants.map((v) => v.id)];
}

function buildGroups(items: Halacha[]): Group[] {
  const byCase = new Map<string, Halacha[]>();
  for (const h of items) {
    const arr = byCase.get(h.case_law_id) ?? [];
    arr.push(h);
    byCase.set(h.case_law_id, arr);
  }
  const groups: Group[] = [];
  for (const [caseLawId, members] of byCase) {
    // collapse near-duplicate clusters (#84.2): one card per cluster_id.
    const clusters = new Map<string, Halacha[]>();
    for (const h of members) {
      const key = h.cluster_id ?? h.id;
      const arr = clusters.get(key) ?? [];
      arr.push(h);
      clusters.set(key, arr);
    }
    const reviewItems: ReviewItem[] = [];
    for (const mem of clusters.values()) {
      // canonical = strongest phrasing (highest confidence, verified wins ties)
      mem.sort((a, b) =>
        b.confidence - a.confidence
        || Number(b.quote_verified) - Number(a.quote_verified));
      reviewItems.push({ ...mem[0], variants: mem.slice(1) });
    }
    // active-learning order within the case: most-uncertain first
    reviewItems.sort((a, b) => a.confidence - b.confidence);
    const first = members[0];
    groups.push({
      caseLawId,
      caseNumber: first.case_number ?? "",
      court: first.court ?? "",
      decisionDate: first.decision_date ?? null,
      precedentLevel: first.precedent_level ?? "",
      items: reviewItems,
      pendingTotal: members.length,
    });
  }
  return groups.sort((a, b) => b.pendingTotal - a.pendingTotal);
}

// ─── Restore panel (used for "rejected" and "approved" tabs) ──────────────────

function RestorePanel({
  status,
  primaryLabel,
  getPrimaryStatus,
  secondaryLabel,
  getSecondaryStatus,
}: {
  status: string;
  primaryLabel: string;
  getPrimaryStatus: () => "approved" | "rejected" | "pending_review";
  secondaryLabel: string;
  getSecondaryStatus: () => "approved" | "rejected" | "pending_review";
}) {
  const [searchInput, setSearchInput] = useState("");
  const [search, setSearch] = useState("");
  useEffect(() => {
    const t = setTimeout(() => setSearch(searchInput.trim()), 300);
    return () => clearTimeout(t);
  }, [searchInput]);

  const { data, isPending, error } = useHalachotByStatus(status, { search });
  const update = useUpdateHalacha();
  const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());

  const groups = useMemo<Group[]>(
    () => buildGroups(data?.items ?? []),
    [data],
  );
  const searching = search.length > 0;
  const matchCount = data?.count ?? 0;

  const restore = async (h: Halacha, newStatus: "approved" | "rejected" | "pending_review") => {
    try {
      await update.mutateAsync({ id: h.id, patch: { review_status: newStatus } });
      toast.success("עודכן");
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "שגיאה");
    }
  };

  const toggleGroup = (caseLawId: string) => {
    setExpandedIds((prev) => {
      const next = new Set(prev);
      if (next.has(caseLawId)) next.delete(caseLawId);
      else next.add(caseLawId);
      return next;
    });
  };

  const body = error ? (
    <div className="rounded bg-danger-bg border border-danger/40 px-6 py-5 text-danger text-center">
      {error.message}
    </div>
  ) : isPending ? (
    <div className="space-y-3">
      {[...Array(3)].map((_, i) => <Skeleton key={i} className="h-24 w-full" />)}
    </div>
  ) : !groups.length ? (
    <div className="text-center text-ink-muted py-16">
      <p className="text-lg">{searching ? "אין התאמות לחיפוש." : "אין הלכות בסטטוס זה."}</p>
    </div>
  ) : (
    <div className="space-y-3">
      {groups.map((g) => {
        const isOpen = expandedIds.has(g.caseLawId);
        return (
          <div key={g.caseLawId} className="rounded-lg border border-rule bg-surface overflow-hidden">
            <button
              type="button"
              onClick={() => toggleGroup(g.caseLawId)}
              className={`
                w-full flex items-center gap-3 px-4 py-3 text-right
                hover:bg-gold-wash/30 transition-colors
                ${isOpen ? "bg-gold-wash/40 border-b border-rule" : ""}
              `}
              aria-expanded={isOpen}
            >
              {isOpen ? (
                <ChevronDown className="w-4 h-4 text-ink-muted shrink-0" />
              ) : (
                <ChevronLeft className="w-4 h-4 text-ink-muted shrink-0" />
              )}
              <div className="flex-1 min-w-0 text-right">
                <div className="font-semibold text-navy truncate">
                  {cleanCitation(g.caseNumber)}
                </div>
                <div className="text-[0.72rem] text-ink-muted flex items-center gap-2 flex-wrap mt-0.5">
                  {g.court && <span>{g.court}</span>}
                  {g.decisionDate && <span>· {formatDate(g.decisionDate)}</span>}
                </div>
              </div>
              <Badge variant="outline" className="tabular-nums">
                {g.pendingTotal}
              </Badge>
            </button>

            {isOpen && (
              <div className="p-4 space-y-3 bg-rule-soft/20">
                {g.items.map((h) => (
                  <HalachaRestoreCard
                    key={h.id}
                    h={h}
                    primaryLabel={primaryLabel}
                    primaryAction={() => restore(h, getPrimaryStatus())}
                    secondaryLabel={secondaryLabel}
                    secondaryAction={() => restore(h, getSecondaryStatus())}
                  />
                ))}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );

  return (
    <div className="space-y-4">
      {/* Locate bar — reach any item in this status, not only the top window */}
      <div className="flex items-center gap-2.5 rounded-lg border border-rule bg-surface px-3.5 py-2.5 shadow-sm">
        <Search className="w-4 h-4 text-ink-muted shrink-0" />
        <input
          value={searchInput}
          onChange={(e) => setSearchInput(e.target.value)}
          placeholder="חיפוש — מספר פס״ד, שם-תיק או נוסח-הלכה"
          dir="rtl"
          className="flex-1 bg-transparent border-0 outline-none text-sm text-ink placeholder:text-ink-muted"
        />
        {searchInput && (
          <button
            type="button"
            onClick={() => setSearchInput("")}
            className="shrink-0 w-5 h-5 rounded-full bg-rule-soft text-ink-muted hover:text-navy
              flex items-center justify-center text-[0.7rem]"
            title="נקה"
          >
            <X className="w-3 h-3" />
          </button>
        )}
      </div>

      {searching && !isPending && !error && (
        <div className="flex items-center gap-2 flex-wrap rounded-lg border border-gold/50 bg-gold-wash px-3.5 py-2.5 text-sm text-navy">
          <span>מציג עבור</span>
          <span className="rounded bg-navy text-parchment text-[0.78rem] font-semibold px-2 py-0.5">
            {search}
          </span>
          <span className="text-ink-muted">
            · {matchCount} {matchCount === 1 ? "התאמה" : "התאמות"}
          </span>
          <button
            type="button"
            onClick={() => setSearchInput("")}
            className="ms-auto text-gold-deep font-semibold text-[0.8rem] underline hover:no-underline"
          >
            נקה חיפוש
          </button>
        </div>
      )}

      {body}
    </div>
  );
}

// ─── Pending queue panel (main review flow) ───────────────────────────────────

function PendingPanel() {
  // #133 unified queue — ONE fetch of all pending, split client-side by ACTION:
  // "judgment" = items the panel deliberated (or clean) → chair approves/rejects;
  // "fix" = flagged-but-never-adjudicated → extraction repair. (No empty "תור נקי".)
  const [view, setView] = useState<"judgment" | "fix">("judgment");
  // Locate a pending halacha by decision/text — server-side, so an item ranked
  // below the 500-item display window is still reachable (the chair's core ask).
  const [searchInput, setSearchInput] = useState("");
  const [search, setSearch] = useState("");
  useEffect(() => {
    const t = setTimeout(() => setSearch(searchInput.trim()), 300);
    return () => clearTimeout(t);
  }, [searchInput]);
  const searching = search.length > 0;

  const { data, isPending, error } = useHalachotPending({ limit: 500, search });
  const update = useUpdateHalacha();
  const batch = useBatchReviewHalachot();
  const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
  const [focusedId, setFocusedId] = useState<string | null>(null);

  const allItems = useMemo(() => data?.items ?? [], [data]);
  // Full pending count for this filter (incl. items beyond the display window).
  const pendingTotal = data?.total ?? allItems.length;
  const judgmentCount = useMemo(
    () => allItems.filter((h) => !isExtractionFixItem(h)).length, [allItems]);
  const fixCount = useMemo(
    () => allItems.filter(isExtractionFixItem).length, [allItems]);

  const segmentItems = useMemo(
    () => allItems.filter((h) =>
      view === "fix" ? isExtractionFixItem(h) : !isExtractionFixItem(h)),
    [allItems, view],
  );
  const groups = useMemo<Group[]>(() => buildGroups(segmentItems), [segmentItems]);
  const totalCount = segmentItems.length;

  const visibleItems = useMemo<ReviewItem[]>(() => {
    const out: ReviewItem[] = [];
    for (const g of groups) {
      // a search narrows to a handful of cases — show them all open
      if (searching || expandedIds.has(g.caseLawId)) out.push(...g.items);
    }
    return out;
  }, [groups, expandedIds, searching]);

  useEffect(() => {
    if (focusedId === null) return;
    if (visibleItems.some((h) => h.id === focusedId)) return;
    setFocusedId(visibleItems[0]?.id ?? null);
  }, [focusedId, visibleItems]);

  useEffect(() => {
    if (!focusedId) return;
    const el = document.querySelector(`[data-halacha-id="${focusedId}"]`);
    el?.scrollIntoView({ block: "nearest", behavior: "smooth" });
  }, [focusedId]);

  const focused: ReviewItem | null = focusedId
    ? visibleItems.find((h) => h.id === focusedId) ?? null
    : null;

  const moveFocus = (delta: 1 | -1) => {
    if (visibleItems.length === 0) return;
    const idx = focusedId
      ? visibleItems.findIndex((h) => h.id === focusedId)
      : -1;
    const next = idx < 0
      ? (delta > 0 ? 0 : visibleItems.length - 1)
      : Math.max(0, Math.min(visibleItems.length - 1, idx + delta));
    setFocusedId(visibleItems[next].id);
  };

  const REVIEW_TOAST: Record<string, string> = {
    approved: "אושר", rejected: "נדחה", deferred: "נדחה למועד",
  };

  const review = async (
    it: ReviewItem,
    status: "approved" | "rejected" | "deferred",
    extra?: Partial<EditState>,
  ) => {
    const ids = itemIds(it);
    try {
      // #84.2 — a cluster card applies the decision to all its variants at once
      // (an edit, which is canonical-specific, stays single).
      if (ids.length > 1 && !extra) {
        await batch.mutateAsync({ ids, status });
      } else {
        await update.mutateAsync({
          id: it.id,
          patch: { review_status: status, ...extra },
        });
      }
      toast.success(
        (REVIEW_TOAST[status] ?? "עודכן")
        + (ids.length > 1 ? ` (${ids.length} וריאנטים)` : ""),
      );
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "שגיאה");
    }
  };

  const reviewGroup = async (g: Group, status: "approved" | "rejected") => {
    try {
      const res = await batch.mutateAsync({
        ids: g.items.flatMap(itemIds), status,
      });
      toast.success(
        `${status === "approved" ? "אושרו" : "נדחו"} ${res.updated} הלכות`,
      );
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "שגיאה");
    }
  };

  const toggleGroup = (caseLawId: string) => {
    setExpandedIds((prev) => {
      const next = new Set(prev);
      if (next.has(caseLawId)) {
        next.delete(caseLawId);
      } else {
        next.add(caseLawId);
        const g = groups.find((x) => x.caseLawId === caseLawId);
        if (g && g.items.length) {
          setTimeout(() => setFocusedId(g.items[0].id), 0);
        }
      }
      return next;
    });
  };

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      const tag = (e.target as HTMLElement)?.tagName?.toLowerCase();
      if (tag === "input" || tag === "textarea") return;
      if (e.key === "j") {
        e.preventDefault();
        moveFocus(1);
      } else if (e.key === "k") {
        e.preventDefault();
        moveFocus(-1);
      } else if ((e.key === "a" || e.key === "A") && focused) {
        e.preventDefault();
        review(focused, "approved");
      } else if ((e.key === "r" || e.key === "R") && focused) {
        e.preventDefault();
        if (window.confirm("לדחות הלכה זו?")) review(focused, "rejected");
      } else if ((e.key === "d" || e.key === "D") && focused) {
        e.preventDefault();
        review(focused, "deferred");
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [focused, visibleItems]);

  const segBtn = (key: "judgment" | "fix", label: string, count: number) => (
    <Button
      size="sm"
      variant={view === key ? "default" : "ghost"}
      className={view === key ? "bg-gold text-navy hover:bg-gold-deep" : ""}
      onClick={() => setView(key)}
    >
      {label}
      <span className={`ms-2 text-[0.7rem] px-1.5 py-0.5 rounded-full tabular-nums
        ${view === key ? "bg-navy/15" : "bg-black/10 text-ink-muted"}`}>
        {count}
      </span>
    </Button>
  );

  // #133 — two segments by ACTION (deliberated→judge vs flagged→fix); no empty "תור נקי".
  const viewToggle = (
    <div className="flex items-center gap-1 rounded-lg border border-rule p-0.5 bg-rule-soft/30 w-fit">
      {segBtn("judgment", "להכרעתך", judgmentCount)}
      {segBtn("fix", "דורש תיקון-חילוץ", fixCount)}
    </div>
  );

  let body: ReactNode;
  if (error) {
    body = (
      <div className="rounded bg-danger-bg border border-danger/40 px-6 py-5 text-danger text-center">
        {error.message}
      </div>
    );
  } else if (isPending) {
    body = (
      <div className="space-y-3">
        {[...Array(3)].map((_, i) => <Skeleton key={i} className="h-24 w-full" />)}
      </div>
    );
  } else if (!groups.length) {
    body = searching ? (
      <div className="text-center text-ink-muted py-16">
        <p className="text-lg">לא נמצאו הלכות ממתינות התואמות ל״{search}״.</p>
        <p className="text-sm mt-2">
          נסה מספר-פס״ד, שם-תיק או מילה מנוסח-ההלכה — או נקה את החיפוש.
          {view === "judgment" && fixCount > 0 && " ייתכן שההתאמות נמצאות ב״דורש תיקון-חילוץ״."}
          {view === "fix" && judgmentCount > 0 && " ייתכן שההתאמות נמצאות ב״להכרעתך״."}
        </p>
      </div>
    ) : (
      <div className="text-center text-ink-muted py-16">
        <p className="text-lg">
          {view === "fix"
            ? "אין הלכות הממתינות לתיקון-חילוץ."
            : "אין הלכות הממתינות להכרעתך."}
        </p>
        <p className="text-sm mt-2">
          {view === "fix"
            ? "פריטים שסומנו בפגם-חילוץ (ציטוט לא-מאומת / קטוע / כפילות) ושלא עברו התלבטות-פאנל יופיעו כאן."
            : "הלכות שהפאנל הכריע או דן בהן יופיעו כאן לאישורך."}
        </p>
      </div>
    );
  } else {
    body = (
    <div className="space-y-4">
      <div className="flex items-center gap-3 text-sm text-ink-muted flex-wrap">
        <span>
          <span className="text-navy font-semibold">{totalCount}</span>
          {view === "fix" ? " לתיקון-חילוץ" : " להכרעה"}
          {" "}ב-<span className="text-navy font-semibold">{groups.length}</span> פסיקות
          {view === "judgment" && (
            <span className="text-[0.72rem] ms-2">· ממוין: פיצול-פאנל תחילה</span>
          )}
        </span>
        <span className="me-auto text-[0.72rem]">
          ניווט: <kbd className="bg-rule-soft px-1.5 rounded">J</kbd>/<kbd className="bg-rule-soft px-1.5 rounded">K</kbd>
          {" "}· אישור: <kbd className="bg-rule-soft px-1.5 rounded">A</kbd>
          {" "}· דחייה: <kbd className="bg-rule-soft px-1.5 rounded">R</kbd>
          {" "}· למועד: <kbd className="bg-rule-soft px-1.5 rounded">D</kbd>
          {" "}· עריכה: <kbd className="bg-rule-soft px-1.5 rounded">E</kbd>
        </span>
        {expandedIds.size > 0 && (
          <Button size="sm" variant="ghost" onClick={() => setExpandedIds(new Set())}>
            סגור הכל
          </Button>
        )}
      </div>

      <div className="space-y-3">
        {groups.map((g) => {
          const isOpen = searching || expandedIds.has(g.caseLawId);
          return (
            <div key={g.caseLawId} className="rounded-lg border border-rule bg-surface overflow-hidden">
              <button
                type="button"
                onClick={() => toggleGroup(g.caseLawId)}
                className={`
                  w-full flex items-center gap-3 px-4 py-3 text-right
                  hover:bg-gold-wash/30 transition-colors
                  ${isOpen ? "bg-gold-wash/40 border-b border-rule" : ""}
                `}
                aria-expanded={isOpen}
              >
                {isOpen ? (
                  <ChevronDown className="w-4 h-4 text-ink-muted shrink-0" />
                ) : (
                  <ChevronLeft className="w-4 h-4 text-ink-muted shrink-0" />
                )}
                <div className="flex-1 min-w-0 text-right">
                  <div className="font-semibold text-navy truncate">
                    {cleanCitation(g.caseNumber)}
                  </div>
                  <div className="text-[0.72rem] text-ink-muted flex items-center gap-2 flex-wrap mt-0.5">
                    {g.court && <span>{g.court}</span>}
                    {g.decisionDate && <span>· {formatDate(g.decisionDate)}</span>}
                    {g.precedentLevel && <span>· {g.precedentLevel}</span>}
                  </div>
                </div>
                <Badge variant="outline" className="bg-gold-wash text-gold-deep border-gold/40 tabular-nums">
                  {g.pendingTotal} ממתינות
                  {g.pendingTotal !== g.items.length && ` · ${g.items.length} כרטיסים`}
                </Badge>
              </button>

              {isOpen && (
                <div className="p-4 space-y-3 bg-rule-soft/20">
                  <div className="flex items-center gap-2 justify-end pb-1">
                    <span className="me-auto text-[0.72rem] text-ink-muted">
                      פעולה קבוצתית על {g.pendingTotal} ההלכות:
                    </span>
                    <Button
                      size="sm" variant="ghost" disabled={batch.isPending}
                      onClick={() => {
                        if (window.confirm(`לדחות את כל ${g.pendingTotal} ההלכות בפסק זה?`))
                          reviewGroup(g, "rejected");
                      }}
                      className="text-danger hover:text-danger hover:bg-danger-bg"
                    >
                      <X className="w-3.5 h-3.5 me-1" /> דחה הכל
                    </Button>
                    <Button
                      size="sm" disabled={batch.isPending}
                      onClick={() => {
                        if (window.confirm(`לאשר את כל ${g.pendingTotal} ההלכות בפסק זה?`))
                          reviewGroup(g, "approved");
                      }}
                      className="bg-gold text-navy hover:bg-gold-deep"
                    >
                      <Check className="w-3.5 h-3.5 me-1" /> אשר הכל
                    </Button>
                  </div>
                  {g.items.map((h) => (
                    <HalachaCard
                      key={h.id}
                      h={h}
                      focused={h.id === focusedId}
                      onApprove={() => review(h, "approved")}
                      onReject={() => {
                        if (window.confirm("לדחות הלכה זו?")) review(h, "rejected");
                      }}
                      onDefer={() => review(h, "deferred")}
                      onSave={async (patch) => {
                        try {
                          await update.mutateAsync({ id: h.id, patch });
                          toast.success("נשמר");
                        } catch (e) {
                          toast.error(e instanceof Error ? e.message : "שגיאה");
                        }
                      }}
                    />
                  ))}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
    );
  }

  const overWindow = !searching && pendingTotal > allItems.length;

  return (
    <div className="space-y-4">
      {/* Locate bar — reach any pending halacha, not only the top window */}
      <div className="flex items-center gap-2.5 rounded-lg border border-rule bg-surface px-3.5 py-2.5 shadow-sm">
        <Search className="w-4 h-4 text-ink-muted shrink-0" />
        <input
          value={searchInput}
          onChange={(e) => setSearchInput(e.target.value)}
          placeholder="חיפוש בתור — מספר פס״ד, שם-תיק או נוסח-הלכה"
          dir="rtl"
          className="flex-1 bg-transparent border-0 outline-none text-sm text-ink placeholder:text-ink-muted"
        />
        {searchInput && (
          <button
            type="button"
            onClick={() => setSearchInput("")}
            className="shrink-0 w-5 h-5 rounded-full bg-rule-soft text-ink-muted hover:text-navy
              flex items-center justify-center text-[0.7rem]"
            title="נקה"
          >
            <X className="w-3 h-3" />
          </button>
        )}
      </div>

      {overWindow && (
        <p className="flex items-center gap-2 text-[0.78rem] text-ink-muted px-1 leading-relaxed">
          <span className="bg-info-bg text-info rounded-full px-2 py-0.5 font-semibold text-[0.72rem] shrink-0">
            חלון התצוגה
          </span>
          <span>
            התור מציג את <b className="text-navy">{allItems.length}</b> ההלכות בעלות-העדיפות מתוך{" "}
            <b className="text-navy">{pendingTotal}</b> הממתינות. הלכה מחוץ לחלון — אתר אותה בחיפוש.
          </span>
        </p>
      )}

      {searching && (
        <div className="flex items-center gap-2 flex-wrap rounded-lg border border-gold/50 bg-gold-wash px-3.5 py-2.5 text-sm text-navy">
          <span>מציג ממתינות עבור</span>
          <span className="rounded bg-navy text-parchment text-[0.78rem] font-semibold px-2 py-0.5">
            {search}
          </span>
          <span className="text-ink-muted">
            · {pendingTotal} {pendingTotal === 1 ? "התאמה" : "התאמות"}
          </span>
          <button
            type="button"
            onClick={() => setSearchInput("")}
            className="ms-auto text-gold-deep font-semibold text-[0.8rem] underline hover:no-underline"
          >
            נקה חיפוש · חזרה לכל התור
          </button>
        </div>
      )}

      {viewToggle}
      {body}
    </div>
  );
}

// ─── Main export ──────────────────────────────────────────────────────────────

type Tab = "pending" | "rejected" | "approved";

const TAB_LABELS: Record<Tab, string> = {
  pending: "ממתינות",
  rejected: "נדחו",
  approved: "אושרו",
};

export function HalachaReviewPanel() {
  const [tab, setTab] = useState<Tab>("pending");

  // Real COUNT(*) totals from the stats endpoint — not the limit-capped
  // /api/halachot len(rows), which pinned these chips at 500/1000/1000 and only
  // refreshed on navigation. The stats query polls, so the chips track live (#6/#7/#8).
  const { data: stats } = useLibraryStats();

  const counts: Record<Tab, number | null> = {
    pending: stats?.halachot_pending ?? null,
    rejected: stats?.halachot_rejected ?? null,
    approved: stats?.halachot_approved ?? null,
  };

  return (
    <div className="space-y-4">
      {/* Tab bar */}
      <div className="flex gap-1 border-b border-rule pb-0">
        {(["pending", "rejected", "approved"] as Tab[]).map((t) => (
          <button
            key={t}
            type="button"
            onClick={() => setTab(t)}
            className={`
              px-4 py-2 text-sm font-medium rounded-t-md transition-colors
              ${tab === t
                ? "bg-surface border border-b-surface border-rule text-navy -mb-px"
                : "text-ink-muted hover:text-navy hover:bg-rule-soft/40"
              }
            `}
          >
            {TAB_LABELS[t]}
            {counts[t] !== null && counts[t]! > 0 && (
              <span className={`
                ms-2 text-[0.7rem] px-1.5 py-0.5 rounded-full tabular-nums
                ${t === "pending"
                  ? "bg-gold-wash text-gold-deep"
                  : t === "rejected"
                    ? "bg-danger-bg text-danger"
                    : "bg-navy-soft/20 text-navy"
                }
              `}>
                {counts[t]}
              </span>
            )}
          </button>
        ))}
      </div>

      {/* Tab content */}
      {tab === "pending" && <PendingPanel />}

      {tab === "rejected" && (
        <RestorePanel
          status="rejected"
          primaryLabel="אשר"
          getPrimaryStatus={() => "approved"}
          secondaryLabel="שחזר לתור"
          getSecondaryStatus={() => "pending_review"}
        />
      )}

      {tab === "approved" && (
        <RestorePanel
          status="approved"
          primaryLabel="בטל אישור"
          getPrimaryStatus={() => "pending_review"}
          secondaryLabel="דחה"
          getSecondaryStatus={() => "rejected"}
        />
      )}
    </div>
  );
}
