'use client'

/**
 * TicketsPanel — 工單追蹤面板 (不含 AppLayout)
 * ==============================================
 * Sprint 5: 從 /tickets/page.tsx 抽取
 * 供原始頁面和整合頁面 (/operations) 共用
 *
 * 2026-05-31 Codex: 接上 Incident status-chain / timeline，讓 Tickets 不再只是清單。
 */

import { useEffect, useMemo, useState } from 'react'
import { useSearchParams } from 'next/navigation'
import { useTranslations } from 'next-intl'
import {
  ArrowRight,
  GitBranch,
  ListChecks,
  RefreshCw,
  SearchCheck,
  ShieldCheck,
  TriangleAlert,
} from 'lucide-react'

import { Link } from '@/i18n/routing'
import {
  AwoooPStatusChainPanel,
  type AwoooPStatusChain,
} from '@/components/awooop/status-chain'
import { cn } from '@/lib/utils'

const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? ''

interface Incident {
  incident_id?: string
  id?: string
  title?: string | null
  severity: string
  status: string
  signal_count?: number
  proposal_count?: number
  created_at: string
  updated_at?: string
  affected_services?: string[]
  affected_service?: string | null
}

interface IncidentListResponse {
  incidents: Incident[]
  count?: number
  total?: 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>
}

type IncidentTimelineStage = 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
}

const SEV_COLOR: Record<string, string> = {
  P0: '#cc2200',
  P1: '#F59E0B',
  P2: '#4A90D9',
  P3: '#22C55E',
}

const STATUS_COLOR: Record<string, string> = {
  open: '#cc2200',
  investigating: '#F59E0B',
  in_progress: '#F59E0B',
  mitigating: '#4A90D9',
  resolved: '#22C55E',
  closed: '#87867f',
}

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

function incidentId(incident?: Incident | null) {
  return incident?.incident_id ?? incident?.id ?? ''
}

function incidentServices(incident?: Incident | null) {
  const services = incident?.affected_services?.filter(Boolean) ?? []
  if (services.length > 0) return services
  return incident?.affected_service ? [incident.affected_service] : []
}

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

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

function FocusedIncidentTruthPanel({
  projectId,
  incident,
  incidentId,
  chain,
  timeline,
  loading,
  error,
}: {
  projectId: string
  incident?: Incident | null
  incidentId: string | null
  chain: AwoooPStatusChain | null
  timeline: IncidentTimelineResponse | null
  loading: boolean
  error: string | null
}) {
  const t = useTranslations('tickets.truth')
  const stages = timeline?.timeline?.filter((stage) => stage.status !== 'skipped') ?? []
  const verifier = timeline?.timeline?.find((stage) => stage.stage === 'verifier')
  const sourceCorrelation = chain?.source_refs?.correlation
  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 === 'executor' ||
      event.stage === 'verifier' ||
      event.stage === 'km' ||
      event.stage === 'ai_router'
    ))
    .slice(-5)
    .reverse()
  const title = incident?.title || timeline?.title || incidentServices(incident).join(' / ') || t('unknownTitle')
  const selectedIncidentId = incidentId || timeline?.incident_id || incidentId
  const encodedProjectId = encodeURIComponent(projectId)
  const encodedIncidentId = selectedIncidentId ? encodeURIComponent(selectedIncidentId) : ''

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

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

      <div className="grid gap-px bg-[#e0ddd4] md:grid-cols-4">
        <div className="min-w-0 bg-white px-4 py-3">
          <p className="text-xs font-semibold text-[#77736a]">{t('metrics.stages')}</p>
          <p className="mt-2 font-mono text-xl font-semibold text-[#141413]">
            {timeline ? stages.length : '--'}
          </p>
        </div>
        <div className="min-w-0 bg-white px-4 py-3">
          <p className="text-xs font-semibold text-[#77736a]">{t('metrics.events')}</p>
          <p className="mt-2 font-mono text-xl font-semibold text-[#141413]">
            {timeline ? timeline.events.length : '--'}
          </p>
        </div>
        <div className="min-w-0 bg-white px-4 py-3">
          <p className="text-xs font-semibold text-[#77736a]">{t('metrics.source')}</p>
          <p className="mt-2 truncate font-mono text-sm font-semibold text-[#141413]">
            {sourceCorrelation
              ? `${sourceCorrelation.direct_ref_total ?? 0}/${sourceCorrelation.candidate_total ?? 0}/${sourceCorrelation.applied_link_total ?? 0}`
              : '--'}
          </p>
        </div>
        <div className="min-w-0 bg-white px-4 py-3">
          <p className="text-xs font-semibold text-[#77736a]">{t('metrics.verification')}</p>
          <span className={cn('mt-2 inline-flex border px-2 py-0.5 text-xs font-semibold', timelineStatusClass(verifier?.status ?? chain?.verification))}>
            {verifier?.status ?? chain?.verification ?? '--'}
          </span>
        </div>
      </div>

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

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

        <div className="min-w-0 bg-white p-4">
          <div className="flex items-center gap-2">
            <ListChecks className="h-4 w-4 text-brand-accent" aria-hidden="true" />
            <h3 className="text-sm font-semibold text-[#141413]">{t('evidenceTitle')}</h3>
          </div>
          <div className="mt-3 grid gap-px border border-[#e0ddd4] bg-[#e0ddd4]">
            {[
              [t('executor'), timeline?.timeline?.find((stage) => stage.stage === 'executor')?.title ?? chain?.execution?.latest_operation_type ?? '--'],
              [t('ansible'), chain?.execution?.ansible?.latest_playbook_path ?? chain?.execution?.ansible?.latest_catalog_id ?? '--'],
              [t('mcp'), timeline?.timeline?.find((stage) => stage.stage === 'investigator')?.title ?? '--'],
              [t('km'), timeline?.timeline?.find((stage) => stage.stage === 'km')?.title ?? '--'],
            ].map(([label, value]) => (
              <div key={label} className="min-w-0 bg-white px-3 py-2">
                <p className="text-xs font-semibold text-[#77736a]">{label}</p>
                <p className="mt-1 truncate font-mono text-xs text-[#141413]" title={value}>
                  {value}
                </p>
              </div>
            ))}
          </div>
          {importantEvents.length > 0 ? (
            <div className="mt-3 divide-y divide-[#eee9dd] border border-[#eee9dd]">
              {importantEvents.map((event, index) => (
                <div key={`${event.stage}-${event.timestamp}-${index}`} className="px-3 py-2">
                  <div className="flex flex-wrap items-center gap-2">
                    <span className={cn('border px-2 py-0.5 text-[11px] font-semibold', timelineStatusClass(event.status))}>
                      {event.status}
                    </span>
                    <span className="font-mono text-[11px] text-[#77736a]">{event.source_table ?? '--'}</span>
                  </div>
                  <p className="mt-1 truncate text-xs font-semibold text-[#141413]" title={event.title}>
                    {event.title}
                  </p>
                </div>
              ))}
            </div>
          ) : null}
        </div>
      </div>
    </section>
  )
}

export function TicketsPanel() {
  const t = useTranslations('tickets')
  const searchParams = useSearchParams()
  const queryIncidentId = searchParams.get('incident_id')
  const projectId = searchParams.get('project_id') ?? 'awoooi'
  const [incidents, setIncidents] = useState<Incident[]>([])
  const [total, setTotal] = useState(0)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [chain, setChain] = useState<AwoooPStatusChain | null>(null)
  const [timeline, setTimeline] = useState<IncidentTimelineResponse | null>(null)
  const [detailLoading, setDetailLoading] = useState(false)
  const [detailError, setDetailError] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false
    setLoading(true)
    setError(null)
    fetchJson<IncidentListResponse>(`${API_BASE}/api/v1/incidents`, 12_000)
      .then((data) => {
        if (cancelled) return
        if (!data) {
          setError(t('error'))
          setIncidents([])
          setTotal(0)
          return
        }
        setIncidents(data.incidents ?? [])
        setTotal(data.count ?? data.total ?? data.incidents?.length ?? 0)
      })
      .catch((err) => {
        if (!cancelled) setError(err instanceof Error ? err.message : t('error'))
      })
      .finally(() => {
        if (!cancelled) setLoading(false)
      })
    return () => {
      cancelled = true
    }
  }, [t])

  const selectedIncidentId = useMemo(() => {
    if (queryIncidentId) return queryIncidentId
    return incidentId(incidents[0]) || null
  }, [incidents, queryIncidentId])

  const selectedIncident = useMemo(
    () => incidents.find((incident) => incidentId(incident) === selectedIncidentId) ?? null,
    [incidents, selectedIncidentId]
  )

  useEffect(() => {
    let cancelled = false
    if (!selectedIncidentId) {
      setChain(null)
      setTimeline(null)
      setDetailError(null)
      setDetailLoading(false)
      return () => {
        cancelled = true
      }
    }
    const targetIncidentId = selectedIncidentId

    async function loadIncidentTruth() {
      setDetailLoading(true)
      setDetailError(null)
      const encodedProjectId = encodeURIComponent(projectId)
      const encodedIncidentId = encodeURIComponent(targetIncidentId)
      const [statusChain, incidentTimeline] = await Promise.all([
        fetchJson<AwoooPStatusChain>(
          `${API_BASE}/api/v1/platform/status-chain?project_id=${encodedProjectId}&incident_id=${encodedIncidentId}`,
          12_000
        ),
        fetchJson<IncidentTimelineResponse>(
          `${API_BASE}/api/v1/incidents/${encodedIncidentId}/timeline`,
          12_000
        ),
      ])
      if (cancelled) return
      setChain(statusChain)
      setTimeline(incidentTimeline)
      if (!statusChain && !incidentTimeline) {
        setDetailError(t('truth.loadFailed'))
      }
      setDetailLoading(false)
    }

    loadIncidentTruth()
    return () => {
      cancelled = true
    }
  }, [projectId, selectedIncidentId, t])

  return (
    <div className="min-h-full space-y-5 bg-[#f5f4ed] p-6">
      <div>
        <h1 className="m-0 text-lg font-bold text-[#141413]">{t('title')}</h1>
        <p className="mt-1 text-xs text-[#87867f]">{t('subtitle')}</p>
      </div>

      <FocusedIncidentTruthPanel
        projectId={projectId}
        incident={selectedIncident}
        incidentId={selectedIncidentId}
        chain={chain}
        timeline={timeline}
        loading={detailLoading}
        error={detailError}
      />

      <section className="overflow-hidden 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 text-sm font-bold text-[#141413]">
            <span className="h-1.5 w-1.5 rounded-full bg-[#d97757]" />
            {t('title')} ({loading ? '...' : total})
          </div>
          <span className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2 py-1 text-xs font-semibold text-[#5f5b52]">
            <ShieldCheck className="h-3.5 w-3.5" aria-hidden="true" />
            {t('readOnly')}
          </span>
        </div>
        {loading ? (
          <div className="px-8 py-10 text-center text-sm text-[#87867f]">{t('loading')}</div>
        ) : error ? (
          <div className="px-8 py-10 text-center text-sm text-[#cc2200]">{error}</div>
        ) : incidents.length === 0 ? (
          <div className="px-8 py-10 text-center text-sm text-[#87867f]">{t('noTickets')}</div>
        ) : (
          <div className="overflow-x-auto">
            <table className="w-full border-collapse text-sm">
              <thead>
                <tr className="bg-[#faf9f3]">
                  {[t('id'), t('title_col'), t('priority'), t('status'), t('signals'), t('createdAt'), t('actions')].map((col) => (
                    <th key={col} className="border-b border-[#e0ddd4] px-4 py-3 text-left text-xs font-semibold text-[#87867f]">
                      {col}
                    </th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {incidents.map((incident) => {
                  const rowIncidentId = incidentId(incident)
                  const isSelected = rowIncidentId === selectedIncidentId
                  const services = incidentServices(incident)
                  const title = incident.title || services.join(' / ') || t('unknownService')
                  return (
                    <tr
                      key={rowIncidentId || `${incident.created_at}-${title}`}
                      className={cn(
                        'border-b border-[#f0ede4]',
                        isSelected && 'bg-[#fff7e8]'
                      )}
                    >
                      <td className="px-4 py-3 align-top font-mono text-xs text-[#5f5b52]">
                        <Link
                          href={`/tickets?project_id=${encodeURIComponent(projectId)}&incident_id=${encodeURIComponent(rowIncidentId)}` as never}
                          className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2 py-1 font-semibold text-[#141413] hover:border-[#d97757]"
                        >
                          {rowIncidentId.slice(0, 16)}
                          <ArrowRight className="h-3 w-3" aria-hidden="true" />
                        </Link>
                      </td>
                      <td className="max-w-[360px] px-4 py-3 align-top font-medium text-[#141413]">
                        <div className="truncate" title={title}>{title}</div>
                        {services.length > 1 ? (
                          <div className="mt-1 truncate text-xs text-[#87867f]">
                            {t('serviceCount', { count: services.length })}
                          </div>
                        ) : null}
                      </td>
                      <td className="px-4 py-3 align-top">
                        <span
                          style={{
                            color: SEV_COLOR[incident.severity] ?? '#87867f',
                            background: `${SEV_COLOR[incident.severity] ?? '#87867f'}18`,
                            borderColor: `${SEV_COLOR[incident.severity] ?? '#87867f'}40`,
                          }}
                          className="inline-flex border px-2 py-0.5 text-xs font-bold"
                        >
                          {incident.severity}
                        </span>
                      </td>
                      <td className="px-4 py-3 align-top">
                        <span
                          style={{ color: STATUS_COLOR[incident.status] ?? '#87867f' }}
                          className="text-xs font-semibold"
                        >
                          {incident.status}
                        </span>
                      </td>
                      <td className="px-4 py-3 align-top font-mono text-xs text-[#5f5b52]">
                        {t('signalProposal', {
                          signals: incident.signal_count ?? 0,
                          proposals: incident.proposal_count ?? 0,
                        })}
                      </td>
                      <td className="px-4 py-3 align-top text-xs text-[#87867f]">
                        {formatLocalTime(incident.created_at)}
                      </td>
                      <td className="px-4 py-3 align-top">
                        <Link
                          href={`/awooop/work-items?project_id=${encodeURIComponent(projectId)}&incident_id=${encodeURIComponent(rowIncidentId)}` as never}
                          className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2 py-1 text-xs font-semibold text-[#2e2b26] hover:border-[#1f6feb] hover:bg-[#edf4ff] hover:text-[#0f4fa8]"
                        >
                          <SearchCheck className="h-3.5 w-3.5" aria-hidden="true" />
                          {t('openTruth')}
                        </Link>
                      </td>
                    </tr>
                  )
                })}
              </tbody>
            </table>
          </div>
        )}
      </section>
    </div>
  )
}
