'use client'

/**
 * IncidentCard - AI中心 v6 事件卡片
 * =====================================
 * Anthropic Warmth 米色系 + OpenClaw Blue
 * - 頂邊條顏色 = 嚴重度（P0=紅/P2=橙/P3=藍/OK=綠）
 * - FlowPipeline 7節點 + Q版龍蝦
 * - 授權邏輯從 DualStateIncidentCard (Phase 6.5) 移植
 *   handleApprove / handleReject / CSRF / 30s 超時重試
 *
 * 專案鐵律: 禁止假數據！無數據顯示 "--"
 * @deprecated dual-state-incident-card.tsx (Phase 6.5 舊風格，此元件取代後刪除)
 */

import React, { useState, useCallback, useRef, useEffect } from 'react'
import { useTranslations } from 'next-intl'
import type { IncidentResponse, DecisionInfo, IncidentTimelineResponse } from '@/lib/api-client'
import { apiClient } from '@/lib/api-client'
import { CURRENT_USER } from '@/lib/constants'
import { useCSRF } from '@/hooks/useCSRF'
import { Link } from '@/i18n/routing'
import { FlowPipeline, type FlowStage } from './flow-pipeline'
import type { AwoooPStatusChain } from '@/components/awooop/status-chain'
import { Activity, GitBranch, Lightbulb, ListChecks, Monitor, ShieldCheck } from 'lucide-react'

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

type ButtonState = 'idle' | 'loading' | 'approved' | 'rejected' | 'error' | 'timeout'

const EXECUTION_TIMEOUT_MS = 30000

export interface IncidentCardProps {
  incident: IncidentResponse
  decision?: DecisionInfo | null
  statusChain?: AwoooPStatusChain | null
  projectId?: string
  onApprovalChange?: (proposalId: string, newStatus: 'approved' | 'rejected') => void
}

// =============================================================================
// Severity / Status Config
// =============================================================================

const SEV_CONFIG = {
  P0: { barColor: '#cc2200',  label: 'P0', labelBg: 'rgba(204,34,0,0.1)',    labelColor: '#cc2200' },
  P1: { barColor: '#F59E0B',  label: 'P1', labelBg: 'rgba(245,158,11,0.12)', labelColor: '#d97000' },
  P2: { barColor: '#4A90D9',  label: 'P2', labelBg: 'rgba(74,144,217,0.12)', labelColor: '#4A90D9' },
  P3: { barColor: '#22C55E',  label: 'P3', labelBg: 'rgba(34,197,94,0.1)',   labelColor: '#16a34a' },
} as const

const FLOW_STAGE_ORDER: FlowStage[] = [
  'alert',
  'detection',
  'analysis',
  'proposal',
  'approval',
  'execution',
  'resolved',
]

const SOURCE_CORRELATION_STATUS_KEYS = {
  linked: 'linked',
  candidate_found: 'candidateFound',
  provider_fresh_no_match: 'providerFreshNoMatch',
  missing: 'missing',
  no_incident_context: 'noIncidentContext',
  fetch_failed: 'fetchFailed',
} as const

/** 根據 incident + decision evidence 對應 FlowStage */
function toFlowStage(status: string, severity: string, decision?: DecisionInfo | null): FlowStage {
  const normalizedStatus = status.toLowerCase()
  const decisionState = decision?.state

  if (['resolved', 'closed', 'completed', 'success'].includes(normalizedStatus)) return 'resolved'
  if (decisionState === 'completed') return 'execution'
  if (['executing', 'mitigating'].includes(normalizedStatus) || decisionState === 'executing') return 'execution'
  if (
    ['waiting_approval', 'pending_approval', 'approval_pending'].includes(normalizedStatus) ||
    decisionState === 'ready' ||
    !!decision?.proposal_id
  ) return 'approval'
  if (['proposal_generated', 'proposed'].includes(normalizedStatus) || !!decision?.proposal_data?.action) return 'proposal'
  if (['analyzing', 'analysis'].includes(normalizedStatus) || decisionState === 'analyzing') return 'analysis'
  if (['new', 'open'].includes(normalizedStatus)) return 'alert'

  return severity === 'P0' ? 'alert' : 'detection'
}

function nextFlowStage(stage: FlowStage, isResolved: boolean): FlowStage | null {
  if (isResolved) return null
  const index = FLOW_STAGE_ORDER.indexOf(stage)
  if (index < 0 || index >= FLOW_STAGE_ORDER.length - 1) return null
  return FLOW_STAGE_ORDER[index + 1]
}

function statusChainFlowStage(chain?: AwoooPStatusChain | null): FlowStage | null {
  if (!chain || chain.fetch_error) return null
  const currentStage = String(chain.current_stage ?? '').toLowerCase()
  const repairState = String(chain.repair_state ?? '').toLowerCase()
  const verdict = String(chain.verdict ?? '').toLowerCase()
  const nextStep = String(chain.next_step ?? '').toLowerCase()

  if (verdict === 'auto_repaired_verified' || repairState === 'auto_repaired_verified') {
    return 'resolved'
  }
  if (
    currentStage.includes('execution') ||
    repairState.startsWith('executed') ||
    nextStep.includes('verify_execution')
  ) {
    return 'execution'
  }
  if (
    currentStage.includes('approval') ||
    repairState.includes('manual_required') ||
    nextStep.includes('manual') ||
    chain.needs_human === true
  ) {
    return 'approval'
  }
  if (
    currentStage.includes('proposal') ||
    currentStage.includes('safe') ||
    currentStage.includes('target') ||
    currentStage.includes('blast') ||
    currentStage.includes('llm')
  ) {
    return 'proposal'
  }
  if (
    currentStage.includes('analysis') ||
    currentStage.includes('investigator') ||
    currentStage.includes('router')
  ) {
    return 'analysis'
  }
  if (currentStage.includes('webhook') || currentStage.includes('source') || currentStage.includes('alert')) {
    return 'alert'
  }
  return null
}

function chainValue(value: string | null | undefined, fallback = '--'): string {
  const normalized = String(value ?? '').trim()
  return normalized || fallback
}

function compactChainPath(value: string | null | undefined, fallback = '--'): string {
  const normalized = chainValue(value, fallback)
  return normalized.replace(/^infra\/ansible\/playbooks\//, '')
}

/** 格式化持續時間 */
function formatDuration(createdAt: string | undefined): string {
  if (!createdAt) return '--'
  try {
    const ms = Date.now() - new Date(createdAt).getTime()
    const mins = Math.floor(ms / 60000)
    if (mins < 60) return `${mins}m`
    return `${Math.floor(mins / 60)}h${mins % 60}m`
  } catch {
    return '--'
  }
}

function statusColor(status: string): string {
  switch (status) {
    case 'success':
    case 'completed':
      return '#16a34a'
    case 'warning':
      return '#d97000'
    case 'error':
      return '#cc2200'
    case 'pending':
      return '#87867f'
    default:
      return '#4A90D9'
  }
}

function formatTimelineTime(value: string | null): string {
  if (!value) return '--'
  try {
    return new Date(value).toLocaleTimeString('zh-TW', {
      hour: '2-digit',
      minute: '2-digit',
    })
  } catch {
    return '--'
  }
}

function asRecord(value: unknown): Record<string, unknown> {
  return value && typeof value === 'object' && !Array.isArray(value)
    ? value as Record<string, unknown>
    : {}
}

function eventContext(event: IncidentTimelineResponse['events'][number]): Record<string, unknown> {
  const data = asRecord(event.data)
  return asRecord(data.context)
}

function eventMcpRoute(event: IncidentTimelineResponse['events'][number]): string | null {
  const context = eventContext(event)
  const route = asRecord(context.mcp_route)
  const agent = route.agent_id
  const tool = route.tool_name
  const scope = route.required_scope
  if (!agent && !tool && !scope) return null
  return [agent, tool, scope].filter(Boolean).map(String).join('/')
}

function eventWriteSummary(event: IncidentTimelineResponse['events'][number]): string | null {
  const context = eventContext(event)
  const incident = context.writes_incident_state
  const autoRepair = context.writes_auto_repair_result
  if (incident == null && autoRepair == null) return null
  return `incident=${String(incident)} autoRepair=${String(autoRepair)}`
}

function compactTimelineText(value: string | null | undefined, fallback = '--'): string {
  if (!value) return fallback
  return value.length > 96 ? `${value.slice(0, 96)}...` : value
}

// =============================================================================
// 2026-04-02 Claude Code: Phase R-UI2 handleApprove/Reject 重複邏輯抽取
// useApprovalAction — 統一 setup/teardown：loading 狀態、timeout、error 處理
// action callback 負責差異邏輯（API call），並回傳最終 ButtonState
// 若 action 未回傳（void），hook 維持 idle
// =============================================================================

interface ApprovalActionResult {
  execute: () => Promise<void>
  buttonState: ButtonState
  errorMessage: string | null
  reset: () => void
}

function useApprovalAction(
  action: () => Promise<ButtonState | void>,
  timeoutMs: number,
  timeoutMessage: string,
): ApprovalActionResult {
  const [buttonState, setButtonState] = useState<ButtonState>('idle')
  const [errorMessage, setErrorMessage] = useState<string | null>(null)
  const timeoutRef = useRef<NodeJS.Timeout | null>(null)

  useEffect(() => {
    return () => { if (timeoutRef.current) clearTimeout(timeoutRef.current) }
  }, [])

  const execute = useCallback(async () => {
    if (buttonState === 'loading') return
    setButtonState('loading')
    setErrorMessage(null)
    if (timeoutRef.current) clearTimeout(timeoutRef.current)
    timeoutRef.current = setTimeout(() => {
      setButtonState('timeout')
      setErrorMessage(timeoutMessage)
    }, timeoutMs)

    try {
      const finalState = await action()
      if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = null }
      setButtonState(finalState ?? 'idle')
    } catch (error) {
      if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = null }
      setButtonState('error')
      setErrorMessage(error instanceof Error ? error.message : String(error))
    }
  }, [action, buttonState, timeoutMs, timeoutMessage])

  const reset = useCallback(() => {
    setButtonState('idle')
    setErrorMessage(null)
  }, [])

  return { execute, buttonState, errorMessage, reset }
}

// =============================================================================
// Component
// =============================================================================

export function IncidentCard({ incident, decision, statusChain, projectId = 'awoooi', onApprovalChange }: IncidentCardProps) {
  const t = useTranslations('incident.card')
  const { csrfToken } = useCSRF()

  const [currentProposalId, setCurrentProposalId] = useState<string | null>(null)
  const [aiExpanded, setAiExpanded] = useState(false)
  const [timelineExpanded, setTimelineExpanded] = useState(false)
  const [timelineData, setTimelineData] = useState<IncidentTimelineResponse | null>(null)
  const [timelineLoading, setTimelineLoading] = useState(false)
  const [timelineError, setTimelineError] = useState<string | null>(null)

  const incidentStatus = incident.status as string
  const sev = incident.severity as keyof typeof SEV_CONFIG
  const sevCfg = SEV_CONFIG[sev] ?? SEV_CONFIG.P3
  const heuristicFlowStage = toFlowStage(incidentStatus, incident.severity, decision)
  const statusChainStage = statusChainFlowStage(statusChain)
  const flowStage = statusChainStage ?? heuristicFlowStage
  const isResolved = incidentStatus === 'resolved' || incidentStatus === 'closed'
  const isTruthChainResolved = statusChain?.repair_state === 'auto_repaired_verified' || statusChain?.verdict === 'auto_repaired_verified'
  const isFlowResolved = isResolved || isTruthChainResolved
  const nextStage = nextFlowStage(flowStage, isFlowResolved)
  const hasTruthChain = Boolean(statusChain && !statusChain.fetch_error && statusChain.source_id)
  const flowStageLabels: Record<FlowStage, string> = {
    alert: t('flowStages.alert'),
    detection: t('flowStages.detection'),
    analysis: t('flowStages.analysis'),
    proposal: t('flowStages.proposal'),
    approval: t('flowStages.approval'),
    execution: t('flowStages.execution'),
    resolved: t('flowStages.resolved'),
  }
  const currentStageText = hasTruthChain
    ? t('flowTruthChainCurrent', {
        stage: chainValue(statusChain?.current_stage),
        status: chainValue(statusChain?.stage_status),
      })
    : flowStageLabels[flowStage]
  const nextStageText = hasTruthChain
    ? chainValue(statusChain?.next_step)
    : nextStage ? flowStageLabels[nextStage] : t('flowComplete')
  const flowSourceText = hasTruthChain ? t('flowSourceTruthChain') : t('flowSourceHeuristic')
  const flowSummaryTextStyle = {
    minWidth: 0,
    overflowWrap: 'anywhere' as const,
    wordBreak: 'break-word' as const,
  }
  const statusChainEvidenceItems = hasTruthChain ? [
    {
      key: 'mcp',
      label: t('flowEvidenceMcp', { count: statusChain?.evidence?.mcp_gateway_total ?? 0 }),
      active: (statusChain?.evidence?.mcp_gateway_total ?? 0) > 0,
    },
    {
      key: 'ops',
      label: t('flowEvidenceOps', { count: statusChain?.evidence?.operation_records ?? 0 }),
      active: (statusChain?.evidence?.operation_records ?? 0) > 0,
    },
    {
      key: 'km',
      label: t('flowEvidenceKm', { count: statusChain?.evidence?.knowledge_entries ?? 0 }),
      active: (statusChain?.evidence?.knowledge_entries ?? 0) > 0,
    },
    {
      key: 'repair',
      label: t('flowEvidenceRepair', { count: statusChain?.evidence?.auto_repair_records ?? 0 }),
      active: (statusChain?.evidence?.auto_repair_records ?? 0) > 0,
    },
  ] : []
  const mcpTopTools = (statusChain?.mcp?.top_tools ?? []).slice(0, 3).map(tool => {
    const toolName = chainValue(tool.tool_name)
    return `${toolName} ${tool.success ?? 0}/${tool.failed ?? 0}/${tool.blocked ?? 0}`
  }).join(', ')
  const mcpGateway = statusChain?.mcp?.gateway
  const mcpLegacy = statusChain?.mcp?.legacy
  const statusChainMcpDetail = hasTruthChain
    ? t('flowMcpDetail', {
        success: mcpGateway?.success ?? 0,
        failed: mcpGateway?.failed ?? 0,
        blocked: mcpGateway?.blocked ?? 0,
        firstClass: mcpGateway?.first_class_total ?? 0,
        legacy: mcpLegacy?.total ?? 0,
        tools: mcpTopTools || '--',
      })
    : null
  const execution = statusChain?.execution
  const ansible = execution?.ansible
  const ansibleSummary = ansible?.considered
    ? t('flowExecutionAnsibleConsidered', {
        records: ansible.record_total ?? 0,
        candidates: ansible.candidate_count ?? 0,
      })
    : ansible?.not_used_reason
      ? t('flowExecutionAnsibleNotUsed', { reason: String(ansible.not_used_reason).slice(0, 96) })
      : t('flowExecutionAnsibleEmpty')
  const executionPlaybook = compactChainPath(
    execution?.playbook_paths?.[0]
      ?? execution?.playbook_ids?.[0]
      ?? ansible?.candidate_playbooks?.[0]?.playbook_path
  )
  const statusChainExecutionDetail = hasTruthChain
    ? t('flowExecutionDetail', {
        executor: chainValue(execution?.latest_executor),
        operation: chainValue(execution?.latest_operation_type),
        status: chainValue(execution?.latest_status),
        ansible: ansibleSummary,
        playbook: executionPlaybook,
      })
    : null
  const sourceRefs = statusChain?.source_refs
  const sourceCorrelation = sourceRefs?.correlation
  const sourceCorrelationStatus = String(sourceCorrelation?.status ?? 'missing')
  const sourceCorrelationKey =
    SOURCE_CORRELATION_STATUS_KEYS[
      sourceCorrelationStatus as keyof typeof SOURCE_CORRELATION_STATUS_KEYS
    ] ?? 'missing'
  const sourceCorrelationStatusLabels: Record<typeof sourceCorrelationKey, string> = {
    linked: t('flowSourceCorrelationStatus.linked'),
    candidateFound: t('flowSourceCorrelationStatus.candidateFound'),
    providerFreshNoMatch: t('flowSourceCorrelationStatus.providerFreshNoMatch'),
    missing: t('flowSourceCorrelationStatus.missing'),
    noIncidentContext: t('flowSourceCorrelationStatus.noIncidentContext'),
    fetchFailed: t('flowSourceCorrelationStatus.fetchFailed'),
  }
  const sourceCorrelationLabel = sourceCorrelationStatusLabels[sourceCorrelationKey]
  const latestSource = sourceRefs?.latest_inbound?.channel_type
    ? `${sourceRefs.latest_inbound.channel_type}/${chainValue(sourceRefs.latest_inbound.provider_event_id)}`
    : sourceRefs?.latest_outbound?.channel_type
      ? `${sourceRefs.latest_outbound.channel_type}/${chainValue(sourceRefs.latest_outbound.message_type)}`
      : '--'
  const statusChainSourceRefDetail = hasTruthChain
    ? t('flowSourceRefsDetail', {
        inbound: sourceRefs?.inbound_total ?? 0,
        outbound: sourceRefs?.outbound_total ?? 0,
        alert: sourceRefs?.refs?.alert_ids?.length ?? 0,
        sentry: sourceRefs?.refs?.sentry_issue_ids?.length ?? 0,
        signoz: sourceRefs?.refs?.signoz_alerts?.length ?? 0,
        linked: sourceCorrelation?.direct_ref_total ?? 0,
        candidate: sourceCorrelation?.candidate_total ?? 0,
        correlation: sourceCorrelationLabel,
        latest: latestSource,
      })
    : null
  const encodedProjectId = encodeURIComponent(projectId)
  const encodedIncidentId = encodeURIComponent(incident.incident_id)
  const truthLinks = [
    {
      key: 'monitoring',
      label: t('truthLinkMonitoring'),
      href: `/monitoring?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}` as never,
      Icon: Monitor,
    },
    {
      key: 'workItems',
      label: t('truthLinkWorkItems'),
      href: `/awooop/work-items?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}` as never,
      Icon: ListChecks,
    },
    {
      key: 'runs',
      label: t('truthLinkRuns'),
      href: `/awooop/runs?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}` as never,
      Icon: Activity,
    },
    {
      key: 'approvals',
      label: t('truthLinkApprovals'),
      href: `/awooop/approvals?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}` as never,
      Icon: ShieldCheck,
    },
    {
      key: 'tickets',
      label: t('truthLinkTickets'),
      href: `/tickets?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}` as never,
      Icon: GitBranch,
    },
  ]

  const serviceName = incident.affected_services?.[0] ?? '--'
  const duration = formatDuration(incident.created_at)
  const isDecisionReady = decision?.state === 'ready' || !!currentProposalId
  const isWaitingApproval = incidentStatus === 'waiting_approval' || isDecisionReady
  const isAnalyzing = decision?.state === 'analyzing'
  const decisionAction = decision?.proposal_data?.action ?? ''
  const decisionReasoning = decision?.proposal_data?.reasoning ?? ''

  useEffect(() => {
    setTimelineExpanded(false)
    setTimelineData(null)
    setTimelineError(null)
    setTimelineLoading(false)
  }, [incident.incident_id])

  // ── 解析 proposalId（approve/reject 共用前置步驟）─────────────────────────
  const resolveProposalId = useCallback(async (): Promise<string> => {
    let approvalId = currentProposalId
    if (!approvalId && decision?.token) {
      const proposalResult = await apiClient.generateProposal(incident.incident_id)
      if (!proposalResult.success || !proposalResult.proposal) {
        throw new Error(proposalResult.message || 'Failed to generate proposal')
      }
      approvalId = proposalResult.proposal.id
      setCurrentProposalId(approvalId)
    }
    if (!approvalId) throw new Error('No approval ID available')
    return approvalId
  }, [currentProposalId, decision, incident.incident_id])

  // ── approve action（差異邏輯：signApproval + 成功狀態判斷）────────────────
  const approveAction = useCallback(async (): Promise<ButtonState | void> => {
    if (!isDecisionReady) return
    const approvalId = await resolveProposalId()
    const result = await apiClient.signApproval(approvalId, CURRENT_USER.id, 'Authorized via AI Center', csrfToken)
    const approvalStatus = result.approval?.status?.toLowerCase()
    if (approvalStatus === 'approved') {
      onApprovalChange?.(approvalId, 'approved')
      return 'approved'
    }
    // API 回傳非 approved（保持與原始行為一致：回到 idle）
    return 'idle'
  }, [isDecisionReady, resolveProposalId, csrfToken, onApprovalChange])

  // ── reject action（差異邏輯：rejectApproval + 直接 rejected 狀態）─────────
  const rejectAction = useCallback(async (): Promise<ButtonState> => {
    if (!isDecisionReady) return 'idle'
    const approvalId = await resolveProposalId()
    await apiClient.rejectApproval(approvalId, 'Rejected via AI Center', csrfToken)
    onApprovalChange?.(approvalId, 'rejected')
    return 'rejected'
  }, [isDecisionReady, resolveProposalId, csrfToken, onApprovalChange])

  const timeoutMsg = t('timeoutMessage')

  // ── useApprovalAction instances（共用 buttonState / errorMessage 透過 approve hook 主導）
  // approve 和 reject 原本共用同一個 buttonState；為保持原行為，
  // 以 approveHook 的 state 為主，rejectHook 的 execute 直接呼叫（state 不顯示）
  const approveHook = useApprovalAction(approveAction, EXECUTION_TIMEOUT_MS, timeoutMsg)
  const rejectHook = useApprovalAction(rejectAction, EXECUTION_TIMEOUT_MS, timeoutMsg)

  // 顯示優先順序：若任一 hook 處於 non-idle 則優先顯示；否則 idle
  const activeHook = approveHook.buttonState !== 'idle' ? approveHook : rejectHook
  const buttonState = activeHook.buttonState
  const errorMessage = activeHook.errorMessage

  const handleApprove = approveHook.execute
  const handleReject = rejectHook.execute

  const handleTimelineToggle = useCallback(async () => {
    const nextExpanded = !timelineExpanded
    setTimelineExpanded(nextExpanded)
    if (!nextExpanded || timelineData || timelineLoading) return

    setTimelineLoading(true)
    setTimelineError(null)
    try {
      const timeline = await apiClient.getIncidentTimeline(incident.incident_id)
      setTimelineData(timeline)
    } catch (error) {
      setTimelineError(error instanceof Error ? error.message : String(error))
    } finally {
      setTimelineLoading(false)
    }
  }, [incident.incident_id, timelineData, timelineExpanded, timelineLoading])

  // ── 授權按鈕渲染 ───────────────────────────────────────────────────────────
  const renderApproveButtons = () => {
    switch (buttonState) {
      case 'loading':
        return (
          <span style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 12, color: '#87867f' }}>
            <span style={{ width: 8, height: 8, border: '1.5px solid #e0ddd4', borderTopColor: '#d97757', borderRadius: '50%', animation: 'spin 0.8s linear infinite', display: 'inline-block' }} />
            {t('executing')}
          </span>
        )
      case 'approved':
        return <span style={{ fontSize: 12, color: '#22C55E', fontWeight: 700 }}>{t('approved')}</span>
      case 'rejected':
        return <span style={{ fontSize: 12, color: '#cc2200', fontWeight: 700 }}>{t('rejected')}</span>
      case 'error':
      case 'timeout':
        return (
          <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
            <span style={{ fontSize: 12, color: buttonState === 'timeout' ? '#F59E0B' : '#cc2200' }}>
              {buttonState === 'timeout' ? t('timeout') : t('error')}
            </span>
            <button
              onClick={() => { approveHook.reset(); rejectHook.reset() }}
              style={{ fontSize: 12, padding: '2px 7px', background: '#f0efe8', border: '0.5px solid #e0ddd4', borderRadius: 10, cursor: 'pointer', color: '#87867f' }}
            >
              {t('retry')}
            </button>
          </div>
        )
      default:
        return (
          <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
            <button
              onClick={handleApprove}
              disabled={!isDecisionReady}
              style={{
                padding: '5px 16px',
                background: '#141413',
                color: '#f5f4ed',
                border: 'none',
                borderRadius: 20,
                fontSize: 13,
                cursor: isDecisionReady ? 'pointer' : 'not-allowed',
                opacity: isDecisionReady ? 1 : 0.4,
                fontFamily: 'inherit',
              }}
              title={isAnalyzing ? t('analyzing') : decisionAction || t('authorizeExecution')}
            >
              {t('authorize')}
            </button>
            <button
              onClick={handleReject}
              disabled={!isDecisionReady}
              style={{
                padding: '5px 16px',
                background: 'transparent',
                color: '#87867f',
                border: '0.5px solid #e0ddd4',
                borderRadius: 20,
                fontSize: 13,
                cursor: isDecisionReady ? 'pointer' : 'not-allowed',
                opacity: isDecisionReady ? 1 : 0.4,
                fontFamily: 'inherit',
              }}
            >
              {t('reject')}
            </button>
            {isAnalyzing && (
              <span
                aria-label={t('analyzing')}
                style={{
                  width: 8,
                  height: 8,
                  borderRadius: '50%',
                  background: '#F59E0B',
                  display: 'inline-block',
                }}
              />
            )}
          </div>
        )
    }
  }

  return (
    <div style={{
      background: '#fff',
      border: '0.5px solid #e0ddd4',
      borderRadius: 12,
      overflow: 'hidden',
      boxShadow: '0 1px 4px rgba(0,0,0,0.05)',
    }}>
      {/* 頂邊條 4px */}
      <div style={{ height: 4, background: sevCfg.barColor }} />

      {/* 標頭列：嚴重度 + 服務標籤 + 時間 */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '10px 14px 4px' }}>
        <span style={{
          fontSize: 10, fontWeight: 700, padding: '2px 7px',
          background: sevCfg.labelBg, color: sevCfg.labelColor,
          borderRadius: 4, flexShrink: 0, textTransform: 'uppercase', letterSpacing: '1px',
        }}>
          {sevCfg.label}
        </span>
        <span style={{
          fontSize: 10, color: '#87867f',
          border: '0.5px solid #e0ddd4', padding: '2px 7px', borderRadius: 4,
          background: '#fff',
        }}>
          {serviceName}
        </span>
        <span style={{ marginLeft: 'auto', fontSize: 11, color: '#b0ad9f' }}>
          {duration}
        </span>
      </div>

      {/* 事件標題 — 從 decision action 或 affected_services 組合 */}
      <div style={{ padding: '4px 14px 2px', fontSize: 15, fontWeight: 600, color: '#141413', lineHeight: 1.3, fontFamily: 'var(--font-body), monospace' }}>
        {incident.decision?.proposal_data?.action
          ?? (incident.affected_services?.length
            ? `${incident.affected_services.join(', ')} ${t('anomaly')}`
            : incident.incident_id)}
      </div>

      {/* INC-ID */}
      <div style={{ padding: '0 14px 6px', fontSize: 12, color: '#b0ad9f', fontFamily: 'var(--font-body), monospace' }}>
        {incident.incident_id}
      </div>

      {/* 流程狀態圖 */}
      <FlowPipeline activeStage={flowStage} isResolved={isFlowResolved} severity={sev} />

      <div
        data-testid="incident-flow-summary"
        data-flow-source={hasTruthChain ? 'truth-chain' : 'heuristic'}
        style={{
          margin: '0 14px 8px',
          padding: '7px 9px',
          border: '0.5px solid #e0ddd4',
          borderRadius: 6,
          background: '#fbfaf6',
          display: 'flex',
          alignItems: 'center',
          gap: 10,
          flexWrap: 'wrap',
          fontSize: 11,
          color: '#555550',
          minWidth: 0,
          overflowWrap: 'anywhere',
          wordBreak: 'break-word',
        }}
      >
        <span style={flowSummaryTextStyle}>
          {t('flowCurrentLabel')}: <strong style={{ color: sevCfg.labelColor }}>{currentStageText}</strong>
        </span>
        <span style={{ color: '#b0ad9f' }}>/</span>
        <span style={flowSummaryTextStyle}>
          {t('flowNextLabel')}: <strong style={{ color: '#141413' }}>{nextStageText}</strong>
        </span>
        <span style={{ color: '#b0ad9f' }}>/</span>
        <span style={flowSummaryTextStyle}>
          {t('flowSourceLabel')}: <strong style={{ color: '#555550' }}>{flowSourceText}</strong>
        </span>
        {hasTruthChain && (
          <>
            <span style={{ color: '#b0ad9f' }}>/</span>
            <span style={flowSummaryTextStyle}>
              {t('flowVerdictLabel')}: <strong style={{ color: statusChain?.needs_human ? '#9f2f25' : '#17602a' }}>{chainValue(statusChain?.verdict)}</strong>
            </span>
            <span style={{ color: '#b0ad9f' }}>/</span>
            <span data-testid="incident-flow-evidence" style={{ display: 'flex', alignItems: 'center', gap: 5, flexWrap: 'wrap', ...flowSummaryTextStyle }}>
              {statusChainEvidenceItems.map(item => (
                <strong
                  key={item.key}
                  style={{
                    color: item.active ? '#17602a' : '#87867f',
                    background: item.active ? 'rgba(34,197,94,0.08)' : '#f5f4ed',
                    border: '0.5px solid #e0ddd4',
                    borderRadius: 4,
                    padding: '1px 5px',
                    fontWeight: 600,
                  }}
                >
                  {item.label}
                </strong>
              ))}
            </span>
            <span style={{ color: '#b0ad9f' }}>/</span>
            <span data-testid="incident-mcp-evidence" style={{ color: '#555550', ...flowSummaryTextStyle }}>
              {statusChainMcpDetail}
            </span>
            <span style={{ color: '#b0ad9f' }}>/</span>
            <span data-testid="incident-execution-evidence" style={{ color: '#555550', ...flowSummaryTextStyle }}>
              {statusChainExecutionDetail}
            </span>
            <span style={{ color: '#b0ad9f' }}>/</span>
            <span data-testid="incident-source-ref-evidence" style={{ color: '#555550', ...flowSummaryTextStyle }}>
              {statusChainSourceRefDetail}
            </span>
          </>
        )}
      </div>

      {/* Impact 指標列 */}
      <div style={{
        margin: '0 14px 8px',
        padding: '6px 0',
        borderTop: '0.5px solid #e0ddd4',
        borderBottom: '0.5px solid #e0ddd4',
        display: 'flex',
        gap: 16,
      }}>
        <span style={{ fontSize: 11, color: '#87867f' }}>
          {t('affectedServices')} <strong style={{ color: '#141413' }}>{incident.affected_services?.length ?? 0}</strong>
        </span>
        <span style={{ fontSize: 11, color: '#87867f' }}>
          {t('signalCount')} <strong style={{ color: '#141413' }}>{incident.signal_count ?? '--'}</strong>
        </span>
        <span style={{ fontSize: 11, color: '#87867f' }}>
          {t('statusLabel')} <strong style={{ color: '#141413' }}>{incident.status}</strong>
        </span>
      </div>

      <div style={{
        margin: '0 14px 10px',
        padding: '7px 9px',
        border: '0.5px solid #e0ddd4',
        borderRadius: 6,
        background: '#fff',
        display: 'flex',
        alignItems: 'center',
        gap: 7,
        flexWrap: 'wrap',
      }}>
        <span style={{ fontSize: 11, color: '#87867f', fontWeight: 700 }}>
          {t('truthLinksLabel')}
        </span>
        {truthLinks.map(({ key, label, href, Icon }) => (
          <Link
            key={key}
            href={href}
            style={{
              display: 'inline-flex',
              alignItems: 'center',
              gap: 4,
              padding: '3px 7px',
              border: '0.5px solid #d8d3c7',
              borderRadius: 5,
              background: '#faf9f3',
              color: '#3f3a32',
              fontSize: 11,
              fontWeight: 600,
              textDecoration: 'none',
            }}
          >
            <Icon aria-hidden="true" size={12} strokeWidth={1.8} />
            {label}
          </Link>
        ))}
      </div>

      <button
        onClick={handleTimelineToggle}
        style={{
          margin: '0 14px 10px',
          padding: '7px 0',
          background: '#f8f7f1',
          border: '0.5px solid #e0ddd4',
          borderRadius: 6,
          cursor: 'pointer',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          gap: 6,
          fontSize: 12,
          color: '#4A90D9',
          width: 'calc(100% - 28px)',
          textAlign: 'center' as const,
          fontFamily: 'inherit',
        }}
      >
        <span>{timelineExpanded ? '▾' : '▸'} {t('processingTimeline')}</span>
        {timelineData?.ascii_timeline && (
          <span style={{
            color: '#87867f',
            maxWidth: 260,
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
          }}>
            {timelineData.ascii_timeline}
          </span>
        )}
      </button>
      {timelineExpanded && (
        <div style={{
          margin: '0 10px 10px',
          padding: '8px',
          background: '#fff',
          border: '0.5px solid #e0ddd4',
          borderLeft: '3px solid #4A90D9',
          borderRadius: 6,
        }}>
          {timelineLoading && (
            <div style={{ fontSize: 12, color: '#87867f' }}>{t('timelineLoading')}</div>
          )}
          {timelineError && (
            <div style={{ fontSize: 12, color: '#cc2200' }}>{timelineError}</div>
          )}
          {!timelineLoading && !timelineError && timelineData && (
            <>
              <div style={{
                fontSize: 11,
                color: '#87867f',
                fontFamily: 'var(--font-body), monospace',
                marginBottom: 7,
                whiteSpace: 'nowrap',
                overflow: 'hidden',
                textOverflow: 'ellipsis',
              }}>
                {timelineData.ascii_timeline}
              </div>
              <div style={{ display: 'grid', gap: 5 }}>
                {timelineData.timeline
                  .filter(stage => stage.status !== 'skipped')
                  .map(stage => (
                    <div
                      key={stage.stage}
                      style={{
                        display: 'grid',
                        gridTemplateColumns: '68px 1fr auto',
                        gap: 6,
                        alignItems: 'center',
                        minHeight: 24,
                        fontSize: 11,
                      }}
                    >
                      <span style={{
                        color: statusColor(stage.status),
                        fontWeight: 700,
                        textTransform: 'uppercase',
                      }}>
                        {stage.stage}
                      </span>
                      <span style={{
                        color: '#141413',
                        overflow: 'hidden',
                        textOverflow: 'ellipsis',
                        whiteSpace: 'nowrap',
                      }} title={stage.description ?? stage.title}>
                        {stage.title}
                      </span>
                      <span style={{ color: '#b0ad9f', fontFamily: 'var(--font-body), monospace' }}>
                        {formatTimelineTime(stage.timestamp)}
                      </span>
                    </div>
                  ))}
              </div>
              {timelineData.events.length > 0 && (
                <div style={{ marginTop: 10, paddingTop: 9, borderTop: '0.5px solid #e0ddd4' }}>
                  <div style={{
                    fontSize: 10,
                    color: '#87867f',
                    fontWeight: 700,
                    textTransform: 'uppercase',
                    marginBottom: 6,
                  }}>
                    {t('timelineEvents')}
                  </div>
                  <div style={{ display: 'grid', gap: 6 }}>
                    {timelineData.events.slice(-8).reverse().map((event, index) => {
                      const route = eventMcpRoute(event)
                      const writes = eventWriteSummary(event)
                      return (
                        <div
                          key={`${event.source_table ?? 'event'}-${event.timestamp ?? index}-${event.title}`}
                          style={{
                            display: 'grid',
                            gap: 3,
                            minWidth: 0,
                            padding: '6px 7px',
                            border: '0.5px solid #e0ddd4',
                            borderRadius: 6,
                            background: '#faf9f3',
                          }}
                        >
                          <div style={{
                            display: 'flex',
                            alignItems: 'center',
                            gap: 6,
                            minWidth: 0,
                            fontSize: 11,
                          }}>
                            <span style={{ color: statusColor(event.status), fontWeight: 700, flexShrink: 0 }}>
                              {event.stage}
                            </span>
                            <span style={{
                              color: '#141413',
                              overflow: 'hidden',
                              textOverflow: 'ellipsis',
                              whiteSpace: 'nowrap',
                            }} title={event.title}>
                              {event.title}
                            </span>
                          </div>
                          <div style={{
                            color: '#87867f',
                            fontSize: 10,
                            fontFamily: 'var(--font-body), monospace',
                            overflowWrap: 'anywhere',
                          }}>
                            {t('timelineSource')}: {event.source_table ?? '--'} · {formatTimelineTime(event.timestamp)}
                          </div>
                          {event.description && (
                            <div style={{ color: '#5f5d56', fontSize: 10, lineHeight: 1.35, overflowWrap: 'anywhere' }}>
                              {compactTimelineText(event.description)}
                            </div>
                          )}
                          {route && (
                            <div style={{ color: '#4A90D9', fontSize: 10, lineHeight: 1.35, overflowWrap: 'anywhere' }}>
                              {t('timelineRoute')}: {route}
                            </div>
                          )}
                          {writes && (
                            <div style={{ color: '#166534', fontSize: 10, lineHeight: 1.35, overflowWrap: 'anywhere' }}>
                              {t('timelineWrites')}: {writes}
                            </div>
                          )}
                        </div>
                      )
                    })}
                  </div>
                </div>
              )}
            </>
          )}
        </div>
      )}

      {/* AI 提案行（可展開）*/}
      {decisionAction && (
        <>
          <button
            onClick={() => setAiExpanded(v => !v)}
            style={{
              margin: '0 14px 10px',
              padding: '7px 0',
              background: 'rgba(217,119,87,0.08)',
              border: '0.5px solid rgba(217,119,87,0.3)',
              borderRadius: 6,
              cursor: 'pointer',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              gap: 5,
              fontSize: 12,
              color: '#d97757',
              width: 'calc(100% - 28px)',
              textAlign: 'center' as const,
              fontFamily: 'inherit',
            }}
          >
            <span>{t('aiProposalPreview', { action: compactTimelineText(decisionAction, '') })}</span>
          </button>
          {aiExpanded && (
            <div style={{
              margin: '0 10px 6px',
              padding: '8px',
              background: '#f0efe8',
              border: '0.5px solid #e0ddd4',
              borderLeft: '3px solid #d97757',
              borderRadius: 6,
              fontSize: 13,
              color: '#87867f',
            }}>
              <div style={{ color: '#141413', marginBottom: 4 }}>{decisionAction}</div>
              {decisionReasoning && (
                <div style={{ color: '#87867f', fontStyle: 'italic', display: 'flex', alignItems: 'flex-start', gap: 5 }}>
                  <Lightbulb aria-hidden="true" size={14} strokeWidth={1.8} style={{ flexShrink: 0, marginTop: 1 }} />
                  <span>{decisionReasoning.slice(0, 150)}{decisionReasoning.length > 150 ? '...' : ''}</span>
                </div>
              )}
            </div>
          )}
        </>
      )}

      {/* 授權按鈕（僅 waiting_approval 狀態）*/}
      {isWaitingApproval && (
        <div style={{ padding: '6px 14px 12px', display: 'flex', justifyContent: 'flex-end' }}>
          {renderApproveButtons()}
          {errorMessage && (
            <span style={{ fontSize: 12, color: '#cc2200', marginLeft: 6, maxWidth: 160, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={errorMessage}>
              {errorMessage}
            </span>
          )}
        </div>
      )}

    </div>
  )
}

export default IncidentCard
