// =============================================================================
// WOOO AIOps - AwoooP Run Detail / Timeline
// =============================================================================
// 將 Run FSM、Channel Event、MCP Audit、出站訊息收斂成同一條處置脈絡。

"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import { useSearchParams } from "next/navigation";
import { useLocale, useTranslations } from "next-intl";
import {
  Activity,
  AlertCircle,
  ArrowLeft,
  ArrowRight,
  BookOpenCheck,
  CheckCircle2,
  Clock,
  FileSearch,
  Link2,
  MessageSquareText,
  RefreshCw,
  Route,
  SearchCheck,
  Send,
  ShieldAlert,
  ShieldCheck,
  TriangleAlert,
  Wrench,
} from "lucide-react";

import { Link } from "@/i18n/routing";
import { IncidentEvidenceHeader } from "@/components/awooop/incident-evidence-header";
import {
  AwoooPStatusChainPanel,
  type AwoooPStatusChain,
} from "@/components/awooop/status-chain";
import { cn } from "@/lib/utils";

interface RunDetail {
  run_id: string;
  project_id: string;
  agent_id: string;
  state: string;
  is_shadow: boolean;
  trace_id?: string | null;
  trigger_type?: string | null;
  trigger_ref?: string | null;
  cost_usd: number | string;
  step_count: number;
  attempt_count: number;
  max_attempts: number;
  error_code?: string | null;
  error_detail?: string | null;
  created_at: string;
  started_at?: string | null;
  completed_at?: string | null;
  timeout_at?: string | null;
  heartbeat_at?: string | null;
}

interface TimelineItem {
  ts: string | null;
  kind: "run" | "inbound" | "outbound" | "step" | "mcp" | string;
  title: string;
  status: string;
  summary?: string | null;
  metadata?: Record<string, unknown>;
}

interface ChannelEventDossierItem {
  event_id: string;
  project_id: string;
  channel_type: string;
  provider?: string | null;
  stage: string;
  provider_event_id: string;
  content_preview?: string | null;
  content_redacted?: string | null;
  has_redacted_content: boolean;
  redaction_version?: string | null;
  source_url?: string | null;
  content_sha256?: string | null;
  content_length?: number | null;
  source_refs: Record<string, unknown>;
  source_ref_count: number;
  log_correlation: Record<string, unknown>;
  alertname?: string | null;
  severity?: string | null;
  namespace?: string | null;
  target_resource?: string | null;
  fingerprint?: string | null;
  is_duplicate: boolean;
  provider_ts?: string | null;
  received_at: string;
}

interface ChannelEventDossierResponse {
  events: ChannelEventDossierItem[];
  summary: {
    source_count: number;
    duplicate_total: number;
    redacted_total: number;
    source_ref_total: number;
  };
}

interface McpGatewayBucket {
  agent_id?: string;
  tool_name?: string;
  required_scope?: string;
  total: number;
  success: number;
  failed: number;
  blocked: number;
}

interface McpGatewaySummary {
  total: number;
  success: number;
  failed: number;
  blocked: number;
  first_class_total: number;
  legacy_bridge_total: number;
  policy_enforced_total: number;
  approval_executor_total: number;
  stage: string;
  stage_status: string;
  needs_human: boolean;
  blockers: string[];
  by_agent: McpGatewayBucket[];
  by_tool: McpGatewayBucket[];
  by_scope: McpGatewayBucket[];
}

interface LegacyMcpToolBucket {
  mcp_server?: string | null;
  tool_name?: string | null;
  success: number;
  failed: number;
  last_error?: string | null;
}

interface LegacyMcpSummary {
  total: number;
  success: number;
  failed: number;
  by_tool: LegacyMcpToolBucket[];
}

interface LegacyMcpEvidence {
  schema_version?: string;
  source?: string;
  incident_ids?: string[];
  total?: number;
  limit?: number;
  summary?: LegacyMcpSummary;
}

interface RemediationHistoryItem {
  id?: string;
  incident_id?: string | null;
  work_item_id?: string | null;
  mode?: string | null;
  safety_level?: string | null;
  verification_result_preview?: string | null;
  tool_count?: number | null;
  tools?: string[];
  agent_id?: string | null;
  tool_name?: string | null;
  required_scope?: string | null;
  writes_incident_state?: boolean | null;
  writes_auto_repair_result?: boolean | null;
  created_at?: string | null;
  checks?: Array<{ name?: string; passed?: boolean; detail?: string }>;
}

type OwnerResponseValidationDetailRef = {
  phase: string;
  key: string;
  contract: string;
};

interface RunRemediationHistory {
  schema_version?: string;
  source?: string;
  incident_ids?: string[];
  total?: number;
  limit?: number;
  items?: RemediationHistoryItem[];
  by_work_item?: Array<{
    work_item_id?: string | null;
    incident_id?: string | null;
    count?: number;
    latest_at?: string | null;
    latest_preview?: string | null;
    latest_mode?: string | null;
    latest_route?: string | null;
  }>;
  errors?: Array<{ incident_id?: string; error?: string }>;
}

interface RunDetailResponse {
  run: RunDetail;
  timeline: TimelineItem[];
  mcp_gateway?: McpGatewaySummary;
  mcp_legacy?: LegacyMcpEvidence;
  remediation_history?: RunRemediationHistory;
  awooop_status_chain?: AwoooPStatusChain | null;
  counts: {
    steps: number;
    inbound_events: number;
    outbound_messages: number;
    mcp_calls: number;
    legacy_mcp_calls?: number;
    remediation_history?: number;
    timeline: number;
  };
}

interface IncidentTimelineEvent {
  stage: string;
  status: string;
  title: string;
  description?: string | null;
  actor?: string | null;
  timestamp?: string | null;
  source_table?: string | null;
  data?: Record<string, unknown>;
}

interface IncidentTimelineStage extends IncidentTimelineEvent {
  label: string;
  events?: IncidentTimelineEvent[];
}

interface IncidentTimelineResponse {
  incident_id: string;
  title: string;
  status: string;
  severity: string;
  started_at?: string | null;
  updated_at?: string | null;
  resolved_at?: string | null;
  affected_services?: string[];
  approval_ids?: string[];
  timeline: IncidentTimelineStage[];
  events: IncidentTimelineEvent[];
  ascii_timeline: string;
  reconciliation?: Record<string, unknown>;
}

const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "";
const AUTO_REFRESH_INTERVAL = 30_000;

const ownerResponseValidationDetailRefs: OwnerResponseValidationDetailRef[] = [
  {
    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",
  },
];

const ownerResponseValidationDetailBoundaries = [
  "owner_response_validation_received_count=0",
  "owner_response_validation_accepted_count=0",
  "owner_response_validation_rejected_count=0",
  "run_detail_owner_response_linked=false",
  "run_detail_approval_record_created=false",
  "mcp_execution_authorized=false",
  "remediation_execution_authorized=false",
  "platform_run_creation_authorized=false",
  "execution_router_linked=false",
  "repo_creation_authorized=false",
  "refs_sync_authorized=false",
  "workflow_modification_authorized=false",
  "secret_value_collection_allowed=false",
  "github_primary_switch_authorized=false",
  "runtime_execution_authorized=false",
  "action_buttons_allowed=false",
];

const STATUS_STYLE: Record<string, string> = {
  completed: "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]",
  success: "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]",
  sent: "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]",
  callback_reply_sent: "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]",
  callback_reply_fallback_sent: "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
  callback_reply_rescue_sent: "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
  callback_reply_failed: "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]",
  info: "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]",
  running: "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]",
  received: "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]",
  skipped: "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]",
  waiting_approval: "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
  pending: "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
  shadow: "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]",
  failed: "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]",
  error: "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]",
  blocked: "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]",
  cancelled: "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]",
  timeout: "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]",
  warning: "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
};

const STATUS_TRANSLATION_KEYS: Record<string, string> = {
  blocked: "statuses.blocked",
  cancelled: "statuses.cancelled",
  completed: "statuses.completed",
  error: "statuses.error",
  failed: "statuses.failed",
  info: "statuses.info",
  pending: "statuses.pending",
  received: "statuses.received",
  running: "statuses.running",
  sent: "statuses.sent",
  callback_reply_sent: "statuses.callbackReplySent",
  callback_reply_fallback_sent: "statuses.callbackReplyFallbackSent",
  callback_reply_rescue_sent: "statuses.callbackReplyRescueSent",
  callback_reply_failed: "statuses.callbackReplyFailed",
  shadow: "statuses.shadow",
  skipped: "statuses.skipped",
  success: "statuses.success",
  timeout: "statuses.timeout",
  waiting_approval: "statuses.waitingApproval",
};

const SOURCE_REF_LABEL_KEYS: Record<string, string> = {
  alert_ids: "refs.alertIds",
  approval_ids: "refs.approvalIds",
  event_ids: "refs.eventIds",
  fingerprints: "refs.fingerprints",
  incident_ids: "refs.incidentIds",
  sentry_issue_ids: "refs.sentryIssueIds",
  signoz_alerts: "refs.signozAlerts",
};

const MANUAL_STATES = new Set(["blocked", "cancelled", "error", "failed", "timeout"]);

function formatTime(value: string | null | undefined, locale: string, emptyLabel: string) {
  if (!value) return emptyLabel;
  return new Date(value).toLocaleString(locale === "zh-TW" ? "zh-TW" : "en-US", {
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
  });
}

function statusClass(status: string) {
  return STATUS_STYLE[status] ?? "border-[#d8d3c7] bg-white text-[#5f5b52]";
}

function itemIcon(kind: string) {
  if (kind === "approval") return ShieldCheck;
  if (kind === "inbound") return MessageSquareText;
  if (kind === "outbound") return Send;
  if (kind === "step") return Wrench;
  if (kind === "mcp") return ShieldAlert;
  if (kind === "remediation") return SearchCheck;
  return Route;
}

function firstSourceRefs(sourceRefs: Record<string, unknown>, limit = 6) {
  const refs: Array<{ key: string; value: string }> = [];
  Object.entries(sourceRefs).forEach(([key, value]) => {
    const values = Array.isArray(value) ? value : value ? [value] : [];
    values.slice(0, 3).forEach((entry) => {
      refs.push({ key, value: String(entry) });
    });
  });
  return refs.slice(0, limit);
}

function shortDigest(value?: string | null) {
  if (!value) return null;
  return value.length > 16 ? `${value.slice(0, 12)}...${value.slice(-4)}` : value;
}

function remediationRoute(item?: RemediationHistoryItem | null) {
  if (!item) return "--";
  return [item.agent_id, item.tool_name, item.required_scope].filter(Boolean).join("/") || "--";
}

function booleanLabel(value: boolean | null | undefined, emptyLabel: string) {
  if (value === true) return "true";
  if (value === false) return "false";
  return emptyLabel;
}

function metadataValue(data: Record<string, unknown> | undefined, keys: string[]) {
  if (!data) return null;
  for (const key of keys) {
    const value = data[key];
    if (value !== null && value !== undefined && value !== "") return String(value);
  }
  return null;
}

function RunActionPanel({
  run,
  counts,
  emptyLabel,
}: {
  run?: RunDetail;
  counts?: RunDetailResponse["counts"];
  emptyLabel: string;
}) {
  const t = useTranslations("runDetail.action");

  if (!run) {
    return (
      <section className="border border-[#e0ddd4] bg-white p-4">
        <div className="h-5 w-40 animate-pulse bg-[#f2efe6]" />
        <div className="mt-3 h-4 w-full max-w-xl animate-pulse bg-[#f2efe6]" />
      </section>
    );
  }

  const isApproval = run.state === "waiting_approval";
  const isManual = MANUAL_STATES.has(run.state);
  const isCompleted = run.state === "completed" || run.state === "success";
  const actionKey = isApproval
    ? "approval"
    : isManual
    ? "manual"
    : isCompleted
    ? "completed"
    : run.state === "running"
    ? "running"
    : "observe";
  const Icon = isApproval
    ? ShieldCheck
    : isManual
    ? TriangleAlert
    : isCompleted
    ? CheckCircle2
    : run.state === "running"
    ? Activity
    : SearchCheck;
  const toneClass = isApproval
    ? "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]"
    : isManual
    ? "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]"
    : isCompleted
    ? "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]"
    : "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]";
  const projectParam = encodeURIComponent(run.project_id);
  const primaryHref = isApproval
    ? (`/awooop/approvals/${run.run_id}?project_id=${projectParam}` as never)
    : (`/awooop/runs?project_id=${projectParam}` as never);

  const evidence = [
    { label: t("evidence.inbound"), value: counts?.inbound_events ?? 0 },
    { label: t("evidence.outbound"), value: counts?.outbound_messages ?? 0 },
    { label: t("evidence.mcp"), value: (counts?.mcp_calls ?? 0) + (counts?.legacy_mcp_calls ?? 0) },
    { label: t("evidence.steps"), value: counts?.steps ?? 0 },
  ];

  return (
    <section className="grid gap-px border border-[#e0ddd4] bg-[#e0ddd4] lg:grid-cols-[1.1fr_0.9fr]">
      <div className="bg-white p-4">
        <div className="flex flex-wrap items-start justify-between gap-4">
          <div className="min-w-0">
            <div className="flex items-center gap-2">
              <span className={cn("flex h-8 w-8 items-center justify-center border", toneClass)}>
                <Icon className="h-4 w-4" aria-hidden="true" />
              </span>
              <div>
                <p className="text-xs font-semibold text-[#77736a]">{t("eyebrow")}</p>
                <h3 className="text-base font-semibold text-[#141413]">
                  {t(`${actionKey}.title` as never)}
                </h3>
              </div>
            </div>
            <p className="mt-3 max-w-3xl text-sm leading-6 text-[#5f5b52]">
              {t(`${actionKey}.detail` as never)}
            </p>
          </div>
          <Link
            href={primaryHref}
            className={cn(
              "inline-flex shrink-0 items-center gap-2 border px-3 py-2 text-sm font-semibold",
              toneClass
            )}
          >
            {t(`${actionKey}.primary` as never)}
            <ArrowRight className="h-4 w-4" aria-hidden="true" />
          </Link>
        </div>
      </div>
      <div className="grid grid-cols-2 gap-px bg-[#e0ddd4] md:grid-cols-4 lg:grid-cols-2 xl:grid-cols-4">
        {evidence.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 ?? emptyLabel}
            </p>
          </div>
        ))}
      </div>
    </section>
  );
}

function OwnerResponseValidationDetailBoundaryPanel() {
  const t = useTranslations("awooop.runDetail.ownerResponseValidationDetailBoundary");
  const metrics = [
    { label: t("metrics.packets.label"), value: "4", detail: t("metrics.packets.detail") },
    { label: t("metrics.templates.label"), value: "22", detail: t("metrics.templates.detail") },
    { label: t("metrics.received.label"), value: "0", detail: t("metrics.received.detail") },
    { label: t("metrics.accepted.label"), value: "0", detail: t("metrics.accepted.detail") },
    { label: t("metrics.validationRuns.label"), value: "0", detail: t("metrics.validationRuns.detail") },
    { label: t("metrics.displaySections.label"), value: "8", detail: t("metrics.displaySections.detail") },
  ];

  return (
    <section className="border border-[#d7c9f0] bg-[#fbf8ff]">
      <div className="flex flex-wrap items-start justify-between gap-4 border-b border-[#d7c9f0] px-4 py-4">
        <div className="max-w-4xl">
          <div className="flex items-center gap-2">
            <ShieldCheck className="h-4 w-4 text-[#6d48a8]" aria-hidden="true" />
            <h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
          </div>
          <p className="mt-2 text-sm leading-6 text-[#5f5b52]">{t("subtitle")}</p>
        </div>
        <div className="flex flex-wrap items-center gap-2">
          <span className="border border-[#bda9df] bg-white px-2 py-1 text-xs font-semibold text-[#5c3b91]">
            {t("badge")}
          </span>
          <Link
            href="/iwooos"
            className="inline-flex items-center gap-2 border border-[#bda9df] bg-white px-3 py-2 text-sm font-semibold text-[#141413] hover:border-[#6d48a8]"
          >
            {t("openIwooos")}
            <ArrowRight className="h-4 w-4" aria-hidden="true" />
          </Link>
        </div>
      </div>

      <div className="grid gap-px bg-[#d7c9f0] md:grid-cols-3 xl:grid-cols-6">
        {metrics.map((item) => (
          <div key={item.label} className="min-w-0 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-px bg-[#d7c9f0] lg:grid-cols-[1fr_1fr]">
        <div className="min-w-0 bg-white p-4">
          <h4 className="text-sm font-semibold text-[#141413]">{t("detailRefsTitle")}</h4>
          <div className="mt-3 divide-y divide-[#eee9dd] border border-[#eee9dd]">
            {ownerResponseValidationDetailRefs.map((item) => (
              <div key={item.contract} className="grid gap-2 px-3 py-3 md:grid-cols-[72px_1fr]">
                <span className="font-mono text-xs font-semibold text-[#5c3b91]">{item.phase}</span>
                <div className="min-w-0">
                  <p className="text-sm font-semibold text-[#141413]">
                    {t(`detailRefs.${item.key}.title` as never)}
                  </p>
                  <p className="mt-1 text-xs leading-5 text-[#5f5b52]">
                    {t(`detailRefs.${item.key}.detail` as never)}
                  </p>
                  <p className="mt-2 break-all font-mono text-xs text-[#77736a]">{item.contract}</p>
                </div>
              </div>
            ))}
          </div>
        </div>

        <div className="min-w-0 bg-[#f7f0ff] p-4">
          <div className="flex items-center gap-2">
            <ShieldAlert className="h-4 w-4 text-[#6d48a8]" aria-hidden="true" />
            <span className="text-xs font-semibold text-[#5c3b91]">{t("boundaryLabel")}</span>
          </div>
          <h4 className="mt-2 text-base font-semibold text-[#141413]">{t("boundaryTitle")}</h4>
          <p className="mt-2 text-sm leading-6 text-[#5f5b52]">{t("boundaryDetail")}</p>
          <div className="mt-4 space-y-1 font-mono text-xs leading-6 text-[#141413]">
            {ownerResponseValidationDetailBoundaries.map((item) => (
              <div key={item} className="break-all">
                {item}
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}

function EventDossierPanel({
  dossier,
  locale,
  emptyLabel,
}: {
  dossier?: ChannelEventDossierResponse | null;
  locale: string;
  emptyLabel: string;
}) {
  const t = useTranslations("runDetail.dossier");
  const events = dossier?.events ?? [];
  const summary = dossier?.summary;
  const metrics = [
    { label: t("metrics.sources"), value: summary?.source_count ?? 0 },
    { label: t("metrics.refs"), value: summary?.source_ref_total ?? 0 },
    { label: t("metrics.redacted"), value: summary?.redacted_total ?? 0 },
    { label: t("metrics.duplicates"), value: summary?.duplicate_total ?? 0 },
  ];

  return (
    <section className="border border-[#e0ddd4] bg-white">
      <div className="flex flex-wrap items-center justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
        <div className="flex items-center gap-2">
          <FileSearch className="h-4 w-4 text-brand-accent" aria-hidden="true" />
          <h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
        </div>
        <span className={cn("border px-2 py-0.5 text-xs font-semibold", events.length > 0 ? statusClass("received") : statusClass("pending"))}>
          {events.length > 0 ? t("status.visible") : t("status.empty")}
        </span>
      </div>
      <div className="grid gap-px bg-[#e0ddd4] md: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>
          </div>
        ))}
      </div>
      {events.length === 0 ? (
        <div className="px-4 py-8 text-sm text-[#77736a]">{t("empty")}</div>
      ) : (
        <div className="divide-y divide-[#eee9dd]">
          {events.map((event) => {
            const refs = firstSourceRefs(event.source_refs);
            const digest = shortDigest(event.content_sha256);
            return (
              <article key={event.event_id} className="grid gap-4 px-4 py-4 xl:grid-cols-[240px_1fr]">
                <div className="space-y-3">
                  <div>
                    <div className="flex flex-wrap items-center gap-2">
                      <span className="border border-[#d8d3c7] bg-[#faf9f3] px-2 py-0.5 font-mono text-xs font-semibold text-[#5f5b52]">
                        {event.provider ?? event.channel_type}
                      </span>
                      <span className={cn("border px-2 py-0.5 text-xs font-semibold", statusClass(event.is_duplicate ? "pending" : "received"))}>
                        {event.is_duplicate ? t("duplicate") : t("firstSeen")}
                      </span>
                    </div>
                    <h4 className="mt-2 text-sm font-semibold text-[#141413]">
                      {event.alertname || event.provider_event_id}
                    </h4>
                    <p className="mt-1 font-mono text-xs text-[#77736a]">
                      {formatTime(event.received_at, locale, emptyLabel)}
                    </p>
                  </div>
                  <div className="space-y-2 text-xs">
                    <DetailLine label={t("fields.stage")} value={event.stage} emptyLabel={emptyLabel} />
                    <DetailLine label={t("fields.severity")} value={event.severity} emptyLabel={emptyLabel} />
                    <DetailLine label={t("fields.namespace")} value={event.namespace} emptyLabel={emptyLabel} />
                    <DetailLine label={t("fields.target")} value={event.target_resource} emptyLabel={emptyLabel} />
                    <DetailLine label={t("fields.hash")} value={digest} emptyLabel={emptyLabel} />
                  </div>
                </div>
                <div className="min-w-0 space-y-3">
                  <div className="rounded-none border border-[#eee9dd] bg-[#faf9f3] p-3">
                    <div className="mb-2 text-xs font-semibold text-[#77736a]">{t("content")}</div>
                    <p className="max-h-28 overflow-hidden whitespace-pre-line break-words text-sm leading-6 text-[#5f5b52]">
                      {event.content_redacted || event.content_preview || emptyLabel}
                    </p>
                  </div>
                  <div>
                    <div className="mb-2 text-xs font-semibold text-[#77736a]">{t("sourceRefs")}</div>
                    {refs.length > 0 ? (
                      <div className="flex flex-wrap gap-2">
                        {refs.map((ref) => {
                          const labelKey = SOURCE_REF_LABEL_KEYS[ref.key];
                          const label = labelKey ? t(labelKey as never) : ref.key;
                          return (
                            <span key={`${ref.key}:${ref.value}`} className="inline-flex max-w-full items-center gap-1 border border-[#d8d3c7] bg-white px-2 py-1 text-xs text-[#5f5b52]">
                              <span className="shrink-0 font-semibold">{label}</span>
                              <span className="truncate font-mono">{ref.value}</span>
                            </span>
                          );
                        })}
                      </div>
                    ) : (
                      <p className="text-sm text-[#77736a]">{emptyLabel}</p>
                    )}
                  </div>
                  {event.source_url && (
                    <a
                      href={event.source_url}
                      target="_blank"
                      rel="noreferrer"
                      className="inline-flex max-w-full items-center gap-2 border border-[#d8d3c7] bg-white px-3 py-2 text-sm text-[#5f5b52] hover:border-[#d97757]"
                    >
                      <Link2 className="h-4 w-4 shrink-0" aria-hidden="true" />
                      <span className="truncate">{event.source_url}</span>
                    </a>
                  )}
                </div>
              </article>
            );
          })}
        </div>
      )}
    </section>
  );
}

function DetailLine({
  label,
  value,
  emptyLabel,
}: {
  label: string;
  value?: string | number | null;
  emptyLabel: string;
}) {
  return (
    <div className="grid grid-cols-[84px_1fr] gap-2">
      <span className="font-semibold text-[#77736a]">{label}</span>
      <span className="truncate font-mono text-[#141413]">{value ?? emptyLabel}</span>
    </div>
  );
}

function DetailField({
  label,
  value,
  emptyLabel,
}: {
  label: string;
  value?: string | number | null;
  emptyLabel: string;
}) {
  return (
    <div className="border-b border-[#eee9dd] py-3 last:border-0">
      <div className="text-xs font-semibold uppercase text-[#77736a]">{label}</div>
      <div className="mt-1 break-words font-mono text-sm text-[#141413]">
        {value ?? emptyLabel}
      </div>
    </div>
  );
}

function topBucket(
  buckets: McpGatewayBucket[] | undefined,
  field: "agent_id" | "tool_name" | "required_scope"
) {
  if (!buckets || buckets.length === 0) return null;
  return [...buckets].sort((a, b) => b.total - a.total)[0]?.[field] ?? null;
}

function topLegacyTool(summary?: LegacyMcpSummary | null) {
  const top = summary?.by_tool
    ? [...summary.by_tool].sort((a, b) => (b.success + b.failed) - (a.success + a.failed))[0]
    : null;
  if (!top) return null;
  return [top.mcp_server, top.tool_name].filter(Boolean).join("/") || null;
}

function McpGatewayPanel({
  summary,
  legacy,
  emptyLabel,
  statusLabel,
}: {
  summary?: McpGatewaySummary;
  legacy?: LegacyMcpEvidence;
  emptyLabel: string;
  statusLabel: (status: string) => string;
}) {
  const t = useTranslations("runDetail.gateway");
  const hasRecords = Boolean(summary && summary.total > 0);
  const legacyTotal = legacy?.total ?? 0;
  const hasAnyRecords = hasRecords || legacyTotal > 0;
  const legacySummary = legacy?.summary;
  const legacyTool = topLegacyTool(legacySummary);
  const toneClass = summary?.needs_human
    ? "border-[#e2a29b] bg-[#fff0ef] text-[#9f2f25]"
    : summary?.stage_status === "success"
    ? "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]"
    : "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]";
  const agent = topBucket(summary?.by_agent, "agent_id");
  const tool = topBucket(summary?.by_tool, "tool_name");
  const scope = topBucket(summary?.by_scope, "required_scope");
  const metrics = [
    { label: t("metrics.firstClass"), value: summary?.first_class_total ?? 0 },
    { label: t("metrics.policy"), value: summary?.policy_enforced_total ?? 0 },
    { label: t("metrics.approvalExecutor"), value: summary?.approval_executor_total ?? 0 },
    { label: t("metrics.legacyBridge"), value: summary?.legacy_bridge_total ?? 0 },
  ];
  const gatewayTools = summary?.by_tool?.slice(0, 4) ?? [];
  const legacyTools = legacySummary?.by_tool?.slice(0, 4) ?? [];

  return (
    <section className="border border-[#e0ddd4] bg-white">
      <div className="flex flex-wrap items-center justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
        <div className="flex items-center gap-2">
          <ShieldCheck className="h-4 w-4 text-brand-accent" aria-hidden="true" />
          <h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
        </div>
        <span className={cn("border px-2 py-0.5 text-xs font-semibold", hasAnyRecords ? toneClass : statusClass("pending"))}>
          {hasRecords ? statusLabel(summary?.stage_status ?? "pending") : legacyTotal > 0 ? t("legacy.only") : t("emptyState")}
        </span>
      </div>
      <div className="grid gap-px bg-[#e0ddd4] md: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]">
              {hasRecords ? item.value : emptyLabel}
            </p>
          </div>
        ))}
      </div>
      <div className="grid gap-px bg-[#e0ddd4] md:grid-cols-3">
        {[
          { label: t("agent"), value: agent },
          { label: t("tool"), value: tool },
          { label: t("scope"), value: scope },
        ].map((item) => (
          <div key={item.label} className="min-w-0 bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
            <p className="mt-1 truncate font-mono text-sm text-[#141413]">
              {item.value ?? emptyLabel}
            </p>
          </div>
        ))}
      </div>
      {hasRecords && summary?.blockers && summary.blockers.length > 0 && (
        <div className="border-t border-[#eee9dd] bg-[#fff0ef] px-4 py-3 text-sm text-[#9f2f25]">
          <span className="font-semibold">{t("blockers")}</span>{" "}
          <span className="font-mono text-xs">{summary.blockers.slice(0, 3).join(", ")}</span>
        </div>
      )}
      {legacyTotal > 0 && (
        <div className="grid gap-px border-t border-[#e0ddd4] bg-[#e0ddd4] md:grid-cols-4">
          {[
            { label: t("legacy.total"), value: legacyTotal },
            { label: t("legacy.success"), value: legacySummary?.success ?? 0 },
            { label: t("legacy.failed"), value: legacySummary?.failed ?? 0 },
            { label: t("legacy.topTool"), value: legacyTool ?? emptyLabel },
          ].map((item) => (
            <div key={item.label} className="min-w-0 bg-white px-4 py-3">
              <p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
              <p className="mt-1 truncate font-mono text-sm text-[#141413]">{item.value}</p>
            </div>
          ))}
        </div>
      )}
      <div className="grid gap-px border-t border-[#e0ddd4] bg-[#e0ddd4] lg:grid-cols-2">
        <div className="min-w-0 bg-white p-4">
          <div className="flex items-center justify-between gap-3">
            <h4 className="text-sm font-semibold text-[#141413]">{t("evidence.firstClassTitle")}</h4>
            <span className={cn("shrink-0 border px-2 py-0.5 text-xs font-semibold", gatewayTools.length > 0 ? statusClass("success") : statusClass("pending"))}>
              {t((gatewayTools.length > 0 ? "evidence.observed" : "evidence.notObserved") as never)}
            </span>
          </div>
          {gatewayTools.length > 0 ? (
            <div className="mt-3 divide-y divide-[#eee9dd] border border-[#eee9dd]">
              {gatewayTools.map((item) => (
                <div key={`${item.agent_id ?? "gateway"}:${item.tool_name ?? "unknown"}`} className="grid gap-3 px-3 py-3 md:grid-cols-[1fr_112px]">
                  <div className="min-w-0">
                    <p className="truncate font-mono text-xs font-semibold text-[#141413]" title={item.tool_name ?? emptyLabel}>
                      {item.tool_name ?? emptyLabel}
                    </p>
                    <p className="mt-1 truncate text-xs text-[#77736a]" title={item.agent_id ?? emptyLabel}>
                      {t("evidence.agentScope", {
                        agent: item.agent_id ?? emptyLabel,
                        scope: item.required_scope ?? emptyLabel,
                      })}
                    </p>
                  </div>
                  <p className="font-mono text-xs leading-5 text-[#5f5b52]">
                    {t("evidence.counts", {
                      success: item.success ?? 0,
                      failed: item.failed ?? 0,
                      blocked: item.blocked ?? 0,
                    })}
                  </p>
                </div>
              ))}
            </div>
          ) : (
            <p className="mt-3 border border-[#eee9dd] bg-[#faf9f3] px-3 py-3 text-sm leading-6 text-[#5f5b52]">
              {t("evidence.firstClassEmpty")}
            </p>
          )}
        </div>

        <div className="min-w-0 bg-white p-4">
          <div className="flex items-center justify-between gap-3">
            <h4 className="text-sm font-semibold text-[#141413]">{t("evidence.selfBuiltTitle")}</h4>
            <span className={cn("shrink-0 border px-2 py-0.5 text-xs font-semibold", legacyTools.length > 0 ? statusClass("success") : statusClass("pending"))}>
              {t((legacyTools.length > 0 ? "evidence.observed" : "evidence.notObserved") as never)}
            </span>
          </div>
          {legacyTools.length > 0 ? (
            <div className="mt-3 divide-y divide-[#eee9dd] border border-[#eee9dd]">
              {legacyTools.map((item) => {
                const route = [item.mcp_server, item.tool_name].filter(Boolean).join("/") || emptyLabel;
                return (
                  <div key={route} className="grid gap-3 px-3 py-3 md:grid-cols-[1fr_112px]">
                    <div className="min-w-0">
                      <p className="truncate font-mono text-xs font-semibold text-[#141413]" title={route}>
                        {route}
                      </p>
                      <p className="mt-1 truncate text-xs text-[#77736a]" title={item.last_error ?? emptyLabel}>
                        {item.last_error ?? t("evidence.noError")}
                      </p>
                    </div>
                    <p className="font-mono text-xs leading-5 text-[#5f5b52]">
                      {t("evidence.legacyCounts", {
                        success: item.success ?? 0,
                        failed: item.failed ?? 0,
                      })}
                    </p>
                  </div>
                );
              })}
            </div>
          ) : (
            <p className="mt-3 border border-[#eee9dd] bg-[#faf9f3] px-3 py-3 text-sm leading-6 text-[#5f5b52]">
              {t("evidence.selfBuiltEmpty")}
            </p>
          )}
        </div>
      </div>
    </section>
  );
}

function RemediationEvidencePanel({
  history,
  locale,
  emptyLabel,
}: {
  history?: RunRemediationHistory;
  locale: string;
  emptyLabel: string;
}) {
  const t = useTranslations("runDetail.remediation");
  const total = history?.total ?? 0;
  const latest = history?.items?.[0] ?? null;
  const hasRecords = total > 0;
  const incidentIds = history?.incident_ids ?? [];
  const route = remediationRoute(latest);
  const metrics = [
    { label: t("metrics.incidents"), value: incidentIds.length || emptyLabel },
    { label: t("metrics.dryRuns"), value: total },
    { label: t("metrics.tools"), value: latest?.tool_count ?? emptyLabel },
    {
      label: t("metrics.writes"),
      value: latest
        ? `${booleanLabel(latest.writes_incident_state, emptyLabel)}/${booleanLabel(
            latest.writes_auto_repair_result,
            emptyLabel
          )}`
        : emptyLabel,
    },
  ];

  return (
    <section className="border border-[#e0ddd4] bg-white">
      <div className="flex flex-wrap items-center justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
        <div className="flex items-center gap-2">
          <SearchCheck className="h-4 w-4 text-brand-accent" aria-hidden="true" />
          <h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
        </div>
        <span className={cn("border px-2 py-0.5 text-xs font-semibold", hasRecords ? statusClass("warning") : statusClass("pending"))}>
          {hasRecords ? t("status.linked") : t("status.empty")}
        </span>
      </div>
      <div className="grid gap-px bg-[#e0ddd4] md:grid-cols-4">
        {metrics.map((item) => (
          <div key={item.label} className="min-w-0 bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
            <p className="mt-2 truncate font-mono text-xl font-semibold text-[#141413]">
              {item.value}
            </p>
          </div>
        ))}
      </div>
      {latest ? (
        <div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-[1fr_1fr]">
          <div className="min-w-0 bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#77736a]">{t("latest")}</p>
            <p className="mt-2 text-sm leading-6 text-[#5f5b52]">
              <span className="font-mono text-[#141413]">{latest.incident_id ?? emptyLabel}</span>{" "}
              {t("preview", {
                mode: latest.mode ?? emptyLabel,
                preview: latest.verification_result_preview ?? emptyLabel,
              })}
            </p>
            <p className="mt-1 font-mono text-xs text-[#77736a]">
              {formatTime(latest.created_at, locale, emptyLabel)}
            </p>
          </div>
          <div className="min-w-0 bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#77736a]">{t("route")}</p>
            <p className="mt-2 truncate font-mono text-sm text-[#141413]">{route}</p>
            <p className="mt-1 text-xs leading-5 text-[#77736a]">
              {t("writes", {
                incident: booleanLabel(latest.writes_incident_state, emptyLabel),
                autoRepair: booleanLabel(latest.writes_auto_repair_result, emptyLabel),
              })}
            </p>
          </div>
        </div>
      ) : (
        <div className="px-4 py-8 text-sm text-[#77736a]">{t("empty")}</div>
      )}
    </section>
  );
}

function IncidentAuditTimelinePanel({
  timeline,
  chain,
  loading,
  locale,
  emptyLabel,
  statusLabel,
}: {
  timeline?: IncidentTimelineResponse | null;
  chain?: AwoooPStatusChain | null;
  loading: boolean;
  locale: string;
  emptyLabel: string;
  statusLabel: (status: string) => string;
}) {
  const t = useTranslations("runDetail.incidentAudit");
  const incidentId = chain?.source_id ?? timeline?.incident_id ?? null;
  const stages = timeline?.timeline?.filter((stage) => stage.status !== "skipped") ?? [];
  const sourceCorrelation = chain?.source_refs?.correlation;
  const candidates = sourceCorrelation?.top_candidates ?? [];
  const ansible = chain?.execution?.ansible;
  const verifier = timeline?.timeline?.find((stage) => stage.stage === "verifier");
  const kmStage = timeline?.timeline?.find((stage) => stage.stage === "km");
  const executor = timeline?.timeline?.find((stage) => stage.stage === "executor");
  const sourceReason = sourceCorrelation?.missing_reason ?? emptyLabel;
  const selectedPlaybook = chain?.execution?.playbook_paths?.[0]
    ?? chain?.execution?.playbook_ids?.[0]
    ?? ansible?.latest_catalog_id
    ?? ansible?.latest_playbook_path
    ?? ansible?.candidate_playbooks?.[0]?.playbook_path
    ?? ansible?.candidate_playbooks?.[0]?.catalog_id
    ?? emptyLabel;
  const importantEvents = (timeline?.events ?? [])
    .filter((event) => (
      event.source_table === "automation_operation_log"
      || event.source_table === "knowledge_entries"
      || event.source_table === "incident_evidence"
      || event.source_table === "alert_operation_log"
      || event.stage === "verifier"
      || event.stage === "km"
      || event.stage === "executor"
      || event.stage === "ai_router"
    ))
    .slice(-8)
    .reverse();
  const metrics = [
    { label: t("metrics.stages"), value: stages.length },
    { label: t("metrics.events"), value: timeline?.events?.length ?? 0 },
    {
      label: t("metrics.matches"),
      value: `${sourceCorrelation?.direct_ref_total ?? 0}/${sourceCorrelation?.candidate_total ?? 0}/${sourceCorrelation?.applied_link_total ?? 0}`,
    },
    { label: t("metrics.verification"), value: verifier?.status ?? chain?.verification ?? emptyLabel },
  ];

  if (loading && !timeline) {
    return (
      <section className="border border-[#e0ddd4] bg-white">
        <div className="border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
          <div className="h-5 w-48 animate-pulse bg-[#f2efe6]" />
        </div>
        <div className="grid gap-px bg-[#e0ddd4] md:grid-cols-4">
          {Array.from({ length: 4 }).map((_, index) => (
            <div key={index} className="bg-white px-4 py-3">
              <div className="h-4 w-24 animate-pulse bg-[#f2efe6]" />
              <div className="mt-2 h-7 w-16 animate-pulse bg-[#f2efe6]" />
            </div>
          ))}
        </div>
      </section>
    );
  }

  return (
    <section className="border border-[#e0ddd4] bg-white">
      <div className="flex flex-wrap items-center justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
        <div className="flex items-center gap-2">
          <FileSearch className="h-4 w-4 text-brand-accent" aria-hidden="true" />
          <div>
            <h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
            <p className="mt-1 font-mono text-xs text-[#77736a]">{incidentId ?? emptyLabel}</p>
          </div>
        </div>
        <span className={cn("border px-2 py-0.5 text-xs font-semibold", timeline ? statusClass("received") : statusClass("pending"))}>
          {timeline ? t("status.linked") : t("status.empty")}
        </span>
      </div>

      <div className="grid gap-px bg-[#e0ddd4] md:grid-cols-4">
        {metrics.map((item) => (
          <div key={item.label} className="min-w-0 bg-white px-4 py-3">
            <p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
            <p className="mt-2 truncate font-mono text-xl font-semibold text-[#141413]" title={String(item.value)}>
              {item.value}
            </p>
          </div>
        ))}
      </div>

      <div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-[1fr_1fr]">
        <div className="min-w-0 bg-white p-4">
          <div className="flex items-center gap-2">
            <Route className="h-4 w-4 text-brand-accent" aria-hidden="true" />
            <h4 className="text-sm font-semibold text-[#141413]">{t("stagesTitle")}</h4>
          </div>
          {stages.length > 0 ? (
            <div className="mt-3 grid gap-2 md:grid-cols-2">
              {stages.slice(0, 10).map((stage) => (
                <div key={stage.stage} className="min-w-0 border border-[#eee9dd] bg-[#faf9f3] 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", statusClass(stage.status))}>
                      {statusLabel(stage.status)}
                    </span>
                  </div>
                  <p className="mt-2 truncate text-sm font-semibold text-[#141413]" title={stage.title}>
                    {stage.title}
                  </p>
                  <p className="mt-1 truncate font-mono text-xs text-[#77736a]" title={stage.source_table ?? emptyLabel}>
                    {stage.source_table ?? emptyLabel}
                  </p>
                </div>
              ))}
            </div>
          ) : (
            <p className="mt-3 text-sm text-[#77736a]">{t("empty")}</p>
          )}
          {timeline?.ascii_timeline && (
            <p className="mt-3 break-words border border-[#eee9dd] bg-white px-3 py-2 font-mono text-xs leading-5 text-[#5f5b52]">
              {timeline.ascii_timeline}
            </p>
          )}
        </div>

        <div className="min-w-0 bg-white p-4">
          <div className="flex items-center gap-2">
            <SearchCheck className="h-4 w-4 text-brand-accent" aria-hidden="true" />
            <h4 className="text-sm font-semibold text-[#141413]">{t("matchingTitle")}</h4>
          </div>
          <div className="mt-3 grid gap-px border border-[#e0ddd4] bg-[#e0ddd4] md:grid-cols-3">
            <div className="min-w-0 bg-white px-3 py-2">
              <p className="text-xs font-semibold text-[#77736a]">{t("playbook")}</p>
              <p className="mt-1 truncate font-mono text-xs text-[#141413]" title={selectedPlaybook}>{selectedPlaybook}</p>
            </div>
            <div className="min-w-0 bg-white px-3 py-2">
              <p className="text-xs font-semibold text-[#77736a]">{t("executor")}</p>
              <p className="mt-1 truncate font-mono text-xs text-[#141413]" title={executor?.title ?? emptyLabel}>{executor?.status ?? emptyLabel}</p>
            </div>
            <div className="min-w-0 bg-white px-3 py-2">
              <p className="text-xs font-semibold text-[#77736a]">{t("km")}</p>
              <p className="mt-1 truncate font-mono text-xs text-[#141413]" title={kmStage?.title ?? emptyLabel}>{kmStage?.status ?? emptyLabel}</p>
            </div>
          </div>

          {candidates.length > 0 ? (
            <div className="mt-3 divide-y divide-[#eee9dd] border border-[#eee9dd]">
              {candidates.slice(0, 3).map((candidate) => (
                <div key={`${candidate.provider}:${candidate.provider_event_id}`} className="grid gap-2 px-3 py-3 md:grid-cols-[96px_1fr]">
                  <span className="font-mono text-xs font-semibold text-[#5f5b52]">{candidate.provider ?? emptyLabel}</span>
                  <div className="min-w-0">
                    <p className="truncate font-mono text-xs font-semibold text-[#141413]" title={candidate.provider_event_id ?? emptyLabel}>
                      {candidate.provider_event_id ?? emptyLabel}
                    </p>
                    <p className="mt-1 truncate text-xs text-[#77736a]" title={(candidate.reasons ?? []).join(", ")}>
                      {t("candidateDetail", {
                        score: candidate.score ?? 0,
                        state: candidate.link_state ?? emptyLabel,
                        reasons: (candidate.reasons ?? []).join(", ") || emptyLabel,
                      })}
                    </p>
                  </div>
                </div>
              ))}
            </div>
          ) : (
            <p className="mt-3 border border-[#eee9dd] bg-[#faf9f3] px-3 py-3 text-sm leading-6 text-[#5f5b52]">
              {t("matchingEmpty", { reason: sourceReason })}
            </p>
          )}
        </div>
      </div>

      <div className="border-t border-[#e0ddd4] bg-[#faf9f3] px-4 py-2">
        <div className="flex items-center gap-2">
          <BookOpenCheck className="h-4 w-4 text-brand-accent" aria-hidden="true" />
          <h4 className="text-sm font-semibold text-[#141413]">{t("eventsTitle")}</h4>
        </div>
      </div>
      {importantEvents.length > 0 ? (
        <div className="divide-y divide-[#eee9dd]">
          {importantEvents.map((event, index) => {
            const primaryMeta = metadataValue(event.data, ["operation_type", "verification_result", "playbook_id", "matched_playbook_id"]);
            const secondaryMeta = metadataValue(event.data, ["status", "duration_ms", "execution_kind", "repair_executed"]);
            return (
              <article key={`${event.stage}-${event.timestamp}-${index}`} className="grid gap-3 px-4 py-3 md:grid-cols-[132px_1fr]">
                <div className="font-mono text-xs text-[#77736a]">
                  {formatTime(event.timestamp, locale, emptyLabel)}
                </div>
                <div className="min-w-0">
                  <div className="flex flex-wrap items-center gap-2">
                    <span className={cn("border px-2 py-0.5 text-xs font-semibold", statusClass(event.status))}>
                      {statusLabel(event.status)}
                    </span>
                    <span className="font-mono text-xs text-[#77736a]">{event.source_table ?? emptyLabel}</span>
                  </div>
                  <p className="mt-2 break-words text-sm font-semibold text-[#141413]">{event.title}</p>
                  {event.description && (
                    <p className="mt-1 line-clamp-2 text-sm leading-6 text-[#5f5b52]">{event.description}</p>
                  )}
                  {(primaryMeta || secondaryMeta) && (
                    <p className="mt-2 truncate font-mono text-xs text-[#77736a]" title={[primaryMeta, secondaryMeta].filter(Boolean).join(" / ")}>
                      {[primaryMeta, secondaryMeta].filter(Boolean).join(" / ")}
                    </p>
                  )}
                </div>
              </article>
            );
          })}
        </div>
      ) : (
        <div className="px-4 py-8 text-sm text-[#77736a]">{t("eventsEmpty")}</div>
      )}
    </section>
  );
}

function TimelineRow({
  item,
  locale,
  emptyLabel,
  statusLabel,
}: {
  item: TimelineItem;
  locale: string;
  emptyLabel: string;
  statusLabel: (status: string) => string;
}) {
  const Icon = itemIcon(item.kind);
  return (
    <article className="grid gap-3 border-b border-[#eee9dd] bg-white px-4 py-4 last:border-0 md:grid-cols-[132px_1fr]">
      <div className="font-mono text-xs text-[#77736a]">
        {formatTime(item.ts, locale, emptyLabel)}
      </div>
      <div className="min-w-0">
        <div className="flex flex-wrap items-center gap-2">
          <span className="flex h-7 w-7 items-center justify-center border border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]">
            <Icon className="h-3.5 w-3.5" aria-hidden="true" />
          </span>
          <h3 className="text-sm font-semibold text-[#141413]">{item.title}</h3>
          <span className={cn("border px-2 py-0.5 text-xs font-semibold", statusClass(item.status))}>
            {statusLabel(item.status)}
          </span>
        </div>
        {item.summary && (
          <p className="mt-2 whitespace-pre-line break-words text-sm leading-6 text-[#5f5b52]">
            {item.summary}
          </p>
        )}
        {item.metadata && Object.keys(item.metadata).length > 0 && (
          <div className="mt-3 grid gap-2 md:grid-cols-2">
            {Object.entries(item.metadata).slice(0, 4).map(([key, value]) => (
              <div key={key} className="border border-[#eee9dd] bg-[#faf9f3] px-3 py-2">
                <div className="text-xs font-semibold text-[#77736a]">{key}</div>
                <div className="mt-1 truncate font-mono text-xs text-[#141413]">
                  {value === null || value === undefined ? emptyLabel : String(value)}
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    </article>
  );
}

export default function RunDetailPage({
  params,
}: {
  params: { locale: string; run_id: string };
}) {
  const { run_id } = params;
  const locale = useLocale();
  const t = useTranslations("runDetail");
  const searchParams = useSearchParams();
  const projectId = searchParams.get("project_id") ?? "";

  const [detail, setDetail] = useState<RunDetailResponse | null>(null);
  const [dossier, setDossier] = useState<ChannelEventDossierResponse | null>(null);
  const [incidentTimeline, setIncidentTimeline] = useState<IncidentTimelineResponse | null>(null);
  const [incidentTimelineLoading, setIncidentTimelineLoading] = useState(false);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [lastRefresh, setLastRefresh] = useState<Date | null>(null);

  const fetchDetail = useCallback(async () => {
    try {
      setError(null);
      const query = new URLSearchParams();
      if (projectId) query.set("project_id", projectId);
      const suffix = query.toString() ? `?${query.toString()}` : "";
      const res = await fetch(`${API_BASE}/api/v1/platform/runs/${run_id}/detail${suffix}`);
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const data: RunDetailResponse = await res.json();
      setDetail(data);
      const timelineIncidentId = data.awooop_status_chain?.source_id
        || data.remediation_history?.incident_ids?.[0]
        || null;
      if (timelineIncidentId) {
        setIncidentTimelineLoading(true);
        try {
          const timelineRes = await fetch(`${API_BASE}/api/v1/incidents/${encodeURIComponent(timelineIncidentId)}/timeline`);
          setIncidentTimeline(timelineRes.ok ? await timelineRes.json() as IncidentTimelineResponse : null);
        } catch {
          setIncidentTimeline(null);
        } finally {
          setIncidentTimelineLoading(false);
        }
      } else {
        setIncidentTimeline(null);
        setIncidentTimelineLoading(false);
      }
      const dossierProjectId = projectId || data.run?.project_id;
      const dossierQuery = new URLSearchParams();
      dossierQuery.set("run_id", run_id);
      dossierQuery.set("limit", "20");
      if (dossierProjectId) dossierQuery.set("project_id", dossierProjectId);
      try {
        const dossierRes = await fetch(`${API_BASE}/api/v1/platform/events/dossier?${dossierQuery.toString()}`);
        setDossier(dossierRes.ok ? await dossierRes.json() as ChannelEventDossierResponse : null);
      } catch {
        setDossier(null);
      }
      setLastRefresh(new Date());
    } catch (err) {
      setIncidentTimeline(null);
      setIncidentTimelineLoading(false);
      setError(err instanceof Error ? err.message : t("errors.loadFailed"));
    } finally {
      setLoading(false);
    }
  }, [projectId, run_id, t]);

  useEffect(() => {
    setLoading(true);
    fetchDetail();
  }, [fetchDetail]);

  useEffect(() => {
    const timer = setInterval(fetchDetail, AUTO_REFRESH_INTERVAL);
    return () => clearInterval(timer);
  }, [fetchDetail]);

  const run = detail?.run;
  const latestRemediation = detail?.remediation_history?.items?.[0] ?? null;
  const durationText = useMemo(() => {
    if (!run?.created_at) return t("empty");
    const end = run.completed_at || run.heartbeat_at || new Date().toISOString();
    const ms = Math.max(0, new Date(end).getTime() - new Date(run.created_at).getTime());
    return t("durationSeconds", { seconds: Math.round(ms / 1000) });
  }, [run, t]);

  const statusLabel = useCallback(
    (status: string) => {
      const key = STATUS_TRANSLATION_KEYS[status];
      return key ? t(key as never) : status;
    },
    [t]
  );

  return (
    <div className="space-y-6">
      <Link
        href="/awooop/runs"
        className="inline-flex items-center gap-2 text-sm text-[#77736a] hover:text-[#141413]"
      >
        <ArrowLeft className="h-4 w-4" aria-hidden="true" />
        {t("back")}
      </Link>

      <div className="flex flex-wrap items-center justify-between gap-3">
        <div className="flex items-center gap-3">
          <Activity className="h-5 w-5 text-brand-accent" aria-hidden="true" />
          <div>
            <h2 className="text-lg font-semibold text-[#141413]">{t("title")}</h2>
            <p className="font-mono text-xs text-[#77736a]">{run_id}</p>
          </div>
        </div>
        <button
          onClick={() => {
            setLoading(true);
            fetchDetail();
          }}
          disabled={loading}
          className="inline-flex items-center gap-2 border border-[#d8d3c7] bg-white px-3 py-2 text-sm text-[#5f5b52] hover:border-[#d97757] disabled:opacity-50"
        >
          <RefreshCw className={cn("h-4 w-4", loading && "animate-spin")} aria-hidden="true" />
          {t("refresh")}
        </button>
      </div>

      {error && (
        <div className="flex items-start gap-3 border border-[#e2a29b] bg-[#fff0ef] p-4 text-[#9f2f25]">
          <AlertCircle className="mt-0.5 h-5 w-5" aria-hidden="true" />
          <div>
            <p className="text-sm font-semibold">{t("errors.title")}</p>
            <p className="mt-1 text-xs">{error}</p>
          </div>
        </div>
      )}

      <section className="grid gap-px border border-[#e0ddd4] bg-[#e0ddd4] md:grid-cols-4">
        <div className="bg-white p-4">
          <div className="text-xs font-semibold text-[#77736a]">{t("stats.state")}</div>
          <div className={cn("mt-3 inline-flex border px-2 py-1 text-sm font-semibold", statusClass(run?.state ?? "pending"))}>
            {run ? statusLabel(run.state) : t("empty")}
          </div>
        </div>
        <div className="bg-white p-4">
          <div className="text-xs font-semibold text-[#77736a]">{t("stats.timeline")}</div>
          <div className="mt-2 font-mono text-2xl font-semibold text-[#141413]">
            {detail?.counts.timeline ?? 0}
          </div>
        </div>
        <div className="bg-white p-4">
          <div className="text-xs font-semibold text-[#77736a]">{t("stats.mcpSteps")}</div>
          <div className="mt-2 font-mono text-2xl font-semibold text-[#141413]">
            {(detail?.counts.mcp_calls ?? 0) + (detail?.counts.legacy_mcp_calls ?? 0) + (detail?.counts.steps ?? 0)}
          </div>
        </div>
        <div className="bg-white p-4">
          <div className="flex items-center gap-2 text-xs font-semibold text-[#77736a]">
            <Clock className="h-3.5 w-3.5" aria-hidden="true" />
            {t("stats.duration")}
          </div>
          <div className="mt-2 font-mono text-2xl font-semibold text-[#141413]">{durationText}</div>
        </div>
      </section>

      <IncidentEvidenceHeader
        projectId={run?.project_id || projectId || "awoooi"}
        runId={run_id}
        incidentIds={detail?.remediation_history?.incident_ids}
        dryRunCount={detail?.remediation_history?.total}
        latestRoute={remediationRoute(latestRemediation)}
        writesIncidentState={latestRemediation?.writes_incident_state}
        writesAutoRepairResult={latestRemediation?.writes_auto_repair_result}
      />

      <AwoooPStatusChainPanel chain={detail?.awooop_status_chain} />

      <IncidentAuditTimelinePanel
        timeline={incidentTimeline}
        chain={detail?.awooop_status_chain}
        loading={incidentTimelineLoading}
        locale={locale}
        emptyLabel={t("empty")}
        statusLabel={statusLabel}
      />

      <RunActionPanel run={run} counts={detail?.counts} emptyLabel={t("empty")} />

      <OwnerResponseValidationDetailBoundaryPanel />

      <McpGatewayPanel
        summary={detail?.mcp_gateway}
        legacy={detail?.mcp_legacy}
        emptyLabel={t("empty")}
        statusLabel={statusLabel}
      />

      <RemediationEvidencePanel
        history={detail?.remediation_history}
        locale={locale}
        emptyLabel={t("empty")}
      />

      <EventDossierPanel dossier={dossier} locale={locale} emptyLabel={t("empty")} />

      <section className="grid gap-4 xl:grid-cols-[360px_1fr]">
        <aside className="border border-[#e0ddd4] bg-white">
          <div className="border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
            <h3 className="text-sm font-semibold text-[#141413]">{t("summary.title")}</h3>
          </div>
          <div className="px-4">
            <DetailField label={t("summary.project")} value={run?.project_id} emptyLabel={t("empty")} />
            <DetailField label={t("summary.agent")} value={run?.agent_id} emptyLabel={t("empty")} />
            <DetailField label={t("summary.traceId")} value={run?.trace_id} emptyLabel={t("empty")} />
            <DetailField label={t("summary.trigger")} value={run?.trigger_type} emptyLabel={t("empty")} />
            <DetailField label={t("summary.triggerRef")} value={run?.trigger_ref} emptyLabel={t("empty")} />
            <DetailField label={t("summary.cost")} value={run ? `$${Number(run.cost_usd ?? 0).toFixed(4)}` : null} emptyLabel={t("empty")} />
            <DetailField label={t("summary.attempts")} value={run ? `${run.attempt_count}/${run.max_attempts}` : null} emptyLabel={t("empty")} />
            <DetailField label={t("summary.created")} value={formatTime(run?.created_at, locale, t("empty"))} emptyLabel={t("empty")} />
            <DetailField label={t("summary.completed")} value={formatTime(run?.completed_at, locale, t("empty"))} emptyLabel={t("empty")} />
            <DetailField label={t("summary.error")} value={run?.error_detail || run?.error_code} emptyLabel={t("empty")} />
          </div>
        </aside>

        <section className="border border-[#e0ddd4] bg-white">
          <div className="flex flex-wrap items-center justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
            <div>
              <h3 className="text-sm font-semibold text-[#141413]">{t("timeline.title")}</h3>
              <p className="mt-1 text-xs text-[#77736a]">
                {t("timeline.lastUpdated", {
                  time: lastRefresh
                    ? lastRefresh.toLocaleTimeString(locale === "zh-TW" ? "zh-TW" : "en-US")
                    : t("empty"),
                })}
              </p>
            </div>
            <span className="border border-[#d8d3c7] bg-white px-2 py-0.5 text-xs font-semibold text-[#5f5b52]">
              {t("timeline.count", { count: detail?.counts.timeline ?? 0 })}
            </span>
          </div>
          {loading && !detail ? (
            <div className="space-y-3 p-4">
              {Array.from({ length: 5 }).map((_, index) => (
                <div key={index} className="h-16 animate-pulse bg-[#f2efe6]" />
              ))}
            </div>
          ) : detail && detail.timeline.length > 0 ? (
            <div>
              {detail.timeline.map((item, index) => (
                <TimelineRow
                  key={`${item.kind}-${item.ts}-${index}`}
                  item={item}
                  locale={locale}
                  emptyLabel={t("empty")}
                  statusLabel={statusLabel}
                />
              ))}
            </div>
          ) : (
            <div className="px-4 py-12 text-center text-sm text-[#77736a]">
              {t("timeline.empty")}
            </div>
          )}
        </section>
      </section>
    </div>
  );
}
