"use client";

import { useState, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Badge } from "@/components/ui/badge";
import { Markdown } from "@/components/ui/markdown";
import {
  useAgentActivity,
  useSendComment,
  useSubmitInteraction,
  useDismissInteraction,
  useSetIssueStatus,
} from "@/lib/api/agents";
import type {
  Interaction,
  InteractionPayload,
  InteractionQuestion,
  InteractionTask,
  PaperclipComment,
  PaperclipAgentStatus,
  PaperclipIssue,
} from "@/lib/api/agents";
import { formatRelative } from "@/lib/format-date";
import { toast } from "sonner";
import {
  Bot,
  User,
  Send,
  Loader2,
  MessageSquare,
  Clock,
  CheckCircle2,
  XCircle,
  HelpCircle,
  ChevronDown,
  Ban,
  Target,
  Plus,
  AlertTriangle,
} from "lucide-react";

/* ── Role → color mapping ────────────────────────────────────── */

const ROLE_COLORS: Record<string, string> = {
  ceo: "bg-blue-100 text-blue-800 border-blue-200",
  researcher: "bg-purple-100 text-purple-800 border-purple-200",
  engineer: "bg-emerald-100 text-emerald-800 border-emerald-200",
  qa: "bg-amber-100 text-amber-800 border-amber-200",
};

const ROLE_DOT: Record<string, string> = {
  ceo: "bg-blue-500",
  researcher: "bg-purple-500",
  engineer: "bg-emerald-500",
  qa: "bg-amber-500",
};

const ROLE_LABELS: Record<string, string> = {
  ceo: "מנהל",
  researcher: "חוקר",
  engineer: "מהנדס",
  qa: "בודק איכות",
  general: "כללי",
};

const ISSUE_STATUS_LABELS: Record<string, string> = {
  backlog: "ממתין",
  todo: "לביצוע",
  in_progress: "בביצוע",
  in_review: "בבדיקה",
  done: "הושלם",
  cancelled: "בוטל",
  blocked: "חסום",
};

function roleColor(role: string | null) {
  return ROLE_COLORS[role ?? ""] ?? "bg-gray-100 text-gray-700 border-gray-200";
}

function roleDot(role: string | null) {
  return ROLE_DOT[role ?? ""] ?? "bg-gray-400";
}

function roleLabel(role: string | null) {
  return ROLE_LABELS[role ?? ""] ?? role ?? "";
}

function issueStatusLabel(status: string) {
  return ISSUE_STATUS_LABELS[status] ?? status;
}

/* ── Agent roster (mockup 18i) — who's doing what right now ────── */

const AGENT_STATUS_LABEL: Record<string, string> = {
  active: "פעיל",
  running: "פעיל",
  idle: "ממתין",
  error: "שגיאה",
};

function agentIsActive(s: string) {
  return s === "active" || s === "running";
}

function agentStatusDot(s: string) {
  return agentIsActive(s)
    ? "bg-success shadow-[0_0_6px_rgba(74,124,89,0.6)]"
    : s === "error"
      ? "bg-danger"
      : "bg-rule";
}

function agentStatusTone(s: string) {
  return agentIsActive(s)
    ? "bg-success-bg text-success"
    : s === "error"
      ? "bg-danger-bg text-danger"
      : "bg-rule-soft text-ink-muted";
}

function AgentRoster({ agents }: { agents: PaperclipAgentStatus[] }) {
  if (!agents.length) return null;

  const activeCount = agents.filter((a) => agentIsActive(a.status)).length;

  return (
    <div className="mb-3 rounded-lg border border-rule bg-parchment/40 p-3">
      <div className="flex items-center gap-2 mb-2.5">
        <Bot className="w-4 h-4 text-gold-deep shrink-0" />
        <h3 className="text-[0.82rem] font-bold text-navy">סוכני התיק</h3>
        <span className="text-[0.72rem] text-ink-muted">
          <b className="text-success font-bold">{activeCount} פעילים</b> מתוך {agents.length}
        </span>
      </div>
      {/* Compact roster — all agents on one row (mockup 18i v2): status · name ·
          role only. The per-agent "current activity" line was removed: it
          matched comments by role-fallback, so agents sharing a role showed the
          same text. */}
      <div className="grid gap-1.5 grid-cols-3 sm:grid-cols-5 lg:grid-cols-9">
        {agents.map((a) => {
          const active = agentIsActive(a.status);
          return (
            <div
              key={a.id}
              className={`rounded-lg border p-2 min-w-0 ${active ? "border-success/40 bg-success-bg/40" : "border-rule bg-surface"}`}
            >
              <div className="flex items-center gap-1.5 min-w-0">
                <span className={`w-1.5 h-1.5 rounded-full shrink-0 ${agentStatusDot(a.status)}`} />
                <span className="text-[0.72rem] font-bold text-navy truncate">{a.name}</span>
              </div>
              <div className="text-[0.6rem] text-ink-muted mt-0.5 truncate">{roleLabel(a.role)}</div>
              <span
                className={`inline-block mt-1 text-[0.55rem] font-bold rounded-full px-1.5 py-0.5 whitespace-nowrap ${agentStatusTone(a.status)}`}
              >
                {AGENT_STATUS_LABEL[a.status] ?? a.status}
              </span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ── Time formatting ─────────────────────────────────────────── */

function timeAgo(iso: string | null): string {
  return formatRelative(iso, { units: "short", fallback: "" });
}

/* ── Issue identifier → find matching identifier ─────────────── */

function issueIdentifier(
  comment: PaperclipComment,
  issueMap: Map<string, string>,
): string {
  return issueMap.get(comment.issue_id) ?? "";
}

/* ── Comment card ────────────────────────────────────────────── */

function CommentCard({
  comment,
  issueMap,
}: {
  comment: PaperclipComment;
  issueMap: Map<string, string>;
}) {
  const isAgent = !!comment.author_agent_id;
  const label = isAgent ? comment.agent_name ?? "סוכן" : "חיים";
  const identifier = issueIdentifier(comment, issueMap);

  return (
    <div className="group relative flex gap-3 py-3 px-2 rounded-lg hover:bg-sand-soft/50 transition-colors">
      {/* Avatar */}
      <div className="flex-shrink-0 pt-0.5">
        {isAgent ? (
          <div
            className={`w-8 h-8 rounded-full flex items-center justify-center ${roleColor(comment.agent_role)}`}
          >
            <Bot className="w-4 h-4" />
          </div>
        ) : (
          <div className="w-8 h-8 rounded-full flex items-center justify-center bg-gold-soft text-gold-deep border border-gold">
            <User className="w-4 h-4" />
          </div>
        )}
      </div>

      {/* Content */}
      <div className="flex-1 min-w-0">
        {/* Header */}
        <div className="flex items-center gap-2 mb-1 flex-wrap">
          <span className="text-sm font-semibold text-navy">{label}</span>
          {isAgent && comment.agent_role && (
            <span
              className={`inline-flex items-center gap-1 text-[11px] px-1.5 py-0.5 rounded-full border ${roleColor(comment.agent_role)}`}
            >
              <span className={`w-1.5 h-1.5 rounded-full ${roleDot(comment.agent_role)}`} />
              {roleLabel(comment.agent_role)}
            </span>
          )}
          {identifier && (
            <Badge variant="outline" className="text-[10px] font-mono">
              {identifier}
            </Badge>
          )}
          <span className="text-[11px] text-ink-faint me-auto flex items-center gap-1">
            <Clock className="w-3 h-3" />
            {timeAgo(comment.created_at)}
          </span>
        </div>

        {/* Body */}
        <div className="text-sm">
          <Markdown content={comment.body} />
        </div>
      </div>
    </div>
  );
}

/* ── Interaction card ────────────────────────────────────────── */

const RESOLVED_LABELS: Record<string, { text: string; tone: string; Icon: typeof CheckCircle2 }> = {
  answered: { text: "נענה", tone: "text-emerald-700 bg-emerald-50 border-emerald-200", Icon: CheckCircle2 },
  accepted: { text: "התקבל", tone: "text-emerald-700 bg-emerald-50 border-emerald-200", Icon: CheckCircle2 },
  rejected: { text: "נדחה", tone: "text-rose-700 bg-rose-50 border-rose-200", Icon: XCircle },
  expired: { text: "פג תוקף", tone: "text-ink-faint bg-gray-50 border-gray-200", Icon: XCircle },
  failed: { text: "כשל", tone: "text-rose-700 bg-rose-50 border-rose-200", Icon: XCircle },
};

function ResolvedBadge({ status }: { status: string }) {
  const meta = RESOLVED_LABELS[status];
  if (!meta) return null;
  const { text, tone, Icon } = meta;
  return (
    <span className={`inline-flex items-center gap-1 text-[11px] px-2 py-0.5 rounded-full border ${tone}`}>
      <Icon className="w-3 h-3" />
      {text}
    </span>
  );
}

function summaryAnswer(interaction: Interaction): string | null {
  const result = interaction.result;
  if (!result) return null;
  if (typeof result.summaryMarkdown === "string" && result.summaryMarkdown.trim()) {
    return result.summaryMarkdown;
  }
  if (interaction.kind === "ask_user_questions" && Array.isArray(result.answers)) {
    const optionLabel = (qid: string, oid: string): string => {
      const q = interaction.payload.questions?.find((qq) => qq.id === qid);
      return q?.options.find((o) => o.id === oid)?.label ?? oid;
    };
    return (result.answers as Array<{ questionId: string; optionIds: string[] }>)
      .map((a) =>
        `**${interaction.payload.questions?.find((q) => q.id === a.questionId)?.prompt ?? a.questionId}** — ${a.optionIds
          .map((oid) => optionLabel(a.questionId, oid))
          .join(", ")}`,
      )
      .join("\n\n");
  }
  if (interaction.kind === "request_confirmation" && typeof result.reason === "string" && result.reason) {
    return `נימוק: ${result.reason}`;
  }
  if (interaction.kind === "suggest_tasks") {
    const created = Array.isArray(result.createdTasks) ? result.createdTasks.length : 0;
    const skipped = Array.isArray(result.skippedClientKeys) ? result.skippedClientKeys.length : 0;
    if (created || skipped) {
      const parts: string[] = [];
      if (created) parts.push(`נוצרו ${created} משימות`);
      if (skipped) parts.push(`דילוג על ${skipped}`);
      return parts.join(" · ");
    }
  }
  return null;
}

function AskUserQuestionsForm({
  interaction,
  onSubmit,
  pending,
}: {
  interaction: Interaction;
  onSubmit: (answers: Array<{ questionId: string; optionIds: string[] }>) => void;
  pending: boolean;
}) {
  const questions: InteractionQuestion[] = interaction.payload.questions ?? [];
  const [selections, setSelections] = useState<Record<string, string[]>>({});

  const setSingle = (qid: string, oid: string) =>
    setSelections((prev) => ({ ...prev, [qid]: [oid] }));

  const toggleMulti = (qid: string, oid: string) =>
    setSelections((prev) => {
      const cur = prev[qid] ?? [];
      return {
        ...prev,
        [qid]: cur.includes(oid) ? cur.filter((x) => x !== oid) : [...cur, oid],
      };
    });

  const missingRequired = questions.some(
    (q) => (q.required ?? true) && !(selections[q.id]?.length),
  );

  const handleSend = () => {
    const answers = questions
      .map((q) => ({ questionId: q.id, optionIds: selections[q.id] ?? [] }))
      .filter((a) => a.optionIds.length > 0);
    onSubmit(answers);
  };

  return (
    <div className="space-y-4">
      {questions.map((q) => {
        const isSingle = (q.selectionMode ?? "single") === "single";
        const chosen = selections[q.id] ?? [];
        return (
          <fieldset key={q.id} className="space-y-2">
            <legend className="text-sm font-semibold text-navy mb-1">
              {q.prompt}
              {(q.required ?? true) && <span className="text-rose-600 ms-1">*</span>}
            </legend>
            <div className="space-y-1.5">
              {q.options.map((opt) => {
                const checked = chosen.includes(opt.id);
                return (
                  <label
                    key={opt.id}
                    className={`flex items-start gap-2 cursor-pointer rounded-md border p-2 transition-colors ${
                      checked
                        ? "border-navy bg-navy/5"
                        : "border-rule hover:bg-sand-soft/60"
                    }`}
                  >
                    <input
                      type={isSingle ? "radio" : "checkbox"}
                      name={q.id}
                      value={opt.id}
                      checked={checked}
                      onChange={() =>
                        isSingle ? setSingle(q.id, opt.id) : toggleMulti(q.id, opt.id)
                      }
                      className="mt-1 accent-navy"
                      disabled={pending}
                    />
                    <span className="flex-1 text-sm">
                      <span className="font-medium text-navy">{opt.label}</span>
                      {opt.description && (
                        <span className="block text-xs text-ink-faint mt-0.5">
                          {opt.description}
                        </span>
                      )}
                    </span>
                  </label>
                );
              })}
            </div>
          </fieldset>
        );
      })}
      <div className="flex justify-end">
        <Button
          size="sm"
          onClick={handleSend}
          disabled={pending || missingRequired}
        >
          {pending ? (
            <Loader2 className="w-4 h-4 animate-spin" />
          ) : (
            <Send className="w-4 h-4 me-1" />
          )}
          {interaction.payload.submitLabel || "שלח תשובה"}
        </Button>
      </div>
    </div>
  );
}

function RequestConfirmationForm({
  interaction,
  onAccept,
  onReject,
  pending,
}: {
  interaction: Interaction;
  onAccept: () => void;
  onReject: (reason: string) => void;
  pending: boolean;
}) {
  const payload = interaction.payload;
  const allowReason = payload.allowDeclineReason !== false;
  const requireReason = payload.rejectRequiresReason === true;
  const [showReason, setShowReason] = useState(requireReason);
  const [reason, setReason] = useState("");

  const acceptLabel = (payload.acceptLabel as string) || "אישור";
  const rejectLabel = (payload.rejectLabel as string) || "דחייה";
  const reasonLabel =
    (payload.rejectReasonLabel as string) || "נימוק (לא חובה)";
  const reasonPlaceholder =
    (payload.declineReasonPlaceholder as string) || "סיבת הדחייה...";

  const handleReject = () => {
    if (requireReason && !reason.trim()) {
      setShowReason(true);
      return;
    }
    onReject(reason.trim());
  };

  return (
    <div className="space-y-3">
      {typeof payload.prompt === "string" && (
        <div className="text-sm text-navy whitespace-pre-line">{payload.prompt}</div>
      )}
      {typeof payload.detailsMarkdown === "string" && payload.detailsMarkdown && (
        <div className="text-sm bg-sand-soft/40 rounded-md p-2">
          <Markdown content={payload.detailsMarkdown} />
        </div>
      )}
      {showReason && allowReason && (
        <div className="space-y-1">
          <label className="text-xs font-medium text-ink-faint">{reasonLabel}</label>
          <Textarea
            value={reason}
            onChange={(e) => setReason(e.target.value)}
            placeholder={reasonPlaceholder}
            className="min-h-[60px] text-sm"
            dir="rtl"
            disabled={pending}
          />
        </div>
      )}
      <div className="flex flex-wrap gap-2 justify-end">
        {allowReason && !showReason && (
          <Button
            size="sm"
            variant="outline"
            onClick={() => setShowReason(true)}
            disabled={pending}
          >
            הוסף נימוק
          </Button>
        )}
        <Button
          size="sm"
          variant="outline"
          onClick={handleReject}
          disabled={pending || (requireReason && !reason.trim())}
        >
          <XCircle className="w-4 h-4 me-1" />
          {rejectLabel}
        </Button>
        <Button size="sm" onClick={onAccept} disabled={pending}>
          {pending ? (
            <Loader2 className="w-4 h-4 animate-spin" />
          ) : (
            <CheckCircle2 className="w-4 h-4 me-1" />
          )}
          {acceptLabel}
        </Button>
      </div>
    </div>
  );
}

function SuggestTasksForm({
  interaction,
  onAccept,
  onReject,
  pending,
}: {
  interaction: Interaction;
  onAccept: (selectedClientKeys: string[]) => void;
  onReject: (reason: string) => void;
  pending: boolean;
}) {
  const tasks: InteractionTask[] = (interaction.payload.tasks as InteractionTask[]) ?? [];
  const [selected, setSelected] = useState<Set<string>>(
    () => new Set(tasks.map((t) => t.clientKey)),
  );
  const [showReason, setShowReason] = useState(false);
  const [reason, setReason] = useState("");

  const toggle = (key: string) =>
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(key)) next.delete(key);
      else next.add(key);
      return next;
    });

  return (
    <div className="space-y-3">
      <div className="space-y-1.5 max-h-[260px] overflow-y-auto">
        {tasks.map((t) => {
          const checked = selected.has(t.clientKey);
          return (
            <label
              key={t.clientKey}
              className={`flex items-start gap-2 cursor-pointer rounded-md border p-2 ${
                checked
                  ? "border-navy bg-navy/5"
                  : "border-rule hover:bg-sand-soft/60"
              }`}
            >
              <input
                type="checkbox"
                checked={checked}
                onChange={() => toggle(t.clientKey)}
                className="mt-1 accent-navy"
                disabled={pending}
              />
              <span className="flex-1 text-sm">
                <span className="font-medium text-navy">{t.title}</span>
                {t.description && (
                  <span className="block text-xs text-ink-faint mt-0.5 whitespace-pre-line">
                    {t.description}
                  </span>
                )}
              </span>
            </label>
          );
        })}
      </div>
      {showReason && (
        <Textarea
          value={reason}
          onChange={(e) => setReason(e.target.value)}
          placeholder="סיבת הדחייה (לא חובה)..."
          className="min-h-[60px] text-sm"
          dir="rtl"
          disabled={pending}
        />
      )}
      <div className="flex flex-wrap gap-2 justify-end">
        <Button
          size="sm"
          variant="outline"
          onClick={() => (showReason ? onReject(reason.trim()) : setShowReason(true))}
          disabled={pending}
        >
          <XCircle className="w-4 h-4 me-1" />
          {showReason ? "אישור דחייה" : "דחייה"}
        </Button>
        <Button
          size="sm"
          onClick={() => onAccept(Array.from(selected))}
          disabled={pending || selected.size === 0}
        >
          {pending ? (
            <Loader2 className="w-4 h-4 animate-spin" />
          ) : (
            <CheckCircle2 className="w-4 h-4 me-1" />
          )}
          אישור משימות נבחרות ({selected.size})
        </Button>
      </div>
    </div>
  );
}

function InteractionCard({
  interaction,
  caseNumber,
  issueMap,
  defaultOpen = true,
}: {
  interaction: Interaction;
  caseNumber: string;
  issueMap: Map<string, string>;
  defaultOpen?: boolean;
}) {
  const submit = useSubmitInteraction(caseNumber);
  const dismiss = useDismissInteraction(caseNumber);
  const identifier = issueMap.get(interaction.issue_id) ?? "";
  const isPending = interaction.status === "pending";
  const summary = summaryAnswer(interaction);
  // Pending interactions collapse into an accordion (mockup 18i v2); resolved
  // ones always render their one-line summary.
  const [open, setOpen] = useState(defaultOpen);

  const send = (action: "respond" | "accept" | "reject", payload: InteractionPayload | Record<string, unknown>) => {
    submit.mutate(
      {
        issue_id: interaction.issue_id,
        interaction_id: interaction.id,
        action,
        payload: payload as Record<string, unknown>,
      },
      {
        onSuccess: () => toast.success("התשובה נשלחה"),
        onError: () => toast.error("שגיאה בשליחת התשובה"),
      },
    );
  };

  const handleDismiss = () => {
    dismiss.mutate(
      { issue_id: interaction.issue_id, interaction_id: interaction.id },
      {
        onSuccess: () => toast.success("הבקשה בוטלה"),
        onError: () => toast.error("שגיאה בביטול הבקשה"),
      },
    );
  };

  return (
    <div
      className={`group relative flex gap-3 py-3 px-2 rounded-lg border transition-colors ${
        isPending
          ? "border-amber-300 bg-amber-50/40"
          : "border-rule bg-sand-soft/30"
      }`}
    >
      <div className="flex-shrink-0 pt-0.5">
        <div
          className={`w-8 h-8 rounded-full flex items-center justify-center ${
            isPending
              ? "bg-amber-100 text-amber-800 border border-amber-300"
              : "bg-emerald-100 text-emerald-800 border border-emerald-200"
          }`}
        >
          <HelpCircle className="w-4 h-4" />
        </div>
      </div>

      <div className="flex-1 min-w-0">
        <div className="flex items-center gap-2 mb-2 flex-wrap">
          {isPending ? (
            <button
              type="button"
              onClick={() => setOpen((o) => !o)}
              aria-expanded={open}
              className="flex items-center gap-2 flex-wrap text-start min-w-0"
            >
              <ChevronDown
                className={`w-4 h-4 text-ink-faint shrink-0 transition-transform ${open ? "rotate-180" : ""}`}
              />
              <span className="text-sm font-semibold text-navy">
                {interaction.title || "שאלה לסוכן"}
              </span>
              <span className="inline-flex items-center gap-1 text-[11px] px-2 py-0.5 rounded-full border border-amber-300 bg-amber-100 text-amber-800">
                ממתין לתשובה
              </span>
            </button>
          ) : (
            <>
              <span className="text-sm font-semibold text-navy">
                {interaction.title || "שאלה לסוכן"}
              </span>
              <ResolvedBadge status={interaction.status} />
            </>
          )}
          {identifier && (
            <Badge variant="outline" className="text-[10px] font-mono">
              {identifier}
            </Badge>
          )}
          <span className="text-[11px] text-ink-faint me-auto flex items-center gap-1">
            <Clock className="w-3 h-3" />
            {timeAgo(interaction.resolved_at ?? interaction.created_at)}
          </span>
          {isPending && (
            <Button
              size="sm"
              variant="ghost"
              onClick={handleDismiss}
              disabled={dismiss.isPending}
              className="h-7 px-2 text-ink-muted hover:text-danger"
              title="בטל בקשה כפולה/מיושנת — ללא העברה לסוכן"
            >
              {dismiss.isPending ? (
                <Loader2 className="w-3.5 h-3.5 animate-spin" />
              ) : (
                <Ban className="w-3.5 h-3.5 me-1" />
              )}
              התעלם
            </Button>
          )}
        </div>

        {interaction.summary && (!isPending || open) && (
          <div className="text-xs text-ink-faint mb-2">{interaction.summary}</div>
        )}

        {isPending ? (
          open ? (
            interaction.kind === "ask_user_questions" ? (
              <AskUserQuestionsForm
                interaction={interaction}
                onSubmit={(answers) => send("respond", { answers })}
                pending={submit.isPending}
              />
            ) : interaction.kind === "request_confirmation" ? (
              <RequestConfirmationForm
                interaction={interaction}
                onAccept={() => send("accept", {})}
                onReject={(reason) =>
                  send("reject", reason ? { reason } : {})
                }
                pending={submit.isPending}
              />
            ) : interaction.kind === "suggest_tasks" ? (
              <SuggestTasksForm
                interaction={interaction}
                onAccept={(keys) =>
                  send("accept", keys.length ? { selectedClientKeys: keys } : {})
                }
                onReject={(reason) =>
                  send("reject", reason ? { reason } : {})
                }
                pending={submit.isPending}
              />
            ) : null
          ) : null
        ) : summary ? (
          <div className="text-sm">
            <Markdown content={summary} />
          </div>
        ) : null}
      </div>
    </div>
  );
}

/* ── Issue group (timeline grouped by issue, accordion) ──────── */

type FeedItem =
  | { kind: "comment"; at: number; comment: PaperclipComment }
  | { kind: "interaction"; at: number; interaction: Interaction };

/** Drop the "[ערר 1043-02-26] " prefix Paperclip puts on every issue title. */
function shortIssueTitle(title: string): string {
  return title.replace(/^\s*\[[^\]]*\]\s*/, "").trim() || title;
}

function IssueGroup({
  issue,
  items,
  caseNumber,
  issueMap,
  defaultOpen,
}: {
  issue: PaperclipIssue;
  items: FeedItem[];
  caseNumber: string;
  issueMap: Map<string, string>;
  defaultOpen: boolean;
}) {
  const [open, setOpen] = useState(defaultOpen);
  const setStatus = useSetIssueStatus(caseNumber);
  const closed = issue.status === "done" || issue.status === "cancelled";
  // Show the parent run this issue hangs under (e.g. a "הרצה חדשה" nested under
  // the live CEO root) so the hierarchy is visible, not flattened.
  const parentIdentifier = issue.parent_id
    ? issueMap.get(issue.parent_id)
    : undefined;

  const close = (status: "done" | "cancelled") =>
    setStatus.mutate(
      { issue_id: issue.id, status },
      {
        onSuccess: () =>
          toast.success(
            status === "done" ? "המשימה סומנה כהושלמה" : "המשימה בוטלה",
          ),
        onError: () => toast.error("שגיאה בעדכון סטטוס המשימה"),
      },
    );

  return (
    <div className="border-b-2 border-rule last:border-b-0">
      <div className="w-full flex items-center gap-2 px-2 py-2.5 hover:bg-sand-soft/50">
        <button
          type="button"
          onClick={() => setOpen((o) => !o)}
          aria-expanded={open}
          className="flex-1 flex items-center gap-2 text-start min-w-0"
        >
          <Badge
            variant={closed ? "secondary" : "default"}
            className="text-[10px] font-mono shrink-0"
          >
            {issue.identifier}
          </Badge>
          <span className="text-[0.78rem] font-semibold text-navy truncate">
            {shortIssueTitle(issue.title)}
          </span>
          {parentIdentifier && (
            <span
              className="inline-flex items-center gap-0.5 text-[0.6rem] font-bold text-gold-deep bg-gold/10 border border-rule rounded-full px-1.5 py-0.5 shrink-0 whitespace-nowrap"
              title={`תת-משימה של ${parentIdentifier}`}
            >
              ↳ {parentIdentifier}
            </span>
          )}
          <span className="text-[0.7rem] text-ink-muted whitespace-nowrap">
            {issueStatusLabel(issue.status)} · {items.length}
          </span>
        </button>
        {!closed && (
          <div className="flex items-center gap-1 shrink-0">
            <Button
              size="sm"
              variant="outline"
              onClick={() => close("done")}
              disabled={setStatus.isPending}
              className="h-7 px-2 text-[0.7rem] text-success border-success/40 hover:bg-success-bg"
              title="סמן את המשימה כהושלמה"
            >
              {setStatus.isPending ? (
                <Loader2 className="w-3 h-3 animate-spin" />
              ) : (
                <CheckCircle2 className="w-3.5 h-3.5 me-1" />
              )}
              הושלם
            </Button>
            <Button
              size="sm"
              variant="ghost"
              onClick={() => close("cancelled")}
              disabled={setStatus.isPending}
              className="h-7 px-2 text-[0.7rem] text-ink-muted hover:text-danger"
              title="בטל את המשימה"
            >
              בטל
            </Button>
          </div>
        )}
        <button
          type="button"
          onClick={() => setOpen((o) => !o)}
          aria-label={open ? "כווץ" : "הרחב"}
          className="shrink-0"
        >
          <ChevronDown
            className={`w-4 h-4 text-ink-faint transition-transform ${open ? "rotate-180" : ""}`}
          />
        </button>
      </div>
      {open && (
        <div className="pb-1">
          {items.map((item) =>
            item.kind === "comment" ? (
              <CommentCard
                key={`c-${item.comment.id}`}
                comment={item.comment}
                issueMap={issueMap}
              />
            ) : (
              <InteractionCard
                key={`i-${item.interaction.id}`}
                interaction={item.interaction}
                caseNumber={caseNumber}
                issueMap={issueMap}
              />
            ),
          )}
        </div>
      )}
    </div>
  );
}

/* ── Compose: directive box + target selector (top of tab) ────── */

const isOpenStatus = (s: string) => s !== "done" && s !== "cancelled";

/** Client mirror of the server's pick_default_comment_target
 * (web/paperclip_client.py): prefer the newest OPEN top-level issue (the live
 * CEO main issue), then any open issue, then any top-level, else newest.
 * `issues` arrives oldest→newest, so the last match is the newest. */
function pickDefaultTarget(issues: PaperclipIssue[]): PaperclipIssue | null {
  if (!issues.length) return null;
  const isTop = (i: PaperclipIssue) => i.parent_id == null;
  const preds: ((i: PaperclipIssue) => boolean)[] = [
    (i) => isOpenStatus(i.status) && isTop(i),
    (i) => isOpenStatus(i.status),
    isTop,
  ];
  for (const p of preds) {
    const m = issues.filter(p);
    if (m.length) return m[m.length - 1];
  }
  return issues[issues.length - 1];
}

const STATUS_TONE: Record<string, string> = {
  in_progress: "bg-emerald-100 text-emerald-700",
  in_review: "bg-amber-100 text-amber-700",
  todo: "bg-emerald-100 text-emerald-700",
  backlog: "bg-emerald-100 text-emerald-700",
  blocked: "bg-red-100 text-red-700",
  done: "bg-gray-100 text-gray-500",
  cancelled: "bg-red-50 text-red-600",
};
function statusTone(s: string) {
  return STATUS_TONE[s] ?? "bg-gray-100 text-gray-600";
}

/** Where a chair instruction is routed. `auto` lets the server pick the live
 * CEO main issue; `issue` targets an explicit one; `new_run` opens a fresh
 * CEO-owned run (bypasses the human-owned-issue cancellation). */
type Target =
  | { kind: "auto" }
  | { kind: "issue"; id: string }
  | { kind: "new_run" };

function TargetSelector({
  issues,
  target,
  onChange,
}: {
  issues: PaperclipIssue[];
  target: Target;
  onChange: (t: Target) => void;
}) {
  const [open, setOpen] = useState(false);
  const defaultIssue = useMemo(() => pickDefaultTarget(issues), [issues]);
  const activeIssues = issues.filter((i) => isOpenStatus(i.status));
  const closedIssues = issues.filter((i) => !isOpenStatus(i.status));
  // id → identifier, to label a run with the parent it hangs under (↳ CMP-189).
  const idToIdentifier = useMemo(
    () => new Map(issues.map((i) => [i.id, i.identifier])),
    [issues],
  );
  const parentTag = (i: PaperclipIssue) =>
    i.parent_id ? idToIdentifier.get(i.parent_id) : undefined;

  // The issue the pill currently represents (explicit pick, or the auto default).
  const effectiveIssue =
    target.kind === "issue"
      ? issues.find((i) => i.id === target.id) ?? null
      : target.kind === "auto"
        ? defaultIssue
        : null;

  return (
    <div className="relative min-w-0">
      <button
        type="button"
        onClick={() => setOpen((o) => !o)}
        aria-expanded={open}
        className="flex items-center gap-2 bg-sand-soft/60 border border-rule rounded-lg px-2.5 py-1.5 hover:border-gold/60 min-w-0 max-w-full"
      >
        <Target className="w-3.5 h-3.5 text-gold-deep shrink-0" />
        {target.kind === "new_run" ? (
          <span className="text-xs font-semibold text-navy">פתח הרצה חדשה</span>
        ) : effectiveIssue ? (
          <>
            <Badge variant="outline" className="text-[10px] font-mono shrink-0">
              {effectiveIssue.identifier}
            </Badge>
            <span className="text-xs font-medium text-navy truncate">
              {shortIssueTitle(effectiveIssue.title)}
            </span>
            <span
              className={`text-[10px] font-semibold rounded-full px-2 py-0.5 shrink-0 ${statusTone(effectiveIssue.status)}`}
            >
              {issueStatusLabel(effectiveIssue.status)}
            </span>
          </>
        ) : (
          <span className="text-xs text-ink-muted">בחר יעד</span>
        )}
        <ChevronDown className="w-3.5 h-3.5 text-ink-faint shrink-0 ms-1" />
      </button>

      {open && (
        <>
          <div
            className="fixed inset-0 z-10"
            onClick={() => setOpen(false)}
            aria-hidden
          />
          <div className="absolute z-20 top-full mt-1 start-0 w-[360px] max-w-[88vw] max-h-[62vh] overflow-y-auto overscroll-contain bg-white border border-rule rounded-xl shadow-lg p-1.5">
            {/* "פתח הרצה חדשה" is the FIRST, most-prominent option so it's always
                visible without scrolling to the bottom of a long task list. */}
            <button
              type="button"
              onClick={() => {
                onChange({ kind: "new_run" });
                setOpen(false);
              }}
              className="w-full flex items-center gap-2.5 px-2 py-2 rounded-lg text-start bg-gold/10 border border-rule hover:border-gold/60"
            >
              <span className="w-6 h-6 rounded-md bg-navy text-white flex items-center justify-center shrink-0">
                <Plus className="w-4 h-4" />
              </span>
              <span className="min-w-0">
                <span className="block text-xs font-semibold text-navy">
                  פתח הרצה חדשה
                </span>
                <span className="block text-[10px] text-ink-muted">
                  ה-CEO ייצור משימה חדשה ויטפל בהוראה מאפס
                </span>
              </span>
            </button>
            <div className="h-px bg-rule-soft my-1.5 mx-1" />

            <div className="px-2 py-1 text-[10px] font-bold text-ink-faint uppercase tracking-wide">
              משימות פעילות
            </div>
            {activeIssues.map((i) => {
              const selected =
                (target.kind === "issue" && target.id === i.id) ||
                (target.kind === "auto" && defaultIssue?.id === i.id);
              const parent = parentTag(i);
              return (
                <button
                  key={i.id}
                  type="button"
                  onClick={() => {
                    onChange({ kind: "issue", id: i.id });
                    setOpen(false);
                  }}
                  className={`w-full flex items-center gap-2 px-2 py-2 rounded-lg text-start hover:bg-sand-soft ${parent ? "ps-6" : ""} ${selected ? "bg-gold/10" : ""}`}
                >
                  <span className="w-1.5 h-1.5 rounded-full bg-emerald-500 shrink-0" />
                  <Badge variant="outline" className="text-[10px] font-mono shrink-0">
                    {i.identifier}
                  </Badge>
                  {parent && (
                    <span
                      className="text-[9px] font-bold text-gold-deep bg-gold/10 border border-rule rounded-full px-1.5 py-0.5 shrink-0 whitespace-nowrap"
                      title={`תת-משימה של ${parent}`}
                    >
                      ↳ {parent}
                    </span>
                  )}
                  <span className="text-xs text-ink-soft truncate flex-1">
                    {shortIssueTitle(i.title)}
                  </span>
                  <span
                    className={`text-[10px] font-semibold rounded-full px-2 py-0.5 shrink-0 ${statusTone(i.status)}`}
                  >
                    {issueStatusLabel(i.status)}
                  </span>
                  {selected && (
                    <CheckCircle2 className="w-3.5 h-3.5 text-gold-deep shrink-0" />
                  )}
                </button>
              );
            })}

            {closedIssues.length > 0 && (
              <>
                <div className="h-px bg-rule-soft my-1.5 mx-1" />
                <div className="px-2 py-1 text-[10px] font-bold text-ink-faint uppercase tracking-wide">
                  משימות סגורות · שליחה אליהן לא תעיר סוכן
                </div>
                {closedIssues.map((i) => {
                  const parent = parentTag(i);
                  return (
                    <div
                      key={i.id}
                      className={`w-full flex items-center gap-2 px-2 py-2 rounded-lg opacity-60 cursor-not-allowed ${parent ? "ps-6" : ""}`}
                      title="משימה סגורה — שליחה אליה לא תעיר סוכן"
                    >
                      <span className="w-1.5 h-1.5 rounded-full bg-ink-faint shrink-0" />
                      <Badge variant="outline" className="text-[10px] font-mono shrink-0">
                        {i.identifier}
                      </Badge>
                      {parent && (
                        <span
                          className="text-[9px] font-bold text-gold-deep bg-gold/10 border border-rule rounded-full px-1.5 py-0.5 shrink-0 whitespace-nowrap"
                          title={`תת-משימה של ${parent}`}
                        >
                          ↳ {parent}
                        </span>
                      )}
                      <span className="text-xs text-ink-faint truncate flex-1">
                        {shortIssueTitle(i.title)}
                      </span>
                      <span
                        className={`text-[10px] font-semibold rounded-full px-2 py-0.5 shrink-0 ${statusTone(i.status)}`}
                      >
                        {issueStatusLabel(i.status)}
                      </span>
                    </div>
                  );
                })}
              </>
            )}
          </div>
        </>
      )}
    </div>
  );
}

function Composer({
  caseNumber,
  issues,
}: {
  caseNumber: string;
  issues: PaperclipIssue[];
}) {
  const sendComment = useSendComment(caseNumber);
  const [body, setBody] = useState("");
  const [target, setTarget] = useState<Target>({ kind: "auto" });

  const selectedIssue =
    target.kind === "issue" ? issues.find((i) => i.id === target.id) ?? null : null;
  const closedTarget = !!selectedIssue && !isOpenStatus(selectedIssue.status);

  const handleSend = () => {
    if (!body.trim() || closedTarget) return;
    const vars =
      target.kind === "new_run"
        ? { body: body.trim(), new_run: true }
        : target.kind === "issue"
          ? { body: body.trim(), issue_id: target.id }
          : { body: body.trim() };
    sendComment.mutate(vars, {
      onSuccess: (res) => {
        setBody("");
        setTarget({ kind: "auto" });
        toast.success(
          res.new_run
            ? `נפתחה הרצה חדשה — ${res.issue_identifier}`
            : `נשלח ל-${res.issue_identifier}`,
        );
      },
      onError: () => toast.error("שגיאה בשליחת ההודעה"),
    });
  };

  return (
    <div className="bg-white border border-rule rounded-xl shadow-sm p-3.5">
      <div className="flex items-center gap-2 mb-2.5">
        <MessageSquare className="w-4 h-4 text-gold-deep" />
        <h3 className="text-sm font-bold text-navy">הוראה לסוכנים</h3>
        <span className="text-[11px] text-ink-faint ms-auto">
          Ctrl+Enter לשליחה
        </span>
      </div>

      <div className="flex items-center gap-2 mb-2 flex-wrap">
        <span className="text-[11px] font-semibold text-ink-muted shrink-0">
          שליחה אל:
        </span>
        <TargetSelector issues={issues} target={target} onChange={setTarget} />
      </div>

      {closedTarget && (
        <div className="flex items-start gap-2 bg-red-50 border border-red-200 rounded-lg px-3 py-2 mb-2 text-[11.5px] text-red-700 leading-snug">
          <AlertTriangle className="w-4 h-4 shrink-0 mt-0.5" />
          <div>
            <b className="font-bold">יעד סגור.</b> שליחה למשימה סגורה לא תעיר סוכן —
            ההוראה לא תטופל. בחר משימה פעילה או{" "}
            <button
              type="button"
              className="font-bold underline"
              onClick={() => setTarget({ kind: "new_run" })}
            >
              פתח הרצה חדשה
            </button>
            .
          </div>
        </div>
      )}

      <Textarea
        value={body}
        onChange={(e) => setBody(e.target.value)}
        placeholder="כתוב הוראה לסוכנים..."
        aria-label="הוראה לסוכנים"
        className="min-h-[60px] resize-none text-sm"
        dir="rtl"
        onKeyDown={(e) => {
          if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
            e.preventDefault();
            handleSend();
          }
        }}
      />
      <div className="flex items-center mt-2">
        <span className="text-[11px] text-ink-faint">
          ההודעה תנותב דרך סוכן ה-CEO · היעד ניתן לשינוי למעלה
        </span>
        <Button
          size="sm"
          className="ms-auto"
          onClick={handleSend}
          disabled={!body.trim() || closedTarget || sendComment.isPending}
        >
          {sendComment.isPending ? (
            <Loader2 className="w-4 h-4 animate-spin" />
          ) : (
            <Send className="w-4 h-4 me-1" />
          )}
          שלח
        </Button>
      </div>
    </div>
  );
}

/* ── Main Feed ───────────────────────────────────────────────── */

export function AgentActivityFeed({
  caseNumber,
}: {
  caseNumber: string;
}) {
  const { data, isLoading, error } = useAgentActivity(caseNumber);

  // Build issue_id → identifier map (memoized — the feed refetches every 10s,
  // and a fresh Map each render would defeat the child cards' memoization).
  const issueMap = useMemo(() => {
    const m = new Map<string, string>();
    for (const iss of data?.issues ?? []) m.set(iss.id, iss.identifier);
    return m;
  }, [data?.issues]);

  // ── Empty / loading states ──

  if (isLoading) {
    return (
      <div className="flex items-center justify-center py-12 text-ink-faint">
        <Loader2 className="w-5 h-5 animate-spin me-2" />
        <span>טוען פעילות סוכנים...</span>
      </div>
    );
  }

  if (error) {
    return (
      <div className="text-center py-12 text-red-500 text-sm">
        שגיאה בטעינת פעילות סוכנים
      </div>
    );
  }

  if (!data?.issues?.length) {
    return (
      <div className="text-center py-12 space-y-2">
        <MessageSquare className="w-10 h-10 mx-auto text-ink-faint/40" />
        <p className="text-sm text-ink-faint">
          התהליך טרם הופעל. לחץ &quot;התחל תהליך&quot; בלשונית סקירה.
        </p>
      </div>
    );
  }

  const comments = data.comments ?? [];
  const interactions = data.interactions ?? [];
  // Pending interactions are surfaced at the top (awaiting-you, mockup 18i) and
  // NOT repeated in the timeline below.
  const pending = interactions.filter((i) => i.status === "pending");

  // Group the timeline by issue, each an accordion (mockup 18i v2). Comments +
  // resolved interactions; pending ones are surfaced above and not repeated.
  const itemsByIssue = new Map<string, FeedItem[]>();
  const pushItem = (issueId: string, item: FeedItem) => {
    const arr = itemsByIssue.get(issueId);
    if (arr) arr.push(item);
    else itemsByIssue.set(issueId, [item]);
  };
  for (const c of comments) {
    pushItem(c.issue_id, {
      kind: "comment",
      at: c.created_at ? new Date(c.created_at).getTime() : 0,
      comment: c,
    });
  }
  for (const i of interactions) {
    if (i.status === "pending") continue;
    pushItem(i.issue_id, {
      kind: "interaction",
      at: i.created_at ? new Date(i.created_at).getTime() : 0,
      interaction: i,
    });
  }
  for (const arr of itemsByIssue.values()) arr.sort((a, b) => a.at - b.at);

  // Order groups by most-recent activity so live work floats to the top.
  const issueGroups = data.issues
    .map((iss) => {
      const items = itemsByIssue.get(iss.id) ?? [];
      const maxAt = items.reduce((m, it) => Math.max(m, it.at), 0);
      return { iss, items, maxAt };
    })
    .filter((g) => g.items.length > 0)
    .sort((a, b) => b.maxAt - a.maxAt);

  const totalFeedItems = issueGroups.reduce((n, g) => n + g.items.length, 0);

  // An issue is "active" if it's not done/cancelled. When everything is closed
  // we should NOT show the "agents are working, waiting for report" spinner.
  const hasActiveIssue = data.issues.some(
    (iss) => iss.status !== "done" && iss.status !== "cancelled",
  );

  return (
    <div className="flex flex-col h-full gap-3">
      {/* directive box — moved to the TOP, above the activity (most available,
          no scrolling). Carries the target selector. */}
      <Composer caseNumber={caseNumber} issues={data.issues} />

      {/* agent roster — who's who, compact one-row (mockup 18i v2) */}
      <AgentRoster agents={data.agents ?? []} />

      {/* pending interactions surfaced to the top — each an accordion, all
          COLLAPSED by default (chair opens to look). Excluded from the timeline. */}
      {pending.length > 0 && (
        <div className="rounded-lg border border-amber-300 bg-amber-50 p-3 space-y-2">
          <div className="flex items-center gap-2 text-[0.82rem] font-bold text-amber-800">
            <Clock className="w-4 h-4" />
            ממתין לתשובתך
            <span className="text-[0.7rem] font-semibold bg-amber-100 rounded-full px-2 py-0.5">
              {pending.length}
            </span>
          </div>
          {pending.map((i) => (
            <InteractionCard
              key={`pending-${i.id}`}
              interaction={i}
              caseNumber={caseNumber}
              issueMap={issueMap}
              defaultOpen={false}
            />
          ))}
        </div>
      )}

      {/* Timeline header */}
      <div className="flex items-center gap-2 px-2 py-2 border-b border-rule">
        <span className="text-[0.82rem] font-bold text-navy">פעילות לפי משימה</span>
        <span className="text-[0.68rem] font-semibold text-gold-deep bg-gold/10 rounded-full px-2 py-0.5">
          החדש למעלה
        </span>
        <span className="text-[0.72rem] text-ink-muted ms-auto">
          {data.issues.length} משימות · {totalFeedItems} הודעות
        </span>
      </div>

      {/* Per-issue accordions — all COLLAPSED by default, newest group first */}
      <div className="flex-1 overflow-y-auto max-h-[500px]">
        {totalFeedItems === 0 ? (
          hasActiveIssue ? (
            <div className="text-center py-8 text-ink-faint text-sm">
              <Loader2 className="w-5 h-5 animate-spin mx-auto mb-2" />
              הסוכנים התחילו לעבוד, ממתין לדיווח ראשון...
            </div>
          ) : (
            <div className="text-center py-8 text-ink-faint text-sm">
              <MessageSquare className="w-5 h-5 mx-auto mb-2 opacity-50" />
              אין משימות פעילות בתיק. כל המשימות הסתיימו או בוטלו.
            </div>
          )
        ) : (
          issueGroups.map(({ iss, items }) => (
            <IssueGroup
              key={iss.id}
              issue={iss}
              items={items}
              caseNumber={caseNumber}
              issueMap={issueMap}
              defaultOpen={false}
            />
          ))
        )}
      </div>
    </div>
  );
}
