// =============================================================================
// WOOO AIOps - AwoooP M7 審批佇列頁面
// =============================================================================
// 每 10 秒自動刷新，timeout < 5 分鐘時紅色高亮警示

"use client";

import { useState, useEffect, useCallback, useMemo, useRef } from "react";
import { useSearchParams } from "next/navigation";
import { useTranslations } from "next-intl";
import {
  ShieldCheck,
  RefreshCw,
  AlertCircle,
  Clock,
  ArrowRight,
  ChevronDown,
  ExternalLink,
  Filter,
  ListChecks,
  MessageSquareWarning,
  SearchCheck,
  TriangleAlert,
  GitBranch,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { Link } from "@/i18n/routing";
import {
  AwoooPStatusChainPanel,
  type AwoooPStatusChain,
} from "@/components/awooop/status-chain";
import type { IncidentTimelineResponse } from "@/lib/api-client";

// =============================================================================
// Types
// =============================================================================

type RemediationStatus =
  | "no_evidence"
  | "mcp_observed"
  | "read_only_dry_run"
  | "write_observed"
  | "blocked"
  | "observed";

interface RemediationSummary {
  incident_ids?: string[];
  total?: number;
  evidence_total?: number;
  status?: RemediationStatus | string;
  has_mcp_investigation?: boolean;
  human_gate_open?: boolean;
  latest_route?: string | null;
  latest_preview?: string | null;
  writes_incident_state?: boolean | null;
  writes_auto_repair_result?: boolean | null;
  mcp_observation_total?: number;
  mcp_observation_success?: number;
  mcp_observation_failed?: number;
}

interface Approval {
  run_id: string;
  project_id: string;
  agent_id: string;
  trigger_type?: string | null;
  trigger_ref?: string | null;
  is_shadow?: boolean | null;
  created_at: string;
  timeout_at: string | null;
  remediation_summary?: RemediationSummary | null;
  awooop_status_chain?: AwoooPStatusChain | null;
}

interface LegacyApproval {
  id: string;
  action: string;
  description?: string | null;
  status: string;
  risk_level?: string | null;
  incident_id?: string | null;
  created_at: string;
  expires_at?: string | null;
  telegram_message_id?: number | null;
  current_signatures?: number | null;
  required_signatures?: number | null;
}

// =============================================================================
// 常數
// =============================================================================

const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "";
const AUTO_REFRESH_INTERVAL = 10_000; // 10 秒

// =============================================================================
// Helpers
// =============================================================================

function getRemainingMs(timeoutAt: string | null): number | null {
  if (!timeoutAt) return null;
  return new Date(timeoutAt).getTime() - Date.now();
}

function formatRemaining(ms: number): string {
  if (ms <= 0) return "已超時";
  const totalSec = Math.floor(ms / 1000);
  const minutes = Math.floor(totalSec / 60);
  const seconds = totalSec % 60;
  if (minutes > 60) {
    const hours = Math.floor(minutes / 60);
    return `${hours}h ${minutes % 60}m`;
  }
  if (minutes > 0) return `${minutes}m ${seconds}s`;
  return `${seconds}s`;
}

function formatLocalTime(value?: string | null): string {
  if (!value) return "--";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return "--";
  return date.toLocaleString("zh-TW", {
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
  });
}

async function fetchJson<T>(url: string, timeoutMs = 12_000): Promise<T | null> {
  const controller = new AbortController();
  const timeout = window.setTimeout(() => controller.abort(), timeoutMs);
  try {
    const res = await fetch(url, {
      cache: "no-store",
      signal: controller.signal,
    });
    if (!res.ok) return null;
    return (await res.json()) as T;
  } catch {
    return null;
  } finally {
    window.clearTimeout(timeout);
  }
}

function timelineStatusClass(status?: string | null) {
  const normalized = String(status ?? "").toLowerCase();
  if (normalized.includes("success") || normalized.includes("ok") || normalized.includes("resolved")) {
    return "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]";
  }
  if (normalized.includes("fail") || normalized.includes("block") || normalized.includes("error")) {
    return "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]";
  }
  if (normalized.includes("warn") || normalized.includes("pending") || normalized.includes("investigating")) {
    return "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]";
  }
  return "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]";
}

function approvalMatchesIncident(approval: Approval, incidentId: string) {
  const summaryIds = approval.remediation_summary?.incident_ids ?? [];
  const chainIds = approval.awooop_status_chain?.incident_ids ?? [];
  return summaryIds.includes(incidentId)
    || chainIds.includes(incidentId)
    || approval.awooop_status_chain?.source_id === incidentId;
}

function uniqueValues(values: Array<string | null | undefined>) {
  return Array.from(new Set(values.filter((value): value is string => Boolean(value))));
}

// =============================================================================
// Sub Components
// =============================================================================

const REMEDIATION_STATUS_CONFIG: Record<
  RemediationStatus,
  {
    labelKey: string;
    detailKey: string;
    icon: typeof ShieldCheck;
    className: string;
  }
> = {
  no_evidence: {
    labelKey: "statuses.noEvidence",
    detailKey: "details.noEvidence",
    icon: AlertCircle,
    className: "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]",
  },
  mcp_observed: {
    labelKey: "statuses.mcpObserved",
    detailKey: "details.mcpObserved",
    icon: SearchCheck,
    className: "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]",
  },
  read_only_dry_run: {
    labelKey: "statuses.readOnlyDryRun",
    detailKey: "details.readOnlyDryRun",
    icon: SearchCheck,
    className: "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]",
  },
  write_observed: {
    labelKey: "statuses.writeObserved",
    detailKey: "details.writeObserved",
    icon: TriangleAlert,
    className: "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
  },
  blocked: {
    labelKey: "statuses.blocked",
    detailKey: "details.blocked",
    icon: TriangleAlert,
    className: "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]",
  },
  observed: {
    labelKey: "statuses.observed",
    detailKey: "details.observed",
    icon: ListChecks,
    className: "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]",
  },
};
const REMEDIATION_FILTER_OPTIONS: RemediationStatus[] = [
  "mcp_observed",
  "read_only_dry_run",
  "write_observed",
  "blocked",
  "observed",
  "no_evidence",
];

function normalizeRemediationStatus(summary?: RemediationSummary | null): RemediationStatus {
  const statusValue = summary?.status;
  if (
    statusValue === "mcp_observed" ||
    statusValue === "read_only_dry_run" ||
    statusValue === "write_observed" ||
    statusValue === "blocked" ||
    statusValue === "observed"
  ) {
    return statusValue;
  }
  return "no_evidence";
}

function RemediationEvidenceCell({ summary }: { summary?: RemediationSummary | null }) {
  const t = useTranslations("awooop.listEvidence");
  const status = normalizeRemediationStatus(summary);
  const config = REMEDIATION_STATUS_CONFIG[status];
  const Icon = config.icon;
  const total = summary?.total ?? 0;
  const mcpTotal = summary?.mcp_observation_total ?? 0;
  const evidenceTotal = summary?.evidence_total ?? total + mcpTotal;
  const route = summary?.latest_route && summary.latest_route !== "--"
    ? summary.latest_route
    : null;
  const countText = status === "mcp_observed"
    ? t("mcpCount", { count: mcpTotal || evidenceTotal })
    : t("count", { count: total });

  return (
    <div className="flex min-w-[230px] flex-col gap-1">
      <span
        className={cn(
          "inline-flex w-fit items-center gap-1.5 border px-2 py-0.5 text-xs font-semibold",
          config.className
        )}
        title={t(config.detailKey)}
      >
        <Icon className="h-3.5 w-3.5" aria-hidden="true" />
        {t(config.labelKey)}
      </span>
      {total > 0 || mcpTotal > 0 ? (
        <span className="text-xs leading-5 text-[#5f5b52]">
          {countText}
          {route ? ` · ${t("route", { route })}` : ""}
        </span>
      ) : (
        <span className="text-xs leading-5 text-[#77736a]">{t("emptyShort")}</span>
      )}
      <span className="text-xs font-semibold text-[#8a5a08]">
        {t("manualGate")}
      </span>
    </div>
  );
}

function LegacyHitlBacklogPanel({
  approvals,
  loading,
  error,
}: {
  approvals: LegacyApproval[];
  loading: boolean;
  error: string | null;
}) {
  const tLegacy = useTranslations("awooop.approvals.legacyHitl");
  const noTelegramCount = approvals.filter((approval) => !approval.telegram_message_id).length;
  const criticalCount = approvals.filter(
    (approval) => approval.risk_level?.toLowerCase() === "critical"
  ).length;
  const observeCount = approvals.filter((approval) =>
    /^(OBSERVE|NO_ACTION)/i.test(approval.action)
  ).length;

  if (!loading && approvals.length === 0 && !error) return null;

  return (
    <section className="border border-[#d9b36f] bg-[#fffaf0]">
      <div className="flex flex-wrap items-start justify-between gap-3 border-b border-[#ead9b4] bg-[#fff7e8] px-4 py-3">
        <div className="flex items-start gap-3">
          <MessageSquareWarning className="mt-0.5 h-5 w-5 text-[#8a5a08]" aria-hidden="true" />
          <div>
            <h3 className="text-sm font-semibold text-[#141413]">{tLegacy("title")}</h3>
            <p className="mt-1 text-xs leading-5 text-[#5f5b52]">{tLegacy("subtitle")}</p>
          </div>
        </div>
        <Link
          href="/authorizations"
          className="inline-flex items-center gap-2 border border-[#d9b36f] bg-white px-3 py-2 text-xs font-semibold text-[#8a5a08] transition hover:bg-[#fff3d6]"
        >
          {tLegacy("openAuthorizations")}
          <ExternalLink className="h-3.5 w-3.5" aria-hidden="true" />
        </Link>
      </div>

      <div className="grid gap-px bg-[#ead9b4] md:grid-cols-4">
        {[
          [tLegacy("summary.pending"), approvals.length],
          [tLegacy("summary.noTelegram"), noTelegramCount],
          [tLegacy("summary.observe"), observeCount],
          [tLegacy("summary.critical"), criticalCount],
        ].map(([label, value]) => (
          <div key={label} className="bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#77736a]">{label}</p>
            <p className="mt-2 font-mono text-2xl font-semibold text-[#141413]">{value}</p>
          </div>
        ))}
      </div>

      {error && (
        <div className="flex items-start gap-2 border-t border-[#ead9b4] bg-[#fff0ef] px-4 py-3 text-xs text-[#9f2f25]">
          <AlertCircle className="mt-0.5 h-4 w-4" aria-hidden="true" />
          {tLegacy("loadFailed", { error })}
        </div>
      )}

      {(loading || approvals.length > 0) && (
        <div className="overflow-x-auto bg-white">
          <table className="w-full" aria-label={tLegacy("tableLabel")}>
            <thead>
              <tr className="border-b border-[#e0ddd4] bg-[#faf9f3]">
                <th className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[#77736a]">{tLegacy("columns.risk")}</th>
                <th className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[#77736a]">{tLegacy("columns.action")}</th>
                <th className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[#77736a]">{tLegacy("columns.incident")}</th>
                <th className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[#77736a]">{tLegacy("columns.source")}</th>
                <th className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-[#77736a]">{tLegacy("columns.created")}</th>
              </tr>
            </thead>
            <tbody>
              {loading ? (
                Array.from({ length: 4 }).map((_, row) => (
                  <tr key={row} className="border-b border-[#f0ede5]">
                    {Array.from({ length: 5 }).map((_, cell) => (
                      <td key={cell} className="px-4 py-3">
                        <div className="h-4 w-24 animate-pulse rounded bg-[#f0ede5]" />
                      </td>
                    ))}
                  </tr>
                ))
              ) : (
                approvals.slice(0, 8).map((approval) => {
                  const risk = approval.risk_level?.toLowerCase() ?? "unknown";
                  const riskClass =
                    risk === "critical"
                      ? "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]"
                      : "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]";
                  return (
                    <tr key={approval.id} className="border-b border-[#f0ede5]">
                      <td className="px-4 py-3 align-top">
                        <span className={cn("inline-flex border px-2 py-0.5 text-xs font-semibold", riskClass)}>
                          {risk}
                        </span>
                      </td>
                      <td className="max-w-[420px] px-4 py-3 align-top">
                        <p className="truncate text-sm font-semibold text-[#141413]">{approval.action}</p>
                        <p className="mt-1 line-clamp-2 text-xs leading-5 text-[#5f5b52]">
                          {approval.description || "--"}
                        </p>
                      </td>
                      <td className="px-4 py-3 align-top font-mono text-xs text-[#5f5b52]">
                        {approval.incident_id || "--"}
                      </td>
                      <td className="px-4 py-3 align-top text-xs text-[#5f5b52]">
                        {approval.telegram_message_id
                          ? tLegacy("telegramRef", { id: approval.telegram_message_id })
                          : tLegacy("noTelegram")}
                      </td>
                      <td className="px-4 py-3 align-top text-xs text-[#5f5b52]">
                        {formatLocalTime(approval.created_at)}
                      </td>
                    </tr>
                  );
                })
              )}
            </tbody>
          </table>
          {!loading && approvals.length > 8 && (
            <div className="border-t border-[#e0ddd4] bg-[#faf9f3] px-4 py-3 text-xs text-[#5f5b52]">
              {tLegacy("moreRows", { count: approvals.length - 8 })}
            </div>
          )}
        </div>
      )}
    </section>
  );
}

type ApprovalSourceFlowStatus = "verified" | "applied" | "evidence" | "provider" | "waiting";

function approvalSourceFlowStatus(chain?: AwoooPStatusChain | null): ApprovalSourceFlowStatus {
  const correlation = chain?.source_refs?.correlation;
  if (!correlation) return "waiting";

  const verificationStatus = String(correlation.verification_status ?? correlation.status ?? "missing");
  if (verificationStatus === "applied_link_verified" || verificationStatus === "direct_ref_verified") {
    return "verified";
  }
  if ((correlation.applied_link_total ?? 0) > 0) return "applied";
  if ((correlation.direct_ref_total ?? 0) > 0 || (correlation.candidate_total ?? 0) > 0) {
    return "evidence";
  }
  if ((correlation.provider_event_total ?? 0) > 0) return "provider";
  return "waiting";
}

function approvalSourceFlowClass(status: ApprovalSourceFlowStatus) {
  if (status === "verified") return "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]";
  if (status === "applied" || status === "evidence") return "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]";
  if (status === "provider") return "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]";
  return "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]";
}

function ApprovalSourceFlowCell({ chain }: { chain?: AwoooPStatusChain | null }) {
  const t = useTranslations("awooop.listEvidence.sourceFlow");
  const status = approvalSourceFlowStatus(chain);
  const correlation = chain?.source_refs?.correlation;
  const detail = t("detail", {
    providers: correlation?.provider_event_total ?? 0,
    direct: correlation?.direct_ref_total ?? 0,
    candidate: correlation?.candidate_total ?? 0,
    applied: correlation?.applied_link_total ?? 0,
  });
  const Icon = status === "verified" || status === "applied"
    ? ShieldCheck
    : status === "waiting"
      ? AlertCircle
      : SearchCheck;

  return (
    <div className="min-w-[190px]">
      <span
        className={cn(
          "inline-flex items-center gap-1.5 border px-2 py-1 text-xs font-semibold",
          approvalSourceFlowClass(status)
        )}
      >
        <Icon className="h-3.5 w-3.5" aria-hidden="true" />
        {t(`statuses.${status}`)}
      </span>
      <p className="mt-1 truncate font-mono text-xs text-[#77736a]" title={detail}>
        {detail}
      </p>
    </div>
  );
}

function TimeoutCell({ timeoutAt }: { timeoutAt: string | null }) {
  const [remaining, setRemaining] = useState<number | null>(
    getRemainingMs(timeoutAt)
  );

  useEffect(() => {
    if (!timeoutAt) return;
    const timer = setInterval(() => {
      setRemaining(getRemainingMs(timeoutAt));
    }, 1000);
    return () => clearInterval(timer);
  }, [timeoutAt]);

  if (remaining === null) return <span className="text-muted-foreground text-sm">--</span>;

  const isCritical = remaining <= 5 * 60 * 1000; // 5 分鐘內
  const isExpired = remaining <= 0;

  return (
    <span
      className={cn(
        "inline-flex items-center gap-1.5 font-mono text-sm",
        isExpired
          ? "text-red-400"
          : isCritical
          ? "text-red-300 font-semibold animate-pulse"
          : "text-muted-foreground"
      )}
      aria-label={`剩餘時間: ${formatRemaining(remaining)}`}
    >
      <Clock className="w-3.5 h-3.5" aria-hidden="true" />
      {formatRemaining(remaining)}
    </span>
  );
}

function DecisionPostureBadge() {
  const t = useTranslations("awooop.approvals");
  return (
    <span className="inline-flex items-center gap-1.5 border border-[#d9b36f] bg-[#fff7e8] px-2 py-0.5 text-xs font-semibold text-[#8a5a08]">
      <ShieldCheck className="h-3.5 w-3.5" aria-hidden="true" />
      {t("badges.humanGate")}
    </span>
  );
}

function Gate5ProjectionBadge() {
  const t = useTranslations("awooop.approvals");
  return (
    <span className="mt-1 inline-flex items-center gap-1.5 border border-[#9bb6d9] bg-[#eef5ff] px-2 py-0.5 text-xs font-semibold text-[#1f5b9b]">
      <GitBranch className="h-3.5 w-3.5" aria-hidden="true" />
      {t("badges.gate5Projection")}
      <span className="font-normal text-[#47709a]">{t("badges.executorHandoffPending")}</span>
    </span>
  );
}

function ApprovalRow({ approval }: { approval: Approval }) {
  const formattedDate = approval.created_at
    ? new Date(approval.created_at).toLocaleDateString("zh-TW", {
        month: "2-digit",
        day: "2-digit",
        hour: "2-digit",
        minute: "2-digit",
      })
    : "--";

  const remainingMs = getRemainingMs(approval.timeout_at);
  const isCritical = remainingMs !== null && remainingMs <= 5 * 60 * 1000;
  const isGate5Projection = approval.trigger_type === "adr100_runtime_replay_gate5";

  return (
    <tr
      className={cn(
        "border-b border-border hover:bg-accent/30 transition-colors",
        isCritical && "bg-[#fff0ef] hover:bg-[#ffe4e1]"
      )}
    >
      <td className="px-4 py-3">
        <Link
          href={`/awooop/approvals/${approval.run_id}`}
          className="inline-flex items-center gap-1.5 font-mono text-xs text-brand-accent bg-brand-accent/10 px-2 py-1 rounded border border-brand-accent/20 hover:bg-brand-accent/20 transition-colors group"
          aria-label={`開啟審批 ${approval.run_id}`}
        >
          {approval.run_id.slice(0, 8)}
          <ArrowRight className="w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity" aria-hidden="true" />
        </Link>
      </td>
      <td className="px-4 py-3">
        <span className="font-mono text-sm text-muted-foreground">
          {approval.project_id || "--"}
        </span>
      </td>
      <td className="px-4 py-3">
        <div className="flex min-w-[180px] flex-col items-start">
          <span className="font-mono text-sm text-muted-foreground">
            {approval.agent_id || "--"}
          </span>
          {isGate5Projection && <Gate5ProjectionBadge />}
        </div>
      </td>
      <td className="px-4 py-3">
        <DecisionPostureBadge />
      </td>
      <td className="px-4 py-3">
        <RemediationEvidenceCell summary={approval.remediation_summary} />
      </td>
      <td className="px-4 py-3">
        <ApprovalSourceFlowCell chain={approval.awooop_status_chain} />
      </td>
      <td className="min-w-[280px] px-4 py-3">
        <AwoooPStatusChainPanel chain={approval.awooop_status_chain} compact />
      </td>
      <td className="px-4 py-3">
        <span className="text-sm text-muted-foreground font-mono">
          {formattedDate}
        </span>
      </td>
      <td className="px-4 py-3">
        <TimeoutCell timeoutAt={approval.timeout_at} />
      </td>
    </tr>
  );
}

function SecurityOwnerResponseGatePanel() {
  const t = useTranslations("awooop.approvals.securityOwnerResponseGate");
  const metrics = [
    {
      label: t("metrics.received"),
      value: "0",
      detail: t("metrics.receivedDetail"),
    },
    {
      label: t("metrics.accepted"),
      value: "0",
      detail: t("metrics.acceptedDetail"),
    },
    {
      label: t("metrics.activeRuntimeGates"),
      value: "0",
      detail: t("metrics.activeRuntimeGatesDetail"),
    },
    {
      label: t("metrics.headline"),
      value: "64%",
      detail: t("metrics.headlineDetail"),
    },
  ];
  const checks = [
    { phase: "S4.9", key: "s49OwnerAttestation" },
    { phase: "S4.10", key: "s410GithubTarget" },
    { phase: "S4.11", key: "s411RefsTruth" },
    { phase: "S4.12", key: "s412WorkflowSecret" },
  ];

  return (
    <section className="border border-[#e0ddd4] bg-white">
      <div className="flex flex-wrap items-start justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
        <div className="flex items-start gap-2">
          <ShieldCheck className="mt-0.5 h-4 w-4 text-[#d97757]" aria-hidden="true" />
          <div>
            <h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
            <p className="mt-1 max-w-3xl text-xs leading-5 text-[#77736a]">{t("subtitle")}</p>
          </div>
        </div>
        <span className="inline-flex border border-[#9bb6d9] bg-[#eef5ff] px-2 py-0.5 text-xs font-semibold text-[#1f5b9b]">
          {t("badge")}
        </span>
      </div>
      <div className="grid gap-px bg-[#e0ddd4] md:grid-cols-2 xl:grid-cols-4">
        {metrics.map((item) => (
          <div key={item.label} className="bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
            <p className="mt-2 font-mono text-2xl font-semibold text-[#141413]">{item.value}</p>
            <p className="mt-2 text-xs leading-5 text-[#5f5b52]">{item.detail}</p>
          </div>
        ))}
      </div>
      <div className="grid gap-4 p-4 xl:grid-cols-[1.1fr_0.9fr]">
        <div className="border border-[#e0ddd4] bg-white">
          <div className="border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3 text-xs font-semibold text-[#141413]">
            {t("ownerChecksTitle")}
          </div>
          <div className="divide-y divide-[#eee9dd]">
            {checks.map((item) => (
              <div key={item.key} className="grid gap-3 px-4 py-3 md:grid-cols-[72px_1fr]">
                <span className="font-mono text-xs font-semibold text-[#141413]">{item.phase}</span>
                <div>
                  <p className="text-sm font-semibold text-[#141413]">
                    {t(`checks.${item.key}.title`)}
                  </p>
                  <p className="mt-1 text-xs leading-5 text-[#5f5b52]">
                    {t(`checks.${item.key}.detail`)}
                  </p>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="border border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
          <p className="text-xs font-semibold text-[#77736a]">{t("boundaryLabel")}</p>
          <p className="mt-2 text-sm font-semibold text-[#141413]">{t("boundaryTitle")}</p>
          <p className="mt-2 text-xs leading-5 text-[#5f5b52]">{t("boundaryDetail")}</p>
          <div className="mt-4 grid gap-2 break-all font-mono text-xs text-[#141413]">
            <span>approval_record_created=false</span>
            <span>owner_response_accepted_count=0</span>
            <span>runtime_execution_authorized=false</span>
            <span>action_buttons_allowed=false</span>
          </div>
          <Link
            href="/iwooos"
            className="mt-4 inline-flex items-center gap-2 border border-[#d8d3c7] bg-white px-3 py-1.5 text-xs font-semibold text-[#141413] hover:border-[#d97757]"
          >
            {t("openIwooos")}
            <ArrowRight className="h-3.5 w-3.5 text-[#77736a]" aria-hidden="true" />
          </Link>
        </div>
      </div>
    </section>
  );
}

function GitHubPrimaryReadinessApprovalBoundaryPanel() {
  const t = useTranslations("awooop.approvals.githubPrimaryReadinessGate");
  const metrics = [
    {
      label: t("metrics.giteaOwner"),
      value: "0/5",
      detail: t("metrics.giteaOwnerDetail"),
    },
    {
      label: t("metrics.githubTargetOwner"),
      value: "0/7",
      detail: t("metrics.githubTargetOwnerDetail"),
    },
    {
      label: t("metrics.refsTruth"),
      value: "0/5",
      detail: t("metrics.refsTruthDetail"),
    },
    {
      label: t("metrics.workflowSecretNames"),
      value: "0/5",
      detail: t("metrics.workflowSecretNamesDetail"),
    },
    {
      label: t("metrics.primaryReady"),
      value: "0",
      detail: t("metrics.primaryReadyDetail"),
    },
  ];
  const responseLanes = [
    { phase: "S4.9", key: "giteaOwnerAttestation" },
    { phase: "S4.10", key: "githubTargetOwner" },
    { phase: "S4.11", key: "refsTruthOwner" },
    { phase: "S4.12", key: "workflowSecretOwner" },
  ];

  return (
    <section className="border border-[#d7e2e0] bg-white">
      <div className="flex flex-wrap items-start justify-between gap-3 border-b border-[#d7e2e0] bg-[#f5fbf9] px-4 py-3">
        <div className="flex items-start gap-2">
          <GitBranch className="mt-0.5 h-4 w-4 text-[#2f7d72]" aria-hidden="true" />
          <div>
            <h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
            <p className="mt-1 max-w-3xl text-xs leading-5 text-[#5f6f6b]">{t("subtitle")}</p>
          </div>
        </div>
        <span className="inline-flex border border-[#8bb9b0] bg-white px-2 py-0.5 text-xs font-semibold text-[#1f6b61]">
          {t("badge")}
        </span>
      </div>
      <div className="grid gap-px bg-[#d7e2e0] md:grid-cols-2 xl:grid-cols-5">
        {metrics.map((item) => (
          <div key={item.label} className="bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#5f6f6b]">{item.label}</p>
            <p className="mt-2 font-mono text-2xl font-semibold text-[#141413]">{item.value}</p>
            <p className="mt-2 text-xs leading-5 text-[#4f5f5b]">{item.detail}</p>
          </div>
        ))}
      </div>
      <div className="grid gap-4 p-4 xl:grid-cols-[1.1fr_0.9fr]">
        <div className="border border-[#d7e2e0] bg-white">
          <div className="border-b border-[#d7e2e0] bg-[#f5fbf9] px-4 py-3 text-xs font-semibold text-[#141413]">
            {t("responseLanesTitle")}
          </div>
          <div className="divide-y divide-[#e3eeec]">
            {responseLanes.map((item) => (
              <div key={item.key} className="grid gap-3 px-4 py-3 md:grid-cols-[72px_1fr]">
                <span className="font-mono text-xs font-semibold text-[#141413]">{item.phase}</span>
                <div>
                  <p className="text-sm font-semibold text-[#141413]">
                    {t(`responseLanes.${item.key}.title`)}
                  </p>
                  <p className="mt-1 text-xs leading-5 text-[#4f5f5b]">
                    {t(`responseLanes.${item.key}.detail`)}
                  </p>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="border border-[#d7e2e0] bg-[#f5fbf9] px-4 py-3">
          <p className="text-xs font-semibold text-[#5f6f6b]">{t("boundaryLabel")}</p>
          <p className="mt-2 text-sm font-semibold text-[#141413]">{t("boundaryTitle")}</p>
          <p className="mt-2 text-xs leading-5 text-[#4f5f5b]">{t("boundaryDetail")}</p>
          <div className="mt-4 grid gap-2 break-all font-mono text-xs text-[#141413]">
            <span>approval_record_created=false</span>
            <span>github_primary_approval_granted=false</span>
            <span>owner_response_accepted_count=0</span>
            <span>repo_creation_authorized=false</span>
            <span>refs_mutation_authorized=false</span>
            <span>secret_value_collection_allowed=false</span>
            <span>runtime_execution_authorized=false</span>
            <span>action_buttons_allowed=false</span>
          </div>
          <Link
            href="/iwooos"
            className="mt-4 inline-flex items-center gap-2 border border-[#8bb9b0] bg-white px-3 py-1.5 text-xs font-semibold text-[#141413] hover:border-[#2f7d72]"
          >
            {t("openIwooos")}
            <ArrowRight className="h-3.5 w-3.5 text-[#5f6f6b]" aria-hidden="true" />
          </Link>
        </div>
      </div>
    </section>
  );
}

function OwnerResponseValidationApprovalBoundaryPanel() {
  const t = useTranslations("awooop.approvals.ownerResponseValidationBoundary");
  const metrics = [
    {
      label: t("metrics.packets"),
      value: "4",
      detail: t("metrics.packetsDetail"),
    },
    {
      label: t("metrics.templates"),
      value: "22",
      detail: t("metrics.templatesDetail"),
    },
    {
      label: t("metrics.received"),
      value: "0",
      detail: t("metrics.receivedDetail"),
    },
    {
      label: t("metrics.accepted"),
      value: "0",
      detail: t("metrics.acceptedDetail"),
    },
    {
      label: t("metrics.rejected"),
      value: "0",
      detail: t("metrics.rejectedDetail"),
    },
    {
      label: t("metrics.displaySections"),
      value: "8",
      detail: t("metrics.displaySectionsDetail"),
    },
  ];
  const reviewRefs = [
    {
      phase: "S4.13",
      key: "validationRollup",
      contract: "source_control_owner_response_validation_rollup_v1",
    },
    {
      phase: "S4.9",
      key: "giteaAttestation",
      contract: "gitea_inventory_owner_attestation_response_v1",
    },
    {
      phase: "S4.10",
      key: "githubTarget",
      contract: "github_target_owner_decision_response_v1",
    },
    {
      phase: "S4.11",
      key: "refsTruth",
      contract: "source_control_ref_truth_owner_response_v1",
    },
    {
      phase: "S4.12",
      key: "workflowSecret",
      contract: "source_control_workflow_secret_name_owner_response_v1",
    },
  ];

  return (
    <section className="border border-[#d9d4e6] bg-white">
      <div className="flex flex-wrap items-start justify-between gap-3 border-b border-[#d9d4e6] bg-[#f8f6fc] px-4 py-3">
        <div className="flex items-start gap-2">
          <ListChecks className="mt-0.5 h-4 w-4 text-[#6f5ca8]" aria-hidden="true" />
          <div>
            <h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
            <p className="mt-1 max-w-3xl text-xs leading-5 text-[#5f5a70]">{t("subtitle")}</p>
          </div>
        </div>
        <span className="inline-flex border border-[#b7add8] bg-white px-2 py-0.5 text-xs font-semibold text-[#5b4b91]">
          {t("badge")}
        </span>
      </div>
      <div className="grid gap-px bg-[#d9d4e6] md:grid-cols-2 xl:grid-cols-6">
        {metrics.map((item) => (
          <div key={item.label} className="bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#5f5a70]">{item.label}</p>
            <p className="mt-2 font-mono text-2xl font-semibold text-[#141413]">{item.value}</p>
            <p className="mt-2 text-xs leading-5 text-[#4f4a60]">{item.detail}</p>
          </div>
        ))}
      </div>
      <div className="grid gap-4 p-4 xl:grid-cols-[1.1fr_0.9fr]">
        <div className="border border-[#d9d4e6] bg-white">
          <div className="border-b border-[#d9d4e6] bg-[#f8f6fc] px-4 py-3 text-xs font-semibold text-[#141413]">
            {t("reviewRefsTitle")}
          </div>
          <div className="divide-y divide-[#e8e3f2]">
            {reviewRefs.map((item) => (
              <div key={item.key} className="grid gap-3 px-4 py-3 md:grid-cols-[72px_1fr]">
                <span className="font-mono text-xs font-semibold text-[#141413]">{item.phase}</span>
                <div>
                  <p className="text-sm font-semibold text-[#141413]">
                    {t(`reviewRefs.${item.key}.title`)}
                  </p>
                  <p className="mt-1 text-xs leading-5 text-[#4f4a60]">
                    {t(`reviewRefs.${item.key}.detail`)}
                  </p>
                  <p className="mt-2 break-all font-mono text-[11px] text-[#5f5a70]">
                    {item.contract}
                  </p>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="border border-[#d9d4e6] bg-[#f8f6fc] px-4 py-3">
          <p className="text-xs font-semibold text-[#5f5a70]">{t("boundaryLabel")}</p>
          <p className="mt-2 text-sm font-semibold text-[#141413]">{t("boundaryTitle")}</p>
          <p className="mt-2 text-xs leading-5 text-[#4f4a60]">{t("boundaryDetail")}</p>
          <div className="mt-4 grid gap-2 break-all font-mono text-xs text-[#141413]">
            <span>owner_response_validation_received_count=0</span>
            <span>owner_response_validation_accepted_count=0</span>
            <span>owner_response_validation_rejected_count=0</span>
            <span>approval_record_created=false</span>
            <span>repo_creation_authorized=false</span>
            <span>refs_sync_authorized=false</span>
            <span>workflow_modification_authorized=false</span>
            <span>secret_value_collection_allowed=false</span>
            <span>github_primary_switch_authorized=false</span>
            <span>runtime_execution_authorized=false</span>
            <span>action_buttons_allowed=false</span>
          </div>
          <Link
            href="/iwooos"
            className="mt-4 inline-flex items-center gap-2 border border-[#b7add8] bg-white px-3 py-1.5 text-xs font-semibold text-[#141413] hover:border-[#6f5ca8]"
          >
            {t("openIwooos")}
            <ArrowRight className="h-3.5 w-3.5 text-[#5f5a70]" aria-hidden="true" />
          </Link>
        </div>
      </div>
    </section>
  );
}

function FocusedIncidentApprovalPanel({
  projectId,
  incidentId,
  chain,
  timeline,
  approvals,
  legacyApprovals,
  loading,
  error,
}: {
  projectId: string;
  incidentId: string;
  chain: AwoooPStatusChain | null;
  timeline: IncidentTimelineResponse | null;
  approvals: Approval[];
  legacyApprovals: LegacyApproval[];
  loading: boolean;
  error: string | null;
}) {
  const t = useTranslations("awooop.approvals.incidentFocus");
  const encodedProjectId = encodeURIComponent(projectId);
  const encodedIncidentId = encodeURIComponent(incidentId);
  const platformMatches = approvals.filter((approval) => approvalMatchesIncident(approval, incidentId));
  const legacyMatches = legacyApprovals.filter((approval) => approval.incident_id === incidentId);
  const timelineApprovalIds = timeline?.approval_ids ?? [];
  const linkedApprovalIds = uniqueValues([
    ...timelineApprovalIds,
    ...platformMatches.map((approval) => approval.run_id),
    ...legacyMatches.map((approval) => approval.id),
  ]);
  const stages = timeline?.timeline?.filter((stage) => stage.status !== "skipped") ?? [];
  const verifier = timeline?.timeline?.find((stage) => stage.stage === "verifier");
  const executor = timeline?.timeline?.find((stage) => stage.stage === "executor");
  const km = timeline?.timeline?.find((stage) => stage.stage === "km");
  const topMcpTool = chain?.mcp?.top_tools?.[0]?.tool_name ?? "--";
  const ansible = chain?.execution?.ansible;
  const outcome = chain?.operator_outcome;
  const needsHuman = chain?.needs_human ?? outcome?.needs_human ?? false;
  const title = timeline?.title ?? chain?.source_id ?? incidentId;
  const sourceCorrelation = chain?.source_refs?.correlation;

  return (
    <section className="border border-[#e0ddd4] bg-white" aria-busy={loading}>
      <div className="flex flex-wrap items-start justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
        <div className="flex min-w-0 items-start gap-3">
          <span className="flex h-9 w-9 shrink-0 items-center justify-center border border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]">
            <SearchCheck className="h-4 w-4" aria-hidden="true" />
          </span>
          <div className="min-w-0">
            <h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
            <p className="mt-1 truncate font-mono text-xs text-[#77736a]">{incidentId}</p>
            <p className="mt-1 truncate text-xs text-[#5f5b52]" title={title}>{title}</p>
          </div>
        </div>
        <div className="flex flex-wrap items-center gap-2">
          {loading ? (
            <span className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2 py-1 text-xs font-semibold text-[#5f5b52]">
              <RefreshCw className="h-3.5 w-3.5 animate-spin" aria-hidden="true" />
              {t("loading")}
            </span>
          ) : null}
          <Link
            href={`/authorizations?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}${
              linkedApprovalIds[0] ? `&approval_id=${encodeURIComponent(linkedApprovalIds[0])}` : ""
            }` as never}
            className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2.5 py-1 text-xs font-semibold text-[#141413] hover:border-[#1f6feb]"
          >
            {t("openAuthorizations")}
            <ArrowRight className="h-3.5 w-3.5" aria-hidden="true" />
          </Link>
          <Link
            href={`/awooop/work-items?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}` as never}
            className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2.5 py-1 text-xs font-semibold text-[#141413] hover:border-[#d97757]"
          >
            {t("openWorkItems")}
            <ArrowRight className="h-3.5 w-3.5" aria-hidden="true" />
          </Link>
          <Link
            href={`/awooop/runs?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}` as never}
            className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2.5 py-1 text-xs font-semibold text-[#141413] hover:border-[#1f6feb]"
          >
            {t("openRuns")}
            <ArrowRight className="h-3.5 w-3.5" aria-hidden="true" />
          </Link>
          <Link
            href={`/tickets?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}` as never}
            className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2.5 py-1 text-xs font-semibold text-[#141413] hover:border-[#2f7d72]"
          >
            {t("openTickets")}
            <ArrowRight className="h-3.5 w-3.5" aria-hidden="true" />
          </Link>
        </div>
      </div>

      {error ? (
        <div className="flex items-start gap-2 border-b border-[#ead9b4] bg-[#fff7e8] px-4 py-3 text-xs leading-5 text-[#8a5a08]">
          <TriangleAlert className="mt-0.5 h-4 w-4 shrink-0" aria-hidden="true" />
          {error}
        </div>
      ) : null}

      <div className="grid gap-px bg-[#e0ddd4] md:grid-cols-5">
        {[
          [t("metrics.approvals"), linkedApprovalIds.length || "--"],
          [t("metrics.stage"), chain?.current_stage ?? "--"],
          [t("metrics.repair"), chain?.repair_state ?? "--"],
          [t("metrics.verification"), verifier?.status ?? chain?.verification ?? "--"],
          [t("metrics.handoff"), needsHuman ? t("needsHuman.yes") : t("needsHuman.no")],
        ].map(([label, value]) => (
          <div key={label} className="min-w-0 bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#77736a]">{label}</p>
            <p className="mt-2 truncate font-mono text-sm font-semibold text-[#141413]" title={String(value)}>
              {value}
            </p>
          </div>
        ))}
      </div>

      <AwoooPStatusChainPanel chain={chain} className="border-x-0 border-t-0" />

      <div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-[1.1fr_0.9fr]">
        <div className="min-w-0 bg-white p-4">
          <div className="flex items-center gap-2">
            <GitBranch className="h-4 w-4 text-brand-accent" aria-hidden="true" />
            <h4 className="text-sm font-semibold text-[#141413]">{t("flowTitle")}</h4>
          </div>
          {timeline?.ascii_timeline ? (
            <p className="mt-3 break-words border border-[#eee9dd] bg-[#faf9f3] px-3 py-2 font-mono text-xs leading-6 text-[#5f5b52]">
              {timeline.ascii_timeline}
            </p>
          ) : (
            <p className="mt-3 text-sm text-[#77736a]">
              {loading ? t("loading") : t("timelineEmpty")}
            </p>
          )}
          {stages.length > 0 ? (
            <div className="mt-3 grid gap-2 md:grid-cols-2">
              {stages.slice(0, 6).map((stage) => (
                <div key={stage.stage} className="min-w-0 border border-[#eee9dd] bg-white px-3 py-2">
                  <div className="flex items-center justify-between gap-2">
                    <span className="truncate text-xs font-semibold text-[#77736a]">{stage.label}</span>
                    <span className={cn("shrink-0 border px-2 py-0.5 text-[11px] font-semibold", timelineStatusClass(stage.status))}>
                      {stage.status}
                    </span>
                  </div>
                  <p className="mt-1 truncate text-xs font-semibold text-[#141413]" title={stage.title}>
                    {stage.title}
                  </p>
                </div>
              ))}
            </div>
          ) : null}
        </div>

        <div className="min-w-0 bg-white p-4">
          <div className="flex items-center gap-2">
            <ListChecks className="h-4 w-4 text-brand-accent" aria-hidden="true" />
            <h4 className="text-sm font-semibold text-[#141413]">{t("handoffTitle")}</h4>
          </div>
          <div className="mt-3 grid gap-px border border-[#e0ddd4] bg-[#e0ddd4]">
            {[
              [t("handoff.approvalIds"), linkedApprovalIds.slice(0, 4).join(", ") || t("empty")],
              [t("handoff.pendingRows"), t("handoff.pendingRowsValue", { platform: platformMatches.length, legacy: legacyMatches.length })],
              [t("handoff.nextAction"), outcome?.next_action ?? chain?.next_step ?? "--"],
              [t("handoff.reason"), outcome?.human_action_reason ?? "--"],
            ].map(([label, value]) => (
              <div key={label} className="min-w-0 bg-white px-3 py-2">
                <p className="text-xs font-semibold text-[#77736a]">{label}</p>
                <p className="mt-1 truncate font-mono text-xs text-[#141413]" title={String(value)}>
                  {value}
                </p>
              </div>
            ))}
          </div>
          <p className="mt-3 text-xs leading-5 text-[#5f5b52]">
            {linkedApprovalIds.length > 0 ? t("linkedExplanation") : t("unlinkedExplanation")}
          </p>
        </div>
      </div>

      <div className="grid gap-px bg-[#e0ddd4] md:grid-cols-4">
        {[
          [t("evidence.executor"), executor?.title ?? chain?.execution?.latest_operation_type ?? "--"],
          [t("evidence.ansible"), ansible?.latest_playbook_path ?? ansible?.latest_catalog_id ?? "--"],
          [t("evidence.mcp"), t("evidence.mcpValue", {
            success: chain?.mcp?.gateway?.success ?? 0,
            total: chain?.mcp?.gateway?.total ?? 0,
            tool: topMcpTool,
          })],
          [t("evidence.source"), sourceCorrelation
            ? t("evidence.sourceValue", {
                direct: sourceCorrelation.direct_ref_total ?? 0,
                candidate: sourceCorrelation.candidate_total ?? 0,
                applied: sourceCorrelation.applied_link_total ?? 0,
              })
            : "--"],
          [t("evidence.km"), km?.title ?? String(chain?.evidence?.knowledge_entries ?? 0)],
          [t("evidence.command"), outcome?.execution_result?.summary_zh ?? "--"],
          [t("evidence.notification"), (outcome?.notification?.channels ?? []).join(", ") || "--"],
          [t("evidence.events"), timeline ? String(timeline.events.length) : "--"],
        ].map(([label, value]) => (
          <div key={label} className="min-w-0 bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#77736a]">{label}</p>
            <p className="mt-2 truncate font-mono text-xs text-[#141413]" title={String(value)}>
              {value}
            </p>
          </div>
        ))}
      </div>
    </section>
  );
}

// =============================================================================
// Main Component
// =============================================================================

export default function ApprovalsPage() {
  const t = useTranslations("awooop.approvals");
  const tEvidence = useTranslations("awooop.listEvidence");
  const tStatusChain = useTranslations("awooop.statusChain");
  const searchParams = useSearchParams();
  const queryIncidentId = searchParams.get("incident_id");
  const projectId = searchParams.get("project_id") ?? "awoooi";
  const [approvals, setApprovals] = useState<Approval[]>([]);
  const [legacyApprovals, setLegacyApprovals] = useState<LegacyApproval[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [legacyError, setLegacyError] = useState<string | null>(null);
  const [incidentChain, setIncidentChain] = useState<AwoooPStatusChain | null>(null);
  const [incidentTimeline, setIncidentTimeline] = useState<IncidentTimelineResponse | null>(null);
  const [incidentLoading, setIncidentLoading] = useState(false);
  const [incidentError, setIncidentError] = useState<string | null>(null);
  const [lastRefresh, setLastRefresh] = useState<Date | null>(null);
  const [evidenceFilter, setEvidenceFilter] = useState<"" | RemediationStatus>("");
  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);

  const fetchApprovals = useCallback(async () => {
    const fetchJson = async (url: string) => {
      const res = await fetch(url);
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      return res.json();
    };

    try {
      setError(null);
      setLegacyError(null);
      const params = new URLSearchParams();
      if (evidenceFilter) params.set("remediation_status", evidenceFilter);
      const qs = params.toString();
      const [platformResult, legacyResult] = await Promise.allSettled([
        fetchJson(`${API_BASE}/api/v1/platform/approvals${qs ? `?${qs}` : ""}`),
        fetchJson(`${API_BASE}/api/v1/approvals/pending`),
      ]);

      if (platformResult.status === "fulfilled") {
        const data = platformResult.value;
        setApprovals(Array.isArray(data.items) ? data.items : []);
      } else {
        setApprovals([]);
        setError(platformResult.reason instanceof Error ? platformResult.reason.message : t("page.genericLoadFailed"));
      }

      if (legacyResult.status === "fulfilled") {
        const data = legacyResult.value;
        const pending = Array.isArray(data) ? data : data.approvals ?? [];
        setLegacyApprovals(Array.isArray(pending) ? pending : []);
      } else {
        setLegacyApprovals([]);
        setLegacyError(legacyResult.reason instanceof Error ? legacyResult.reason.message : t("page.genericLoadFailed"));
      }

      setLastRefresh(new Date());
    } catch (err) {
      setError(err instanceof Error ? err.message : t("page.genericLoadFailed"));
    } finally {
      setLoading(false);
    }
  }, [evidenceFilter, t]);

  useEffect(() => {
    fetchApprovals();
  }, [fetchApprovals]);

  useEffect(() => {
    let cancelled = false;
    if (!queryIncidentId) {
      setIncidentChain(null);
      setIncidentTimeline(null);
      setIncidentError(null);
      setIncidentLoading(false);
      return () => {
        cancelled = true;
      };
    }
    const focusedIncidentId = queryIncidentId;

    async function loadIncidentFocus() {
      setIncidentLoading(true);
      setIncidentError(null);
      const encodedProjectId = encodeURIComponent(projectId);
      const encodedIncidentId = encodeURIComponent(focusedIncidentId);
      const [statusChain, timeline] = await Promise.all([
        fetchJson<AwoooPStatusChain>(
          `${API_BASE}/api/v1/platform/status-chain?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}`
        ),
        fetchJson<IncidentTimelineResponse>(
          `${API_BASE}/api/v1/incidents/${encodedIncidentId}/timeline`
        ),
      ]);
      if (cancelled) return;
      setIncidentChain(statusChain);
      setIncidentTimeline(timeline);
      if (!statusChain && !timeline) {
        setIncidentError(t("incidentFocus.loadFailed"));
      }
      setIncidentLoading(false);
    }

    loadIncidentFocus();
    return () => {
      cancelled = true;
    };
  }, [projectId, queryIncidentId, t]);

  // 10 秒自動刷新
  useEffect(() => {
    intervalRef.current = setInterval(() => {
      fetchApprovals();
    }, AUTO_REFRESH_INTERVAL);
    return () => {
      if (intervalRef.current) clearInterval(intervalRef.current);
    };
  }, [fetchApprovals]);

  const legacyPendingCount = legacyApprovals.length;
  const totalPendingCount = approvals.length + legacyPendingCount;
  const criticalCount = approvals.filter((a) => {
    const ms = getRemainingMs(a.timeout_at);
    return ms !== null && ms <= 5 * 60 * 1000;
  }).length;
  const expiredCount = approvals.filter((a) => {
    const ms = getRemainingMs(a.timeout_at);
    return ms !== null && ms <= 0;
  }).length;
  const readOnlyEvidenceCount = approvals.filter(
    (approval) => normalizeRemediationStatus(approval.remediation_summary) === "read_only_dry_run"
  ).length;
  const mcpObservedCount = approvals.filter(
    (approval) => normalizeRemediationStatus(approval.remediation_summary) === "mcp_observed"
  ).length;
  const noEvidenceCount = approvals.filter(
    (approval) => normalizeRemediationStatus(approval.remediation_summary) === "no_evidence"
  ).length;
  const queueSummary = useMemo(
    () => [
      {
        label: t("summary.pending"),
        value: totalPendingCount,
        detail: t("summary.pendingDetail", {
          platform: approvals.length,
          legacy: legacyPendingCount,
        }),
        icon: ShieldCheck,
        className: "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
      },
      {
        label: t("summary.critical"),
        value: criticalCount,
        detail: t("summary.criticalDetail"),
        icon: Clock,
        className: "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]",
      },
      {
        label: t("summary.expired"),
        value: expiredCount,
        detail: t("summary.expiredDetail"),
        icon: TriangleAlert,
        className: "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]",
      },
      {
        label: tEvidence("summary.mcpObserved"),
        value: mcpObservedCount,
        detail: tEvidence("summary.mcpObservedDetail"),
        icon: SearchCheck,
        className: "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]",
      },
      {
        label: tEvidence("summary.readOnly"),
        value: readOnlyEvidenceCount,
        detail: tEvidence("summary.approvalReadOnlyDetail"),
        icon: SearchCheck,
        className: "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]",
      },
      {
        label: tEvidence("summary.noEvidence"),
        value: noEvidenceCount,
        detail: tEvidence("summary.approvalNoEvidenceDetail"),
        icon: AlertCircle,
        className: "border-[#d8d3c7] bg-white text-[#5f5b52]",
      },
    ],
    [
      approvals.length,
      legacyPendingCount,
      criticalCount,
      expiredCount,
      mcpObservedCount,
      noEvidenceCount,
      readOnlyEvidenceCount,
      totalPendingCount,
      t,
      tEvidence,
    ]
  );

  return (
    <div className="space-y-6">
      {/* Page Header */}
      <div className="flex items-center justify-between flex-wrap gap-3">
        <div className="flex items-center gap-3">
          <ShieldCheck className="w-5 h-5 text-brand-accent" aria-hidden="true" />
          <div>
            <h2 className="text-lg font-semibold text-foreground flex items-center gap-2">
              {t("page.title")}
              {criticalCount > 0 && (
                <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-bold bg-red-600 text-white animate-pulse">
                  {t("page.urgentCount", { count: criticalCount })}
                </span>
              )}
            </h2>
            <p className="text-xs text-muted-foreground">
              {loading
                ? t("page.loading")
                : t("page.pendingRefresh", {
                    count: approvals.length,
                    time: lastRefresh ? lastRefresh.toLocaleTimeString("zh-TW") : "--",
                  })}
            </p>
          </div>
        </div>
        <div className="flex items-center gap-2">
          <span className="text-xs text-muted-foreground">{t("page.autoRefresh")}</span>
          <button
            onClick={() => { setLoading(true); fetchApprovals(); }}
            disabled={loading}
            className="flex items-center gap-2 rounded-button px-3 py-2 text-sm text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:opacity-50"
            aria-label={t("page.refreshNow")}
          >
            <RefreshCw className={cn("w-4 h-4", loading && "animate-spin")} aria-hidden="true" />
            {t("page.refreshNow")}
          </button>
        </div>
      </div>

      <section className="grid gap-px border border-[#e0ddd4] bg-[#e0ddd4] md:grid-cols-2 xl:grid-cols-6">
        {queueSummary.map((item) => {
          const Icon = item.icon;
          return (
            <div key={item.label} className="bg-white px-4 py-3">
              <div className="flex items-start justify-between gap-3">
                <div>
                  <p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
                  <div className="mt-2 font-mono text-2xl font-semibold text-[#141413]">
                    {item.value}
                  </div>
                </div>
                <span
                  className={cn(
                    "flex h-8 w-8 items-center justify-center border",
                    item.className
                  )}
                >
                  <Icon className="h-4 w-4" aria-hidden="true" />
                </span>
              </div>
              <p className="mt-2 text-xs leading-5 text-[#5f5b52]">{item.detail}</p>
            </div>
          );
        })}
      </section>

      {queryIncidentId ? (
        <FocusedIncidentApprovalPanel
          projectId={projectId}
          incidentId={queryIncidentId}
          chain={incidentChain}
          timeline={incidentTimeline}
          approvals={approvals}
          legacyApprovals={legacyApprovals}
          loading={incidentLoading}
          error={incidentError}
        />
      ) : null}
      <SecurityOwnerResponseGatePanel />
      <GitHubPrimaryReadinessApprovalBoundaryPanel />
      <OwnerResponseValidationApprovalBoundaryPanel />
      <LegacyHitlBacklogPanel
        approvals={legacyApprovals}
        loading={loading}
        error={legacyError}
      />

      <div className="flex flex-wrap items-center gap-3 border border-[#e0ddd4] bg-white p-4">
        <Filter className="w-4 h-4 text-muted-foreground flex-shrink-0" aria-hidden="true" />
        <span className="text-sm text-muted-foreground">{tEvidence("filters.label")}</span>
        <div className="relative">
          <select
            value={evidenceFilter}
            onChange={(e) => setEvidenceFilter(e.target.value as "" | RemediationStatus)}
            className="appearance-none rounded-button border border-border bg-background py-1.5 pl-3 pr-8 text-sm text-foreground cursor-pointer focus:outline-none focus:ring-2 focus:ring-brand-accent/50"
            aria-label={tEvidence("filters.label")}
          >
            <option value="">{tEvidence("filters.all")}</option>
            {REMEDIATION_FILTER_OPTIONS.map((status) => (
              <option key={status} value={status}>
                {tEvidence(REMEDIATION_STATUS_CONFIG[status].labelKey)}
              </option>
            ))}
          </select>
          <ChevronDown className="absolute right-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-muted-foreground pointer-events-none" aria-hidden="true" />
        </div>
      </div>

      {/* Error State */}
      {error && (
        <div className="flex items-start gap-3 border border-[#e2a29b] bg-[#fff0ef] p-4">
          <AlertCircle className="w-5 h-5 text-red-400 flex-shrink-0 mt-0.5" aria-hidden="true" />
          <div>
            <p className="text-sm font-medium text-red-300">{t("page.loadFailed")}</p>
            <p className="text-xs text-red-400 mt-1">{error}</p>
          </div>
        </div>
      )}

      {/* Empty State — 所有審批已處理 */}
      {!loading && approvals.length === 0 && !error && (
        <div className="flex flex-col items-center justify-center border border-[#e0ddd4] bg-white py-16">
          <ShieldCheck className="w-12 h-12 text-green-400 mb-3" aria-hidden="true" />
          <p className="text-sm font-medium text-foreground mb-1">{t("empty.title")}</p>
          <p className="text-xs text-muted-foreground">{t("empty.subtitle")}</p>
        </div>
      )}

      {/* Table */}
      {(loading || approvals.length > 0) && (
        <div className="overflow-hidden border border-[#e0ddd4] bg-white shadow-[0_1px_4px_rgba(0,0,0,0.05)]">
          <div className="overflow-x-auto">
            <table className="w-full" role="table" aria-label={t("page.title")}>
              <thead>
                <tr className="border-b border-border bg-muted/50">
                  <th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    {t("columns.runId")}
                  </th>
                  <th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    {t("columns.projectId")}
                  </th>
                  <th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    {t("columns.agent")}
                  </th>
                  <th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    {t("columns.route")}
                  </th>
                  <th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    {tEvidence("column")}
                  </th>
                  <th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    {tEvidence("sourceFlow.column")}
                  </th>
                  <th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    {tStatusChain("title")}
                  </th>
                  <th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    {t("columns.created")}
                  </th>
                  <th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    {t("columns.remaining")}
                  </th>
                </tr>
              </thead>
              <tbody>
                {loading ? (
                  Array.from({ length: 5 }).map((_, i) => (
                    <tr key={i} className="border-b border-border">
                      {Array.from({ length: 9 }).map((_, j) => (
                        <td key={j} className="px-4 py-3">
                          <div className="h-5 bg-muted animate-pulse rounded w-20" />
                        </td>
                      ))}
                    </tr>
                  ))
                ) : (
                  approvals.map((approval) => (
                    <ApprovalRow key={approval.run_id} approval={approval} />
                  ))
                )}
              </tbody>
            </table>
          </div>
        </div>
      )}
    </div>
  );
}
