"use client";

import { useState } from "react";
import Link from "next/link";
import { AppShell } from "@/components/app-shell";
import { SystemHealthSection } from "@/components/operations/system-health-section";
import { AgentAdaptersPanel } from "@/components/operations/agent-adapters-panel";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
} from "@/components/ui/dialog";
import {
  useOperations,
  useServiceAction,
  useDrainToggle,
  useDrainBurst,
  useAgentRuns,
  useRunLog,
  useCancelRun,
  useResetAgentSession,
  useAgentHealth,
  useRecentEscalations,
  type OpsService,
  type OperationsSnapshot,
  type PipelineStats,
  type AgentRun,
  type SubscriptionUsage,
  type UsageWindow,
  type AgentHealthState,
} from "@/lib/api/operations";
import { formatTime, israelParts } from "@/lib/format-date";

function mb(bytes: number): string {
  return `${Math.round((bytes || 0) / 1024 / 1024)}MB`;
}

// claude.ai subscription usage — the fuel gauge for every agent/drain.
// Bar turns amber > 75% and red > 90%; the halacha drain auto-pauses at 100%
// and resumes on its own when a window resets.
function usageResetLabel(iso: string | null): string {
  if (!iso) return "—";
  return `איפוס ${formatTime(iso)}`;
}

function UsageMeter({ label, w }: { label: string; w?: UsageWindow | null }) {
  const util = w?.utilization;
  const active = typeof util === "number";
  const pct = active ? Math.min(100, Math.max(0, util as number)) : 0;
  const fill = pct >= 90 ? "bg-danger" : pct >= 75 ? "bg-warn" : "bg-gold";
  return (
    <div>
      <div className="flex items-baseline justify-between text-[13px] text-ink-soft mb-1.5">
        <span>{label}</span>
        {active ? (
          <b className="text-navy tabular-nums text-[15px] font-bold">{Math.round(pct)}%</b>
        ) : (
          <b className="text-ink-muted text-sm font-semibold">—</b>
        )}
      </div>
      <div className="h-[7px] overflow-hidden rounded-full bg-rule-soft">
        <span className={`block h-full rounded-full ${fill}`} style={{ width: `${pct}%` }} />
      </div>
      <div className="mt-1.5 text-[11.5px] text-ink-muted">
        {active ? usageResetLabel(w?.resets_at ?? null) : "לא פעיל כרגע"}
      </div>
    </div>
  );
}

function SubscriptionUsagePanel({ usage }: { usage: SubscriptionUsage }) {
  if (!usage) return null; // undocumented endpoint unreachable — show nothing
  return (
    <Card className="border-rule">
      <CardContent className="px-5 py-4">
        <div className="grid gap-6 sm:grid-cols-3">
          <UsageMeter label="חלון 5-שעות" w={usage.five_hour} />
          <UsageMeter label="שבועי · כל המודלים" w={usage.seven_day} />
          <UsageMeter label="שבועי · Sonnet" w={usage.seven_day_sonnet} />
        </div>
        <div className="mt-3 border-t border-rule-soft pt-2.5 text-[11.5px] text-ink-muted">
          המנוי המשותף שמפעיל את כל הסוכנים והדריינרים. הפס נצבע בענבר מעל 75%
          ובאדום מעל 90%. דריינר-ההלכות משתהה אוטומטית כשחלון מגיע ל-100% וממשיך
          לבד כשהוא מתאפס.
        </div>
      </CardContent>
    </Card>
  );
}

// mockup 02: every region opens with a navy section heading (h2, 18px) — the
// page reads as a sequence of titled sections rather than a stack of cards.
function SectionHeader({ children }: { children: React.ReactNode }) {
  return <h2 className="text-navy text-lg font-semibold mb-3 mt-2">{children}</h2>;
}

function ago(ms: number): string {
  if (!ms) return "—";
  const secs = Math.floor((Date.now() - ms) / 1000);
  if (secs < 60) return `לפני ${secs}ש׳`;
  if (secs < 3600) return `לפני ${Math.floor(secs / 60)}ד׳`;
  if (secs < 86400) return `לפני ${Math.floor(secs / 3600)}ש׳`;
  return `לפני ${Math.floor(secs / 86400)}י׳`;
}

// Hebrew labels for the raw status strings the backend reports.
const STATUS_HE: Record<string, string> = {
  online: "פעיל",
  stopped: "עצור",
  errored: "שגיאה",
  launching: "עולה",
  pending: "ממתין",
  processing: "בעיבוד",
  running: "רץ",
  done: "הושלם",
  completed: "הושלם",
  failed: "נכשל",
  manual: "ידני",
  approved: "אושר",
  pending_review: "ממתין לאישור",
  rejected: "נדחה",
  published: "פורסם",
  deferred: "נדחה זמנית",
  open: "פתוח",
  closed: "סגור",
  unknown: "לא ידוע",
};

function he(status: string): string {
  return STATUS_HE[status] ?? status;
}

const STATUS_VARIANT: Record<string, "default" | "secondary" | "destructive" | "outline"> = {
  online: "default",
  done: "default",
  completed: "default",
  approved: "default",
  published: "default",
  stopped: "secondary",
  pending: "secondary",
  pending_review: "secondary",
  open: "secondary",
  running: "outline",
  processing: "outline",
  launching: "outline",
  failed: "destructive",
  errored: "destructive",
  manual: "destructive",
  rejected: "destructive",
};

function StatusBadge({ value, count }: { value: string; count?: number }) {
  return (
    <Badge variant={STATUS_VARIANT[value] ?? "outline"} className="font-normal">
      {he(value)}
      {count !== undefined ? <span className="ms-1 font-semibold">{count}</span> : null}
    </Badge>
  );
}

const SERVICE_LABELS: Record<string, string> = {
  "legal-court-fetch-service": "שירות אחזור פסיקה (דפדפן נט המשפט)",
  "legal-court-fetch-xvfb": "צג וירטואלי (Xvfb) לדפדפן",
  "legal-court-fetch-drain": "תזמון: ניקוז תור אחזור פסיקה (שעתי)",
  "legal-metadata-drain": "תזמון: חילוץ מטא-דאטה (Gemini, ×15 דק׳)",
  "legal-halacha-drain": "תזמון: חילוץ הלכות (Claude, חלון-לילה 23:00–05:00)",
  "legal-halacha-supervisor": "ניהול בריאות חילוץ-ההלכות + בקרת BURST (×15 דק׳)",
  "legal-digest-drain": "תזמון: העשרת יומונים (Sonnet, ×שעתיים)",
  "legal-reaper": "מנקה תהליכים-יתומים (נגד דליפות זיכרון)",
  "legal-chat-service": "שירות צ׳אט אימון (גשר ל-claude CLI)",
};

// ── BURST control (halacha drain) — manual "run continuously now until X" ──
function pad2(n: number): string {
  return String(n).padStart(2, "0");
}
/** Format a Date as a datetime-local input value ("YYYY-MM-DDTHH:MM"), local tz. */
function toLocalInput(d: Date): string {
  return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}T${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
}
/** The upcoming Saturday at 18:00, local time. */
function nextSaturday18(): Date {
  const d = new Date();
  d.setHours(18, 0, 0, 0);
  d.setDate(d.getDate() + ((6 - d.getDay() + 7) % 7)); // Sat = getDay() 6
  if (d.getTime() <= Date.now()) d.setDate(d.getDate() + 7);
  return d;
}
const HE_DAYS = ["א׳", "ב׳", "ג׳", "ד׳", "ה׳", "ו׳", "ש׳"];
function fmtDeadline(iso: string): string {
  const p = israelParts(iso);
  if (!p) return "—";
  return `${HE_DAYS[p.weekday]} ${pad2(p.day)}/${pad2(p.month)} ${pad2(p.hour)}:${pad2(p.minute)}`;
}

function BurstControl({ s }: { s: OpsService }) {
  const burst = useDrainBurst();
  const [open, setOpen] = useState(false);
  const [until, setUntil] = useState(() => toLocalInput(nextSaturday18()));
  // The backend (supervisor) is the authority on expiry — it NULLs burst_until at
  // the deadline (snapshot refetches every 5s), so presence ⇒ active. Avoids an
  // impure Date.now() during render.
  const active = !!s.burst_until;

  if (active) {
    return (
      <span className="flex items-center gap-1.5 shrink-0">
        <Badge variant="outline" className="border-gold/40 bg-gold-wash text-gold-deep text-[0.7rem] font-semibold whitespace-nowrap">
          ⚡ BURST · עד {fmtDeadline(s.burst_until!)}
        </Badge>
        <Button
          size="xs"
          variant="ghost"
          className="text-destructive"
          disabled={burst.isPending}
          onClick={() => {
            if (confirm("לעצור את ה-BURST? החילוץ יחזור לחלון-הלילה (23:00–05:00) והדריינר ייעצר.")) {
              burst.mutate({ name: s.name, action: "off" });
            }
          }}
        >
          עצור BURST
        </Button>
      </span>
    );
  }

  return (
    <>
      <Button
        size="xs"
        variant="outline"
        className="border-gold text-gold-deep bg-gold-wash hover:bg-gold-wash/70"
        disabled={busyBurst(burst.isPending, s.disabled)}
        title={s.disabled ? "הפעל את התזמון תחילה" : "הרץ את חילוץ-ההלכות ברצף עכשיו עד מועד נבחר"}
        onClick={() => setOpen(true)}
      >
        ⚡ הפעל BURST
      </Button>
      <Dialog open={open} onOpenChange={setOpen}>
        <DialogContent className="max-w-md">
          <DialogHeader>
            <DialogTitle className="flex items-center gap-2">⚡ הפעלת BURST — חילוץ הלכות רצוף</DialogTitle>
            <DialogDescription className="text-start leading-relaxed">
              הדריינר ירוץ <b>ברצף מעכשיו</b> (מתעלם מחלון-הלילה) ויעבד את תור-ההלכות עד המועד שתבחר,
              תוך ניצול מכסת-Claude הפנויה. נעצר אוטומטית במועד — או ידנית בכפתור &quot;עצור BURST&quot;.
            </DialogDescription>
          </DialogHeader>
          <div className="space-y-2">
            <Label htmlFor="burst-until">רוץ עד</Label>
            <Input
              id="burst-until"
              type="datetime-local"
              value={until}
              dir="ltr"
              className="text-end"
              onChange={(e) => setUntil(e.target.value)}
            />
            <div className="flex flex-wrap gap-1.5 pt-1">
              <button type="button" className="text-[0.72rem] text-gold-deep bg-gold-wash border border-gold rounded-full px-2.5 py-0.5 font-semibold hover:border-gold-deep"
                onClick={() => setUntil(toLocalInput(nextSaturday18()))}>
                שבת 18:00 <span className="font-normal text-ink-muted">(ברירת-מחדל)</span>
              </button>
              <button type="button" className="text-[0.72rem] text-gold-deep bg-gold-wash border border-rule rounded-full px-2.5 py-0.5 hover:border-gold"
                onClick={() => setUntil(toLocalInput(new Date(Date.now() + 5 * 3600 * 1000)))}>
                עוד 5 שעות
              </button>
              <button type="button" className="text-[0.72rem] text-gold-deep bg-gold-wash border border-rule rounded-full px-2.5 py-0.5 hover:border-gold"
                onClick={() => {
                  const m = new Date();
                  m.setHours(24, 0, 0, 0);
                  setUntil(toLocalInput(m));
                }}>
                חצות
              </button>
            </div>
            <p className="text-[0.72rem] text-ink-muted bg-info/10 rounded-md px-2.5 py-2 leading-snug">
              ℹ️ הפעלה ידנית בלבד — המערכת לא תפעיל BURST לבד. נכנס לתוקף תוך ≤15 דק׳ (מחזור המתזמר).
            </p>
          </div>
          <DialogFooter>
            <Button variant="outline" size="sm" onClick={() => setOpen(false)}>
              ביטול
            </Button>
            <Button
              size="sm"
              className="bg-gold text-white hover:bg-gold-deep"
              disabled={burst.isPending || !until}
              onClick={() => {
                burst.mutate({ name: s.name, action: "on", until });
                setOpen(false);
              }}
            >
              ⚡ הפעל BURST
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  );
}
function busyBurst(pending: boolean, disabled?: boolean): boolean {
  return pending || !!disabled;
}

// ── Process-management panel (the "Windows services" view) ─────────────────
function ServiceControls({
  s,
  busy,
  onAction,
  onToggle,
}: {
  s: OpsService;
  busy: boolean;
  onAction: (action: "restart" | "stop" | "start" | "run-now") => void;
  onToggle: (disabled: boolean) => void;
}) {
  const isCron = !!s.cron;

  if (isCron) {
    // Cron drain: "run now" (pm2 restart) + an enable/disable switch (DB flag).
    return (
      <div className="flex items-center gap-2 shrink-0">
        <Button
          size="xs"
          variant="outline"
          disabled={busy || s.disabled}
          onClick={() => onAction("run-now")}
          title={s.disabled ? "הפעל את התזמון כדי להריץ" : "הרץ את ה-drain פעם אחת מיד"}
        >
          הרץ עכשיו
        </Button>
        {s.name === "legal-halacha-drain" && <BurstControl s={s} />}
        <label className="flex items-center gap-1.5 text-[0.7rem] text-ink-muted cursor-pointer">
          <Switch
            checked={!s.disabled}
            disabled={busy}
            onCheckedChange={(on) => {
              if (on || confirm(`לכבות את התזמון "${SERVICE_LABELS[s.name] ?? s.name}"?`)) {
                onToggle(!on);
              }
            }}
          />
          {s.disabled ? "כבוי" : "פעיל"}
        </label>
      </div>
    );
  }

  // Daemon: restart / stop / start.
  const online = s.status === "online";
  return (
    <div className="flex items-center gap-1.5 shrink-0">
      <Button size="xs" variant="outline" disabled={busy} onClick={() => onAction("restart")}>
        הפעל מחדש
      </Button>
      {online ? (
        <Button
          size="xs"
          variant="ghost"
          disabled={busy}
          className="text-destructive"
          onClick={() => {
            if (confirm(`לעצור את "${SERVICE_LABELS[s.name] ?? s.name}"?`)) onAction("stop");
          }}
        >
          עצור
        </Button>
      ) : (
        <Button size="xs" variant="ghost" disabled={busy} onClick={() => onAction("start")}>
          הפעל
        </Button>
      )}
    </div>
  );
}

function ServicesPanel({ data }: { data: OperationsSnapshot }) {
  const action = useServiceAction();
  const toggle = useDrainToggle();
  const busy = action.isPending || toggle.isPending;

  return (
    <Card className="bg-surface border-rule shadow-sm">
      <CardContent className="px-6 py-5">
        <p className="text-ink-muted text-xs mb-4">
          ניהול תהליכי-רקע (pm2) — כמו &quot;שירותים&quot; ב-Windows. דמון = שירות
          רץ-תמיד (הפעל-מחדש/עצור/הפעל). תזמון (cron) = רץ לפי לוח-זמנים
          (&quot;הרץ עכשיו&quot; להרצה מיידית, ומתג הפעלה/כיבוי של התזמון).
        </p>
        {data.services_error ? (
          <p className="text-sm text-destructive">{data.services_error}</p>
        ) : data.services.length === 0 ? (
          <p className="text-sm text-ink-muted">אין שירותים.</p>
        ) : (
          /* mockup 02: services as a table — שירות · סטטוס · זמן-ריצה · זיכרון · controls */
          <div className="overflow-x-auto">
            <table className="w-full text-sm border-collapse">
              <thead>
                <tr className="border-b border-rule-soft text-ink-muted">
                  <th className="text-start font-medium text-xs py-2 pe-3">שירות</th>
                  <th className="text-start font-medium text-xs py-2 px-3">סטטוס</th>
                  <th className="text-start font-medium text-xs py-2 px-3 whitespace-nowrap">
                    זמן-ריצה
                  </th>
                  <th className="text-start font-medium text-xs py-2 px-3 whitespace-nowrap">
                    זיכרון / ↻
                  </th>
                  <th className="py-2 ps-3" />
                </tr>
              </thead>
              <tbody>
                {data.services.map((s: OpsService) => {
                  const isCron = !!s.cron;
                  return (
                    <tr
                      key={s.name}
                      className="border-b border-rule-soft last:border-0 align-top"
                    >
                      <td className="py-2.5 pe-3">
                        <div className="text-navy font-semibold text-[0.82rem]">
                          {SERVICE_LABELS[s.name] ?? s.name}
                        </div>
                        <div className="text-[0.66rem] text-ink-muted font-mono" dir="ltr">
                          {s.name}
                        </div>
                      </td>
                      <td className="py-2.5 px-3">
                        <div className="flex items-center gap-2 flex-wrap">
                          {isCron ? (
                            <Badge
                              variant={s.disabled ? "destructive" : "default"}
                              className="font-normal"
                            >
                              {s.disabled ? "כבוי" : "פעיל (מתוזמן)"}
                            </Badge>
                          ) : (
                            <StatusBadge value={s.status} />
                          )}
                          {s.cron ? (
                            <span
                              className="text-[0.66rem] text-ink-muted font-mono"
                              dir="ltr"
                            >
                              {s.cron}
                            </span>
                          ) : null}
                        </div>
                      </td>
                      <td className="py-2.5 px-3 text-[0.78rem] text-ink-soft whitespace-nowrap tabular-nums">
                        {isCron ? `אחרונה ${ago(s.uptime_ms)}` : ago(s.uptime_ms)}
                      </td>
                      <td className="py-2.5 px-3 text-[0.78rem] text-ink-soft whitespace-nowrap tabular-nums">
                        {mb(s.memory_bytes)} · ↻{s.restarts}
                      </td>
                      <td className="py-2.5 ps-3">
                        <div className="flex justify-end">
                          <ServiceControls
                            s={s}
                            busy={busy}
                            onAction={(a) => action.mutate({ name: s.name, action: a })}
                            onToggle={(disabled) => toggle.mutate({ name: s.name, disabled })}
                          />
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </CardContent>
    </Card>
  );
}

// ── Uniform queue stats ────────────────────────────────────────────────────
function StatTile({
  label,
  value,
  tone,
  title,
}: {
  label: string;
  value: number;
  tone: "navy" | "muted" | "amber" | "green" | "red";
  title?: string;
}) {
  const toneClass = {
    navy: "text-navy",
    muted: "text-ink-muted",
    amber: "text-gold-deep",
    green: "text-emerald-600",
    red: "text-destructive",
  }[tone];
  return (
    <div
      title={title}
      className="flex flex-col items-center rounded-md border border-rule-soft px-2.5 py-1.5 min-w-[68px]"
    >
      <span className={`text-base font-semibold leading-none ${toneClass}`}>{value}</span>
      <span className="text-[0.66rem] text-ink-muted mt-1 text-center">{label}</span>
    </div>
  );
}

function UniformStats({ p }: { p: PipelineStats }) {
  return (
    <div className="space-y-2">
      <div className="flex flex-wrap gap-2">
        <StatTile
          label="בתור"
          value={p.queued}
          tone="navy"
          title="ממתינים שנדרשו במפורש לעיבוד — אלה שה-drain הבא יטפל בהם"
        />
        <StatTile
          label="ממתין"
          value={p.pending}
          tone="muted"
          title="פריטים שטרם עובדו וממתינים לעיבוד — לאו דווקא בתור הפעיל"
        />
        <StatTile label="בעיבוד" value={p.processing} tone="amber" />
        <StatTile label="הושלם" value={p.done} tone="green" />
        {p.failed > 0 ? <StatTile label="נכשל" value={p.failed} tone="red" /> : null}
      </div>
      {p.running_now.length > 0 ? (
        <div className="text-[0.74rem] text-navy">
          <span className="text-ink-muted">רץ עכשיו: </span>
          {p.running_now.join(" · ")}
        </div>
      ) : (
        <div className="text-[0.72rem] text-ink-muted">אין פריט בעיבוד כרגע</div>
      )}
    </div>
  );
}

function StatusRow({ by }: { by: Record<string, number> }) {
  const entries = Object.entries(by).filter(([, n]) => n > 0);
  if (entries.length === 0) return <span className="text-ink-muted text-sm">ריק</span>;
  return (
    <div className="flex flex-wrap gap-1.5">
      {entries
        .sort((a, b) => b[1] - a[1])
        .map(([k, n]) => (
          <StatusBadge key={k} value={k} count={n} />
        ))}
    </div>
  );
}

function PipelineCard({
  title,
  desc,
  children,
  href,
  hrefLabel,
  gate = false,
}: {
  title: string;
  desc: string;
  children: React.ReactNode;
  // INV-IA1: a human-gate card is a *pointer* — the action lives at `href`
  // (/approvals), never duplicated here. /operations only monitors.
  href?: string;
  hrefLabel?: string;
  // mockup 02: gate cards get a gold-wash treatment + gold border so the
  // human-gates read as distinct from the automatic pipelines.
  gate?: boolean;
}) {
  return (
    <Card
      className={
        gate
          ? "bg-gold-wash border-gold/40 shadow-sm"
          : "bg-surface border-rule shadow-sm"
      }
    >
      <CardContent className="px-5 py-4 space-y-2.5">
        <div className="flex items-start justify-between gap-2">
          <div className="min-w-0">
            <h3 className="text-navy text-sm font-semibold mb-0.5">{title}</h3>
            <p className="text-ink-muted text-[0.72rem]">{desc}</p>
          </div>
          {href && (
            <Link
              href={href}
              className="shrink-0 text-[0.72rem] text-gold-deep font-semibold hover:underline whitespace-nowrap"
            >
              {hrefLabel ?? "לטיפול ←"}
            </Link>
          )}
        </div>
        {children}
      </CardContent>
    </Card>
  );
}

// ── Live agents — who's working now + output + controls ────────────────────

// The platform's own liveness signal → a Hebrew label + tone.
const SILENCE_HE: Record<string, { label: string; tone: string }> = {
  ok: { label: "פעיל", tone: "text-emerald-600" },
  suspicion: { label: "שקט חשוד", tone: "text-gold-deep" },
  critical: { label: "תקוע?", tone: "text-destructive" },
};

/** Best-effort: turn the captured NDJSON stream into readable lines (tail). */
function parseRunLog(content: string, maxLines = 400): string {
  if (!content) return "";
  const out: string[] = [];
  for (const raw of content.split("\n")) {
    const line = raw.trim();
    if (!line) continue;
    let chunk = line;
    try {
      const wrap = JSON.parse(line);
      chunk = typeof wrap.chunk === "string" ? wrap.chunk : line;
    } catch {
      // not a wrapper line — keep raw
    }
    // The chunk is often a claude stream-json event; extract the human bits.
    for (const part of chunk.split("\n")) {
      const p = part.trim();
      if (!p) continue;
      try {
        const ev = JSON.parse(p);
        if (ev?.type === "assistant" && ev?.message?.content) {
          const txt = (ev.message.content as Array<{ type: string; text?: string }>)
            .filter((c) => c.type === "text" && c.text)
            .map((c) => c.text)
            .join("");
          if (txt) out.push(txt);
        } else if (ev?.type === "result" && typeof ev.result === "string") {
          out.push(`▸ ${ev.result}`);
        } else if (ev?.type === "system" && ev?.subtype) {
          out.push(`· [${ev.subtype}]`);
        } else {
          out.push(p);
        }
      } catch {
        out.push(p);
      }
    }
  }
  return out.slice(-maxLines).join("\n");
}

function RunLogDialog({ run, onClose }: { run: AgentRun | null; onClose: () => void }) {
  const { data, isLoading, error } = useRunLog(run?.run_id ?? null);
  const text = data ? parseRunLog(data.content) : "";
  return (
    <Dialog open={!!run} onOpenChange={(o) => !o && onClose()}>
      <DialogContent className="max-w-3xl">
        <DialogHeader>
          <DialogTitle>פלט הסוכן — {run?.agent_name}</DialogTitle>
          <DialogDescription>
            {run?.company_label} · ריצה <span dir="ltr" className="font-mono">{run?.run_id?.slice(0, 8)}</span> · מתעדכן חי
          </DialogDescription>
        </DialogHeader>
        {isLoading ? (
          <Skeleton className="h-72 w-full" />
        ) : error ? (
          <p className="text-sm text-destructive">שגיאה בטעינת הלוג: {String(error)}</p>
        ) : (
          <div className="h-[60vh] w-full overflow-y-auto overflow-x-hidden rounded-md border border-rule-soft bg-rule-soft/20 p-3">
            <pre dir="ltr" className="w-full min-w-0 max-w-full overflow-x-hidden text-[0.72rem] leading-relaxed whitespace-pre-wrap break-all text-navy text-start">
              {text || "אין פלט עדיין."}
            </pre>
          </div>
        )}
      </DialogContent>
    </Dialog>
  );
}

function LiveAgentsPanel() {
  const { data, isLoading } = useAgentRuns();
  const cancel = useCancelRun();
  const reset = useResetAgentSession();
  const [logRun, setLogRun] = useState<AgentRun | null>(null);
  const busy = cancel.isPending || reset.isPending;

  return (
    <Card className="bg-surface border-rule shadow-sm">
      <CardContent className="px-6 py-5">
        {/* mockup 02: status pills row (running / queued) + company hint */}
        {data ? (
          <div className="flex items-center gap-2 text-[0.72rem] mb-3 flex-wrap">
            {/* ADM-6 (INV-IA5): counts sum only the companies that loaded.
                When a company errored, mark the totals as a floor ("+") so
                the operator isn't shown a shrunken depth as if complete. */}
            <Badge variant="default" className="font-normal">
              רצים {data.running}{data.errors.length > 0 ? "+" : ""}
            </Badge>
            <Badge variant="secondary" className="font-normal">
              בתור {data.queued}{data.errors.length > 0 ? "+" : ""}
            </Badge>
            {data.errors.length > 0 ? (
              <span
                className="text-warn"
                title={`ספירה חלקית — חברות שלא נטענו: ${data.errors.join(" · ")}`}
              >
                ⚠ חלקי
              </span>
            ) : null}
            <span className="text-ink-muted ms-auto">חברות: CMP · CMPA</span>
          </div>
        ) : null}
        <p className="text-ink-muted text-xs mb-4">
          מי מבין סוכני-הוועדה עובד כרגע ומה הפלט שלו — כולל עבודה שלא קשורה לתיק (כמו
          ריקון תור הלכות ע״י ה-CEO). עצירה היא מבוקרת דרך הפלטפורמה (לא kill).
        </p>
        {isLoading || !data ? (
          <Skeleton className="h-24 w-full" />
        ) : data.runs.length === 0 ? (
          <p className="text-sm text-ink-muted">אין סוכן פעיל כרגע.</p>
        ) : (
          <div className="grid gap-2">
            {data.errors.length > 0 ? (
              <p className="text-[0.72rem] text-destructive">
                לא ניתן לטעון חלק מהחברות: {data.errors.join(" · ")}
              </p>
            ) : null}
            {data.runs.map((r) => {
              const sil = SILENCE_HE[r.silence_level];
              const startMs = r.started_at ? Date.parse(r.started_at) : 0;
              return (
                <div
                  key={r.run_id}
                  className="flex items-center justify-between gap-3 rounded-md border border-rule-soft bg-rule-soft/30 px-3 py-2"
                >
                  <div className="min-w-0">
                    <div className="flex items-center gap-2 flex-wrap">
                      <Badge
                        variant={r.status === "running" ? "default" : "secondary"}
                        className="font-normal"
                      >
                        {r.status === "running" ? "רץ" : "בתור"}
                      </Badge>
                      <span className="text-[0.85rem] text-navy font-semibold">{r.agent_name}</span>
                      {sil ? <span className={`text-[0.68rem] ${sil.tone}`}>● {sil.label}</span> : null}
                    </div>
                    <div className="text-[0.66rem] text-ink-muted flex items-center gap-2 flex-wrap mt-0.5">
                      <span>{r.company_label}</span>
                      {r.status === "running" && startMs ? <span>החל {ago(startMs)}</span> : null}
                      {r.invocation_source ? (
                        <span dir="ltr" className="font-mono">{r.invocation_source}</span>
                      ) : null}
                      {r.continuation_attempt > 0 ? <span>ניסיון #{r.continuation_attempt + 1}</span> : null}
                    </div>
                  </div>
                  <div className="flex items-center gap-1.5 shrink-0">
                    <Button size="xs" variant="outline" onClick={() => setLogRun(r)}>
                      פלט
                    </Button>
                    <Button
                      size="xs"
                      variant="ghost"
                      className="text-destructive"
                      disabled={busy || r.status !== "running"}
                      onClick={() => {
                        if (confirm(`לעצור את הריצה של "${r.agent_name}"? (עצירה מבוקרת)`)) {
                          cancel.mutate(r.run_id);
                        }
                      }}
                    >
                      עצור
                    </Button>
                    <Button
                      size="xs"
                      variant="ghost"
                      disabled={busy}
                      title="איפוס session — ה-wakeup הבא יתחיל נקי"
                      onClick={() => {
                        if (confirm(`לאפס את ה-session של "${r.agent_name}"?`)) {
                          reset.mutate(r.agent_id);
                        }
                      }}
                    >
                      אפס
                    </Button>
                  </div>
                </div>
              );
            })}
          </div>
        )}
      </CardContent>
      <RunLogDialog run={logRun} onClose={() => setLogRun(null)} />
    </Card>
  );
}

// ── Agent health taxonomy (#222) + recent escalations (#218) ───────────────
const HEALTH_LABEL: Record<AgentHealthState, string> = {
  zombie: "זומבי",
  stalled: "תקוע",
  working: "עובד",
  idle: "רגוע",
};

function AgentHealthPanel() {
  const { data, isLoading } = useAgentHealth();
  const { data: esc } = useRecentEscalations();

  const counts = data?.counts;
  // Only zombies/stalled are "problems" — the backend already sorts worst-first.
  // An escalated issue is chair-owned, so it leaves this list; the escalations
  // card below is the handled-loop history.
  const problems = (data?.items ?? []).filter(
    (i) => i.health === "zombie" || i.health === "stalled",
  );
  const healthyWorking = counts?.working ?? 0;
  const healthyIdle = counts?.idle ?? 0;

  return (
    <div className="space-y-4">
      <Card className="bg-surface border-rule shadow-sm">
        <CardContent className="px-6 py-5">
          {isLoading || !data ? (
            <Skeleton className="h-24 w-full" />
          ) : (
            <>
              <div className="flex flex-wrap gap-2 mb-4">
                <StatTile
                  label="זומבי"
                  value={counts?.zombie ?? 0}
                  tone="red"
                  title="לולאת-recovery — משויך+פעיל-כביכול אך אין ריצה חיה. מוסלם אוטומטית."
                />
                <StatTile
                  label="תקוע"
                  value={counts?.stalled ?? 0}
                  tone="amber"
                  title="ננער חוזר בלי התקדמות"
                />
                <StatTile label="עובד" value={counts?.working ?? 0} tone="green" />
                <StatTile label="רגוע" value={counts?.idle ?? 0} tone="muted" />
              </div>

              {problems.length === 0 ? (
                <p className="text-sm text-emerald-600">
                  אין סוכן תקוע כרגע — כל הסוכנים תקינים.
                </p>
              ) : (
                <div className="grid gap-2">
                  {problems.map((p) => (
                    <div
                      key={p.issue_id}
                      className={
                        p.health === "zombie"
                          ? "flex items-center justify-between gap-3 rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2"
                          : "flex items-center justify-between gap-3 rounded-md border border-gold/40 bg-gold-wash px-3 py-2"
                      }
                    >
                      <div className="flex items-center gap-2 flex-wrap min-w-0">
                        <Badge
                          variant={p.health === "zombie" ? "destructive" : "outline"}
                          className={
                            p.health === "stalled"
                              ? "border-warn/50 text-warn font-normal"
                              : "font-normal"
                          }
                        >
                          {HEALTH_LABEL[p.health]}
                        </Badge>
                        <span dir="ltr" className="font-mono text-[0.82rem] text-navy font-semibold">
                          {p.identifier}
                        </span>
                        <span className="text-[0.82rem] text-navy font-semibold">
                          {p.agent_name}
                        </span>
                        <span className="text-[0.7rem] text-ink-muted">
                          {p.recovery_wakeups > 0
                            ? `${p.recovery_wakeups} יקיצות-שחזור`
                            : `${p.wakeups} יקיצות`}
                        </span>
                      </div>
                      <span className="text-[0.7rem] text-ink-muted shrink-0">
                        {p.health === "zombie" ? "יוסלם אוטומטית" : "מנוטר"}
                      </span>
                    </div>
                  ))}
                </div>
              )}

              <p className="mt-3 text-[0.78rem] text-ink-muted">
                <b className="text-navy">{healthyWorking + healthyIdle} סוכנים תקינים</b> —{" "}
                {healthyWorking} עובדים, {healthyIdle} רגועים. מוצגות רק בעיות.
              </p>
            </>
          )}
        </CardContent>
      </Card>

      {/* handled-loop history — the watchdog's escalations, gold-wash like a gate */}
      <Card className="bg-gold-wash border-gold/40 shadow-sm">
        <CardContent className="px-5 py-4">
          <h3 className="text-navy text-sm font-semibold mb-0.5">הסלמות אוטומטיות אחרונות</h3>
          <p className="text-ink-muted text-[0.72rem] mb-3">
            כל זומבי מתמשך (לולאת-recovery ≥2 יקיצות-שחזור) משויך אליך אוטומטית עם הערת-מערכת
            — בלי להעיר סוכן מחדש.
          </p>
          {!esc?.items?.length ? (
            <p className="text-sm text-ink-muted">אין הסלמות אחרונות.</p>
          ) : (
            <div className="grid gap-1">
              {esc.items.map((e) => (
                <div
                  key={`${e.issue_id}-${e.created_at ?? ""}`}
                  className="flex items-start gap-2.5 border-t border-gold/15 first:border-0 pt-2 first:pt-0"
                >
                  <Badge
                    variant={e.severity === "medium" ? "outline" : "destructive"}
                    className={
                      e.severity === "medium"
                        ? "border-gold/50 text-gold-deep font-normal shrink-0"
                        : "font-normal shrink-0"
                    }
                  >
                    {e.severity}
                  </Badge>
                  <div className="min-w-0 flex-1">
                    <span dir="ltr" className="font-mono text-[0.8rem] text-navy font-semibold">
                      {e.identifier}
                    </span>
                    <div className="text-[0.78rem] text-ink-soft">{e.reason}</div>
                  </div>
                  <span className="text-[0.68rem] text-ink-muted shrink-0">
                    {e.created_at ? ago(Date.parse(e.created_at)) : ""}
                  </span>
                </div>
              ))}
            </div>
          )}
        </CardContent>
      </Card>
    </div>
  );
}

export default function OperationsPage() {
  const { data, isLoading, error } = useOperations();

  return (
    <AppShell>
      <section className="space-y-6">
        <header>
          <nav className="text-[0.78rem] text-ink-muted mb-1">
            <Link href="/" className="hover:text-gold-deep">בית</Link>
            <span aria-hidden> · </span>
            <span className="text-navy">תפעול</span>
          </nav>
          <h1 className="text-navy mb-0">תפעול — מה רץ ברקע</h1>
          <p className="text-ink-muted text-sm mt-1 max-w-3xl">
            כל מה שמוריד ומנתח אוטומטית: שירותי-המארח, משימות-התזמון, ותורי-העבודה.
            ניתן להפעיל-מחדש / לעצור / להריץ-עכשיו כל תהליך. מתרענן כל 5 שניות.
          </p>
        </header>

        {error ? (
          <Card className="bg-surface border-rule">
            <CardContent className="px-6 py-5 text-sm text-destructive">
              שגיאה בטעינת מצב התפעול: {String(error)}
            </CardContent>
          </Card>
        ) : isLoading || !data ? (
          <div className="space-y-4">
            <Skeleton className="h-40 w-full" />
            <Skeleton className="h-40 w-full" />
          </div>
        ) : (
          <>
            <SectionHeader>מכסת claude.ai</SectionHeader>
            <SubscriptionUsagePanel usage={data.subscription_usage} />

            <SectionHeader>סוכנים פעילים</SectionHeader>
            <LiveAgentsPanel />

            <SectionHeader>בריאות-הסוכנים</SectionHeader>
            <AgentHealthPanel />

            <SectionHeader>מתאמי-סוכנים</SectionHeader>
            <AgentAdaptersPanel />

            <SectionHeader>שירותים</SectionHeader>
            <ServicesPanel data={data} />

            <SectionHeader>צינורות-עבודה</SectionHeader>
            {/* Automatic pipelines — uniform stat cards */}
            <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
              <PipelineCard
                title="אחזור פסיקה (X13)"
                desc="הורדה אוטומטית מנט-המשפט / פורטל-העליון → קורפוס"
              >
                <UniformStats p={data.pipelines.court_fetch} />
              </PipelineCard>

              <PipelineCard
                title="חילוץ מטא-דאטה"
                desc="Gemini Flash — שם/תקציר/תגיות לכל פסיקה"
              >
                <UniformStats p={data.pipelines.metadata_extraction} />
              </PipelineCard>

              <PipelineCard
                title="חילוץ הלכות"
                desc="Claude — הלכות מכל פסיקה (→ אישור יו״ר)"
              >
                <UniformStats p={data.pipelines.halacha_extraction} />
              </PipelineCard>

              <PipelineCard
                title="העשרת יומונים (רדאר)"
                desc="Sonnet — תקציר/תגיות/קישור-לפסיקה לכל יומון"
              >
                <UniformStats p={data.pipelines.digests} />
                <p className="text-[0.72rem] text-ink-muted">
                  {data.pipelines.digests.linked}/{data.pipelines.digests.total} מקושרים
                  לפסיקה
                </p>
              </PipelineCard>
            </div>

            {/* mockup 02: human-gate pointer cards — gold-wash, "לתיבת-האישורים ←" */}
            <div className="grid gap-4 md:grid-cols-2 mt-4">
              <PipelineCard
                gate
                title="אישור הלכות (שער יו״ר)"
                desc="שער-אנושי, לא תהליך — הפעולה ב-/approvals"
                href="/approvals"
                hrefLabel="לתיבת-האישורים ←"
              >
                <StatusRow by={data.pipelines.halacha_review.by_status} />
              </PipelineCard>

              <PipelineCard
                gate
                title="פסיקה חסרה"
                desc="פערים בקורפוס — נסגרים אוטומטית כשנקלטים"
                href="/approvals"
                hrefLabel="לתיבת-האישורים ←"
              >
                <StatusRow by={data.pipelines.missing_precedents.by_status} />
              </PipelineCard>
            </div>

            <SectionHeader>אחזורים אחרונים</SectionHeader>
            <div className="grid gap-4 lg:grid-cols-2">
              <Card className="bg-surface border-rule shadow-sm">
                <CardContent className="px-5 py-4">
                  <h3 className="text-navy text-sm font-semibold mb-2">
                    אחזורים אחרונים (תור)
                  </h3>
                  <div className="space-y-1.5">
                    {data.pipelines.court_fetch.recent.map((j) => (
                      <div
                        key={j.case_number_norm}
                        className="flex items-start justify-between gap-2 text-[0.78rem] border-b border-rule-soft pb-1.5 last:border-0"
                      >
                        <div className="min-w-0">
                          <StatusBadge value={j.status} />
                          <span className="text-navy ms-2">
                            {j.citation_raw?.slice(0, 48) || j.case_number_norm}
                          </span>
                          {j.error ? (
                            <div className="text-ink-muted text-[0.68rem] truncate">
                              {j.error.slice(0, 80)}
                            </div>
                          ) : null}
                        </div>
                        <span className="text-ink-muted text-[0.66rem] shrink-0" dir="ltr">
                          {(j.tier || "").slice(0, 7)}
                        </span>
                      </div>
                    ))}
                  </div>
                </CardContent>
              </Card>

              <Card className="bg-surface border-rule shadow-sm">
                <CardContent className="px-5 py-4">
                  <h3 className="text-navy text-sm font-semibold mb-2">
                    נקלטו לאחרונה (מבתי-משפט)
                  </h3>
                  <div className="space-y-1.5">
                    {data.pipelines.ingested_recent.map((r) => (
                      <div
                        key={r.case_number}
                        className="text-[0.78rem] text-navy border-b border-rule-soft pb-1.5 last:border-0 truncate"
                      >
                        {r.case_number}
                        <span className="text-ink-muted text-[0.66rem] ms-2" dir="ltr">
                          {r.created_at?.slice(0, 16).replace("T", " ")}
                        </span>
                      </div>
                    ))}
                    {data.pipelines.ingested_recent.length === 0 ? (
                      <p className="text-ink-muted text-sm">עדיין אין.</p>
                    ) : null}
                  </div>
                </CardContent>
              </Card>
            </div>

            {/* INV-IA4: the former /diagnostics surface, folded in here — one
                monitoring intent, one surface. /diagnostics now redirects here. */}
            <div className="pt-2">
              <SectionHeader>בריאות-מערכת</SectionHeader>
              <p className="text-ink-muted text-sm mb-3">
                מצב ה-DB, תורי-אישור, ומסמכים שנכשלו/תקועים. (אוחד מ״אבחון״.)
              </p>
              <SystemHealthSection />
            </div>
          </>
        )}
      </section>
    </AppShell>
  );
}
