/**
 * GearDetailPanel — 어구 그룹 판정 상세 사이드 패널
 *
 * 테이블 행 클릭 시 우측에 슬라이드 표시.
 * G코드 위반 내역, 어구 그룹 정보, 모선 추론 결과 + correlation 상세,
 * 궤적 리플레이 시작 버튼을 종합 표시.
 */
import { useEffect, useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { Badge } from '@shared/components/ui/badge';
import { Button } from '@shared/components/ui/button';
import {
  getGearViolationIntent,
  getGearViolationLabel,
  getGearViolationDesc,
  GEAR_VIOLATION_CODES,
} from '@shared/constants/gearViolationCodes';
import { getZoneCodeIntent, getZoneCodeLabel, getZoneAllowedGears } from '@shared/constants/zoneCodes';
import { getGearJudgmentIntent } from '@shared/constants/permissionStatuses';
import { X, Anchor, MapPin, ShieldAlert, Users, Ship, Play, TrendingUp, Loader2, CheckCircle, XCircle, BarChart3 } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useSettingsStore } from '@stores/settingsStore';
import { fetchGroupCorrelations, fetchVesselTracks, fetchCandidateMetrics, resolveParent, type CandidateMetricItem } from '@/services/vesselAnalysisApi';
import { useGearReplayStore } from '@stores/gearReplayStore';

interface CorrelationItem {
  targetMmsi: string;
  targetName: string;
  targetType: string;
  score: number;
  streak: number;
  freezeState: string;
  proximityRatio: number;
  visitScore: number;
  headingCoherence: number;
  modelName: string;
}

interface GearData {
  id: string;
  groupKey: string;
  type: string;
  owner: string;
  zone: string;
  status: string;
  permit: string;
  risk: string;
  lat: number;
  lng: number;
  parentStatus: string;
  parentMmsi: string;
  confidence: string;
  gCodes: string[];
  gearViolationScore: number;
  gearViolationEvidence: Record<string, Record<string, unknown>>;
  pairTrawlDetected: boolean;
  pairTrawlPairMmsi: string;
  memberCount: number;
  members: Array<{ mmsi: string; name?: string; lat?: number; lon?: number; role?: string; isParent?: boolean }>;
  allowedGears: string[];
}

interface GearDetailPanelProps {
  gear: GearData | null;
  onClose: () => void;
}

export function GearDetailPanel({ gear, onClose }: GearDetailPanelProps) {
  const navigate = useNavigate();
  const { t } = useTranslation('detection');
  const lang = useSettingsStore((s) => s.language);
  const [correlations, setCorrelations] = useState<CorrelationItem[]>([]);
  const [corrLoading, setCorrLoading] = useState(false);
  const [selectedCandidates, setSelectedCandidates] = useState<Set<string>>(new Set());
  const [selectedDetail, setSelectedDetail] = useState<string | null>(null); // 상세 보기 중인 후보 MMSI
  const [detailMetrics, setDetailMetrics] = useState<CandidateMetricItem[]>([]);
  const [detailLoading, setDetailLoading] = useState(false);
  const [resolveLoading, setResolveLoading] = useState(false);
  const [resolveMsg, setResolveMsg] = useState('');
  const replayGroupKey = useGearReplayStore(s => s.groupKey);

  const toggleCandidate = useCallback((mmsi: string) => {
    setSelectedCandidates(prev => {
      const next = new Set(prev);
      if (next.has(mmsi)) next.delete(mmsi); else next.add(mmsi);
      return next;
    });
  }, []);

  // 후보 상세 메트릭 로드
  const loadCandidateDetail = useCallback(async (mmsi: string) => {
    if (!gear) return;
    setSelectedDetail(mmsi);
    setDetailLoading(true);
    setResolveMsg('');
    try {
      const data = await fetchCandidateMetrics(gear.groupKey, mmsi);
      setDetailMetrics(data.items ?? []);
    } catch {
      setDetailMetrics([]);
    } finally {
      setDetailLoading(false);
    }
  }, [gear]);

  // 모선 확정/제외 처리
  const handleResolve = useCallback(async (action: 'confirm' | 'reject') => {
    if (!gear || !selectedDetail) return;
    setResolveLoading(true);
    setResolveMsg('');
    try {
      const result = await resolveParent(gear.groupKey, action, selectedDetail);
      if (result.ok) {
        setResolveMsg(action === 'confirm' ? '모선 확정 완료' : '후보 제외 완료');
      } else {
        setResolveMsg(result.message ?? '처리 실패');
      }
    } catch {
      setResolveMsg('요청 실패');
    } finally {
      setResolveLoading(false);
    }
  }, [gear, selectedDetail]);

  const loadCorrelations = useCallback(async (groupKey: string) => {
    setCorrLoading(true);
    try {
      const data = await fetchGroupCorrelations(groupKey, 0.3);
      // API 응답을 CorrelationItem 형태로 매핑
      const items = Array.isArray(data) ? data : ((data as Record<string, unknown>)?.items as unknown[]) ?? [];
      // 같은 MMSI가 여러 모델에서 중복 → default 모델 우선, 없으면 첫 번째
      const byMmsi = new Map<string, CorrelationItem>();
      for (const item of items) {
        const d = item as Record<string, unknown>;
        const mmsi = String(d.targetMmsi ?? '');
        const mapped: CorrelationItem = {
          targetMmsi: mmsi,
          targetName: String(d.target_name ?? d.targetName ?? ''),
          targetType: String(d.target_type ?? d.targetType ?? ''),
          score: Number(d.score ?? d.current_score ?? d.currentScore ?? 0),
          streak: Number(d.streak ?? d.streak_count ?? d.streakCount ?? 0),
          freezeState: String(d.freeze_state ?? d.freezeState ?? 'ACTIVE'),
          proximityRatio: Number(d.proximity_ratio ?? d.proximityRatio ?? 0),
          visitScore: Number(d.visit_score ?? d.visitScore ?? 0),
          headingCoherence: Number(d.heading_coherence ?? d.headingCoherence ?? 0),
          modelName: String(d.model_name ?? d.modelName ?? 'default'),
        };
        const existing = byMmsi.get(mmsi);
        if (!existing || mapped.modelName === 'default' || mapped.score > existing.score) {
          byMmsi.set(mmsi, mapped);
        }
      }
      const sorted = Array.from(byMmsi.values()).sort((a, b) => b.score - a.score);
      setCorrelations(sorted);
      // 최고 일치율 후보 자동 선택
      if (sorted.length > 0) {
        loadCandidateDetail(sorted[0].targetMmsi);
      }
    } catch {
      setCorrelations([]);
    } finally {
      setCorrLoading(false);
    }
  }, [loadCandidateDetail]);

  useEffect(() => {
    if (gear?.groupKey) {
      loadCorrelations(gear.groupKey);
    } else {
      setCorrelations([]);
    }
  }, [gear?.groupKey, loadCorrelations]);

  const [replayLoading, setReplayLoading] = useState(false);

  const handleStartReplay = useCallback(async () => {
    if (!gear) return;
    setReplayLoading(true);
    try {
      // 자체 백엔드 API로 group history + correlation 조회
      const groupKeyEnc = encodeURIComponent(gear.groupKey);
      const [detailRes, tracksRes] = await Promise.all([
        fetch(`/api/vessel-analysis/groups/${groupKeyEnc}/detail`)
          .then(r => r.json()).catch(() => ({ history: [] })),
        fetch(`/api/vessel-analysis/groups/${groupKeyEnc}/correlations?minScore=0.1`)
          .then(r => r.json()).catch(() => ({ items: [] })),
      ]);
      // detailRes.history → frames 변환
      const historyRes = { frames: (detailRes.history ?? []) };

      const frames = (historyRes.frames ?? []).map((f: Record<string, unknown>) => ({
        snapshotTime: String(f.snapshotTime ?? ''),
        centerLat: Number(f.centerLat ?? 0),
        centerLon: Number(f.centerLon ?? 0),
        memberCount: Number(f.memberCount ?? 0),
        polygon: f.polygon ?? null,
        members: Array.isArray(f.members) ? f.members : [],
      }));

      // correlation 응답 → loadHistory용 CorrelationItem 형태로 매핑
      const corrItems = tracksRes.items ?? [];
      const replayCorrelations = Array.isArray(corrItems) ? corrItems.map((v: Record<string, unknown>) => ({
        targetMmsi: String(v.targetMmsi ?? ''),
        targetName: String(v.targetName ?? ''),
        score: Number(v.score ?? v.currentScore ?? 0),
        freezeState: String(v.freezeState ?? 'ACTIVE'),
      })) : [];

      if (frames.length === 0) {
        frames.push({
          snapshotTime: new Date().toISOString(),
          centerLat: gear.lat,
          centerLon: gear.lng,
          memberCount: gear.memberCount,
          polygon: null,
          members: gear.members ?? [],
        });
      }

      // 선택된 후보 선박 항적 조회 (24시간)
      let candidateTracks: { vesselId: string; shipName: string; geometry: [number, number][]; timestamps: string[] }[] = [];
      if (selectedCandidates.size > 0) {
        const now = new Date();
        const h24ago = new Date(now.getTime() - 24 * 3600_000);
        try {
          candidateTracks = await fetchVesselTracks(
            [...selectedCandidates],
            h24ago.toISOString(),
            now.toISOString(),
          );
        } catch {
          // 항적 조회 실패해도 리플레이는 진행
        }
      }

      useGearReplayStore.getState().loadHistory(gear.groupKey, frames, replayCorrelations, candidateTracks);
      useGearReplayStore.getState().play();
    } catch {
      // silent fallback
    } finally {
      setReplayLoading(false);
    }
  }, [gear, selectedCandidates]);

  if (!gear) return null;

  const allowedGears = gear.allowedGears.length > 0
    ? gear.allowedGears
    : getZoneAllowedGears(gear.zone);

  const parentStatusLabel =
    gear.parentStatus === 'DIRECT_PARENT_MATCH' ? '직접매칭' :
    gear.parentStatus === 'AUTO_PROMOTED' ? '자동승격' :
    gear.parentStatus === 'REVIEW_REQUIRED' ? '심사필요' :
    gear.parentStatus === 'UNRESOLVED' ? '미결정' :
    gear.parentStatus;

  const parentStatusIntent: 'success' | 'info' | 'warning' | 'muted' =
    gear.parentStatus === 'DIRECT_PARENT_MATCH' ? 'success' :
    gear.parentStatus === 'AUTO_PROMOTED' ? 'info' :
    gear.parentStatus === 'REVIEW_REQUIRED' ? 'warning' :
    'muted';

  const hasPairTrawl = gear.pairTrawlDetected || gear.gCodes.includes('G-06');
  const isReplayActive = replayGroupKey === gear.groupKey;

  return (
    <div className="fixed inset-y-0 right-0 w-[420px] bg-background border-l border-border z-50 overflow-y-auto shadow-2xl">
      {/* 헤더 */}
      <div className="sticky top-0 bg-background/95 backdrop-blur-sm border-b border-border px-4 py-3 flex items-center justify-between z-10">
        <div className="flex items-center gap-2">
          <ShieldAlert className="w-4 h-4 text-orange-400" />
          <span className="font-bold text-heading text-sm">어구 판정 상세</span>
          <span className="text-xs font-mono font-bold text-hint">{gear.id}</span>
          <Badge intent={getZoneCodeIntent(gear.zone)} size="sm">
            {getZoneCodeLabel(gear.zone, t, lang)}
          </Badge>
        </div>
        <button type="button" onClick={onClose} className="p-1 hover:bg-surface-raised rounded" aria-label="닫기">
          <X className="w-4 h-4 text-hint" />
        </button>
      </div>

      <div className="p-4 space-y-4">
        {/* G코드 위반 내역 */}
        {gear.gCodes.length > 0 && (
          <div className="bg-surface-raised rounded-lg p-3 space-y-2">
            <div className="flex items-center gap-2 text-xs">
              <ShieldAlert className="w-3.5 h-3.5 text-red-400" />
              <span className="text-label font-medium">G코드 위반 내역</span>
              <span className="text-hint text-[10px]">총 {gear.gearViolationScore}점</span>
            </div>
            <div className="space-y-1.5">
              {gear.gCodes.map((code) => {
                const meta = GEAR_VIOLATION_CODES[code as keyof typeof GEAR_VIOLATION_CODES];
                return (
                  <div key={code} className="flex items-start gap-2 py-1.5 border-b border-border last:border-0">
                    <Badge intent={getGearViolationIntent(code)} size="sm" className="shrink-0 mt-0.5">{code}</Badge>
                    <div className="flex-1 min-w-0">
                      <div className="text-xs text-label font-medium">{getGearViolationLabel(code, t, lang)}</div>
                      <div className="text-[10px] text-hint mt-0.5">{getGearViolationDesc(code, lang)}</div>
                    </div>
                    {meta && <span className="text-[10px] font-mono text-label shrink-0">+{meta.score}pt</span>}
                  </div>
                );
              })}
            </div>
          </div>
        )}

        {/* 어구 그룹 정보 */}
        <div className="bg-surface-raised rounded-lg p-3 space-y-2">
          <div className="flex items-center gap-2 text-xs">
            <Anchor className="w-3.5 h-3.5 text-orange-400" />
            <span className="text-label font-medium">어구 그룹 정보</span>
          </div>
          <div className="grid grid-cols-2 gap-y-1 text-xs">
            <span className="text-hint">그룹 키</span>
            <span className="text-label text-right font-mono text-[10px]">{gear.groupKey}</span>
            <span className="text-hint">그룹 유형</span>
            <span className="text-label text-right">{gear.type}</span>
            <span className="text-hint">모선/소유자</span>
            <span className="text-label text-right font-mono">{gear.owner}</span>
            <span className="text-hint">구성원 수</span>
            <span className="text-label text-right">{gear.memberCount > 0 ? `${gear.memberCount}척` : '-'}</span>
            <span className="text-hint">설치 수역</span>
            <span className="text-right">
              <Badge intent={getZoneCodeIntent(gear.zone)} size="sm">{getZoneCodeLabel(gear.zone, t, lang)}</Badge>
            </span>
            <span className="text-hint">판정 상태</span>
            <span className="text-right">
              <Badge intent={getGearJudgmentIntent(gear.status)} size="sm">{gear.status}</Badge>
            </span>
            <span className="text-hint">허용 어구</span>
            <span className="text-label text-right text-[10px]">{allowedGears.length > 0 ? allowedGears.join(', ') : '없음'}</span>
            <span className="text-hint">위치</span>
            <span className="text-label text-right font-mono text-[10px]">
              {gear.lat.toFixed(4)}°N {gear.lng.toFixed(4)}°E
            </span>
          </div>
        </div>

        {/* 모선 추론 정보 */}
        <div className="bg-surface-raised rounded-lg p-3 space-y-2">
          <div className="flex items-center gap-2 text-xs">
            <Ship className="w-3.5 h-3.5 text-cyan-400" />
            <span className="text-label font-medium">모선 추론</span>
            <Badge intent={parentStatusIntent} size="sm">{parentStatusLabel}</Badge>
          </div>
          <div className="grid grid-cols-2 gap-y-1 text-xs">
            <span className="text-hint">추정 모선</span>
            <span className="text-label text-right font-mono">
              {gear.parentMmsi !== '-' && gear.parentMmsi ? (
                <button type="button" className="text-cyan-400 hover:underline"
                  onClick={() => navigate(`/vessel/${gear.parentMmsi}`)}>
                  {gear.parentMmsi}
                </button>
              ) : '-'}
            </span>
            <span className="text-hint">후보 수</span>
            <span className="text-label text-right">{gear.confidence}</span>
          </div>
        </div>

        {/* 모선 추론 후보 상세 (Correlation) */}
        <div className="bg-surface-raised rounded-lg p-3 space-y-2">
          <div className="flex items-center gap-2 text-xs">
            <TrendingUp className="w-3.5 h-3.5 text-purple-400" />
            <span className="text-label font-medium">추론 후보 상세</span>
            {corrLoading && <Loader2 className="w-3 h-3 animate-spin text-hint" />}
            <span className="text-hint text-[10px]">{correlations.length}건</span>
          </div>
          {correlations.length > 0 ? (
            <div className="space-y-1.5 max-h-[240px] overflow-y-auto">
              {correlations.sort((a, b) => b.score - a.score).map((c, i) => (
                <div key={`${i}-${c.targetMmsi}`}
                  onClick={(e) => {
                    // 체크박스, MMSI 링크 클릭은 전파 방지됨 → 나머지 영역 클릭 시 상세 로드
                    if ((e.target as HTMLElement).closest('input, a, button')) return;
                    loadCandidateDetail(c.targetMmsi);
                  }}
                  className={`py-1.5 px-2 rounded text-xs cursor-pointer transition-colors ${
                    selectedDetail === c.targetMmsi
                      ? 'bg-purple-500/10 border border-purple-500/30'
                      : c.targetMmsi === gear.parentMmsi
                        ? 'bg-cyan-500/10 border border-cyan-500/30'
                        : 'hover:bg-surface-overlay'
                  }`}>
                  <div className="flex items-center gap-2">
                    <input type="checkbox" checked={selectedCandidates.has(c.targetMmsi)}
                      onChange={() => toggleCandidate(c.targetMmsi)}
                      className="w-3 h-3 rounded border-border accent-emerald-500 shrink-0 cursor-pointer"
                      aria-label={`${c.targetMmsi} 리플레이 선택`} />
                    <button type="button"
                      className="text-cyan-400 hover:underline font-mono text-[11px]"
                      onClick={() => navigate(`/vessel/${c.targetMmsi}`)}>
                      {c.targetMmsi}
                    </button>
                    <span className="text-hint text-[9px] truncate max-w-[80px]">{c.targetName}</span>
                    <div className="flex-1" />
                    <div className="w-14 h-1.5 bg-surface-overlay rounded-full overflow-hidden">
                      <div className="h-full rounded-full transition-all"
                        style={{
                          width: `${Math.min(100, c.score * 100)}%`,
                          backgroundColor: c.score >= 0.72 ? '#10b981' : c.score >= 0.5 ? '#f59e0b' : '#64748b',
                        }} />
                    </div>
                    <span className="font-mono text-[10px] text-label w-10 text-right">
                      {(c.score * 100).toFixed(0)}%
                    </span>
                    <Badge intent={c.freezeState === 'ACTIVE' ? 'success' : 'muted'} size="sm">
                      {c.freezeState === 'ACTIVE' ? '활성' : c.freezeState.slice(0, 4)}
                    </Badge>
                  </div>
                  <div className="flex items-center gap-2 mt-1 ml-6 text-[9px] text-hint">
                    <span>근접: {(c.proximityRatio * 100).toFixed(0)}%</span>
                    {c.visitScore > 0 && <span>방문: {(c.visitScore * 100).toFixed(0)}%</span>}
                    {c.headingCoherence > 0 && <span>방향: {(c.headingCoherence * 100).toFixed(0)}%</span>}
                    <span>연속: {c.streak}회</span>
                    <Badge intent={c.targetType === 'VESSEL' ? 'info' : 'muted'} size="sm">
                      {c.targetType === 'VESSEL' ? '선박' : c.targetType === 'GEAR_BUOY' ? '어구' : c.targetType}
                    </Badge>
                  </div>
                </div>
              ))}
            </div>
          ) : !corrLoading ? (
            <div className="text-hint text-[10px] text-center py-2">추론 후보 데이터 없음</div>
          ) : null}
          {correlations.length > 0 && (
            <div className="flex items-center gap-3 pt-1.5 border-t border-border text-[9px] text-hint">
              <span>근접: 어구-선박 근접도</span>
              <span>방문: 어구 구역 방문 빈도</span>
              <span>방향: 침로 일관성</span>
            </div>
          )}
        </div>

        {/* ── 후보 상세 검토 패널 ── */}
        {selectedDetail && (() => {
          const cand = correlations.find(c => c.targetMmsi === selectedDetail);
          if (!cand) return null;
          // 최근 메트릭 평균 계산
          const avg = (arr: (number | null)[]): number | null => {
            const valid = arr.filter((v): v is number => v != null && v > 0);
            return valid.length > 0 ? valid.reduce((a, b) => a + b, 0) / valid.length : null;
          };
          const avgProximity = avg(detailMetrics.map(m => m.proximityRatio));
          const avgVisit = avg(detailMetrics.map(m => m.visitScore));
          const avgActivity = avg(detailMetrics.map(m => m.activitySync));
          const avgDtw = avg(detailMetrics.map(m => m.dtwSimilarity));
          const avgSpeed = avg(detailMetrics.map(m => m.speedCorrelation));
          const avgHeading = avg(detailMetrics.map(m => m.headingCoherence));
          const avgDrift = avg(detailMetrics.map(m => m.driftSimilarity));
          const shadowStayCount = detailMetrics.filter(m => m.shadowStay).length;
          const shadowReturnCount = detailMetrics.filter(m => m.shadowReturn).length;

          const pct = (v: number | null) => v != null ? `${(v * 100).toFixed(1)}%` : '-';
          const bar = (v: number | null, color: string) => (
            <div className="w-full h-1.5 bg-surface-overlay rounded-full overflow-hidden">
              <div className="h-full rounded-full" style={{ width: `${Math.min(100, (v ?? 0) * 100)}%`, backgroundColor: color }} />
            </div>
          );

          return (
            <div className="bg-surface-raised rounded-lg p-3 space-y-3 border border-purple-500/30">
              {/* 헤더 */}
              <div className="flex items-center gap-2 text-xs">
                <BarChart3 className="w-3.5 h-3.5 text-purple-400" />
                <span className="text-label font-medium">후보 검토</span>
                <span className="text-cyan-400 font-mono text-[11px]">{cand.targetMmsi}</span>
                <span className="text-hint text-[9px] truncate">{cand.targetName}</span>
              </div>

              {/* 종합 점수 */}
              <div className="flex items-center gap-3 py-2 border-y border-border">
                <div className="text-center flex-1">
                  <div className={`text-lg font-bold ${cand.score >= 0.72 ? 'text-green-400' : cand.score >= 0.5 ? 'text-yellow-400' : 'text-hint'}`}>
                    {(cand.score * 100).toFixed(1)}%
                  </div>
                  <div className="text-[9px] text-hint">종합 일치율</div>
                </div>
                <div className="text-center flex-1">
                  <div className="text-sm font-bold text-label">{cand.streak}회</div>
                  <div className="text-[9px] text-hint">연속 관측</div>
                </div>
                <div className="text-center flex-1">
                  <div className="text-sm font-bold text-label">{detailMetrics.length}건</div>
                  <div className="text-[9px] text-hint">raw 메트릭</div>
                </div>
              </div>

              {/* 점수 근거 상세 */}
              {detailLoading ? (
                <div className="flex items-center justify-center py-4"><Loader2 className="w-4 h-4 animate-spin text-hint" /></div>
              ) : (
                <div className="space-y-2 text-[10px]">
                  <div className="text-hint font-medium text-[9px]">관측 지표 (최근 {detailMetrics.length}건 평균)</div>
                  {[
                    { label: '근접도', value: avgProximity, color: '#06b6d4', desc: '어구-선박 거리 근접 비율' },
                    { label: '방문 점수', value: avgVisit, color: '#8b5cf6', desc: '어구 구역 방문 빈도' },
                    { label: '활동 동기화', value: avgActivity, color: '#f59e0b', desc: '어구-선박 활동 시간 일치' },
                    { label: 'DTW 유사도', value: avgDtw, color: '#ec4899', desc: '궤적 형태 유사도' },
                    { label: '속도 상관', value: avgSpeed, color: '#14b8a6', desc: '속도 변화 패턴 일치' },
                    { label: '침로 일관성', value: avgHeading, color: '#3b82f6', desc: '방향 변화 일치' },
                    { label: '드리프트 유사도', value: avgDrift, color: '#64748b', desc: '표류 패턴 유사' },
                  ].map(({ label, value, color, desc }) => (
                    <div key={label} className="flex items-center gap-2">
                      <span className="w-[70px] text-label shrink-0">{label}</span>
                      <div className="flex-1">{bar(value, color)}</div>
                      <span className="w-10 text-right font-mono text-label">{pct(value)}</span>
                      <span className="w-[90px] text-hint text-[8px] truncate" title={desc}>{desc}</span>
                    </div>
                  ))}

                  {/* 보정 지표 */}
                  <div className="text-hint font-medium text-[9px] pt-1 border-t border-border">보정 지표</div>
                  <div className="grid grid-cols-2 gap-y-1 gap-x-4">
                    <span className="text-hint">섀도우 체류</span>
                    <span className="text-label text-right">{shadowStayCount}/{detailMetrics.length}건</span>
                    <span className="text-hint">섀도우 복귀</span>
                    <span className="text-label text-right">{shadowReturnCount}/{detailMetrics.length}건</span>
                    <span className="text-hint">동결 상태</span>
                    <span className="text-right"><Badge intent={cand.freezeState === 'ACTIVE' ? 'success' : 'muted'} size="sm">{cand.freezeState === 'ACTIVE' ? '활성' : cand.freezeState}</Badge></span>
                    <span className="text-hint">선박 유형</span>
                    <span className="text-right"><Badge intent={cand.targetType === 'VESSEL' ? 'info' : 'muted'} size="sm">{cand.targetType === 'VESSEL' ? '선박' : cand.targetType === 'GEAR_BUOY' ? '어구' : cand.targetType}</Badge></span>
                  </div>
                </div>
              )}

              {/* 확정/제외 버튼 */}
              {resolveMsg && (
                <div className={`text-[10px] text-center py-1 rounded ${resolveMsg.includes('완료') ? 'text-green-400 bg-green-500/10' : 'text-red-400 bg-red-500/10'}`}>
                  {resolveMsg}
                </div>
              )}
              <div className="flex gap-2">
                <Button variant="primary" size="sm" className="flex-1"
                  onClick={() => handleResolve('confirm')} disabled={resolveLoading}>
                  <CheckCircle className="w-3.5 h-3.5 mr-1" />
                  {resolveLoading ? '처리 중...' : '모선 확정'}
                </Button>
                <Button variant="outline" size="sm" className="flex-1"
                  onClick={() => handleResolve('reject')} disabled={resolveLoading}>
                  <XCircle className="w-3.5 h-3.5 mr-1" />
                  후보 제외
                </Button>
              </div>
            </div>
          );
        })()}

        {/* 궤적 리플레이 버튼 */}
        <Button
          variant={isReplayActive ? 'secondary' : 'primary'}
          size="sm"
          className="w-full"
          onClick={handleStartReplay}
          disabled={replayLoading}
        >
          {replayLoading ? (
            <Loader2 className="w-3.5 h-3.5 mr-1 animate-spin" />
          ) : (
            <Play className="w-3.5 h-3.5 mr-1" />
          )}
          {replayLoading ? '데이터 로딩...'
            : isReplayActive ? '리플레이 재시작'
            : selectedCandidates.size > 0
              ? `리플레이 (후보 ${selectedCandidates.size}척 포함)`
              : '24시간 궤적 리플레이'}
        </Button>

        {/* 쌍끌이 감지 정보 */}
        {hasPairTrawl && (
          <div className="bg-surface-raised rounded-lg p-3 space-y-2">
            <div className="flex items-center gap-2 text-xs">
              <Users className="w-3.5 h-3.5 text-red-400" />
              <span className="text-label font-medium">쌍끌이 트롤 공조</span>
              <Badge intent="critical" size="sm">G-06</Badge>
            </div>
            <div className="grid grid-cols-2 gap-y-1 text-xs">
              <span className="text-hint">상대 선박</span>
              <span className="text-label text-right font-mono">
                {gear.pairTrawlPairMmsi ? (
                  <button type="button" className="text-cyan-400 hover:underline"
                    onClick={() => navigate(`/vessel/${gear.pairTrawlPairMmsi}`)}>
                    {gear.pairTrawlPairMmsi}
                  </button>
                ) : '-'}
              </span>
              {gear.gearViolationEvidence['G-06'] && (() => {
                const ev = gear.gearViolationEvidence['G-06'];
                return (
                  <>
                    {ev.sync_duration_min != null && (
                      <>
                        <span className="text-hint">동기 지속</span>
                        <span className="text-label text-right font-mono">{String(ev.sync_duration_min)}분</span>
                      </>
                    )}
                    {ev.mean_separation_nm != null && (
                      <>
                        <span className="text-hint">평균 간격</span>
                        <span className="text-label text-right font-mono">{(Number(ev.mean_separation_nm) * 1852).toFixed(0)}m</span>
                      </>
                    )}
                  </>
                );
              })()}
            </div>
          </div>
        )}

        {/* 위치 + 액션 */}
        <div className="bg-surface-raised rounded-lg p-3 space-y-2">
          <div className="flex items-center gap-2 text-xs">
            <MapPin className="w-3.5 h-3.5 text-yellow-400" />
            <span className="text-label font-medium">위치</span>
          </div>
          <div className="grid grid-cols-2 gap-y-1 text-xs">
            <span className="text-hint">위도</span>
            <span className="text-label text-right font-mono">{gear.lat.toFixed(6)}°N</span>
            <span className="text-hint">경도</span>
            <span className="text-label text-right font-mono">{gear.lng.toFixed(6)}°E</span>
          </div>
        </div>

        <div className="flex gap-2">
          <Button variant="outline" size="sm" className="flex-1"
            onClick={() => navigate(`/vessel/${gear.owner}`)}>
            <Ship className="w-3.5 h-3.5 mr-1" /> 선박 상세
          </Button>
          <Button variant="primary" size="sm" className="flex-1"
            onClick={() => { /* TODO: 단속 대상 등록 API */ }}>
            <ShieldAlert className="w-3.5 h-3.5 mr-1" /> 단속 등록
          </Button>
        </div>
      </div>
    </div>
  );
}
