/**
 * 선박 궤적 미니맵 — 단일 MMSI 24h 항적 정적 표시.
 * fetchVesselTracks (signal-batch 프록시) 호출 → PathLayer 로 그림.
 */
import { useEffect, useMemo, useRef, useState, useCallback } from 'react';
import { Loader2, Ship, Clock, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Card, CardContent } from '@shared/components/ui/card';
import { Badge } from '@shared/components/ui/badge';
import { PathLayer, ScatterplotLayer } from 'deck.gl';
import type { Layer } from 'deck.gl';
import { BaseMap, type MapHandle } from '@lib/map';
import { useMapLayers } from '@lib/map/hooks/useMapLayers';
import { fetchVesselTracks, type VesselTrack } from '@/services/vesselAnalysisApi';
import type { AnomalySegment } from './vesselAnomaly';

interface Props {
  mmsi: string;
  vesselName?: string;
  hoursBack?: number;
  segments?: AnomalySegment[];
  onClose?: () => void;
}

function fmt(ts: string | number): string {
  const n = typeof ts === 'string' ? parseInt(ts, 10) : ts;
  if (!Number.isFinite(n)) return '-';
  const d = new Date(n * 1000);
  const mm = String(d.getMonth() + 1).padStart(2, '0');
  const dd = String(d.getDate()).padStart(2, '0');
  const hh = String(d.getHours()).padStart(2, '0');
  const mi = String(d.getMinutes()).padStart(2, '0');
  return `${mm}/${dd} ${hh}:${mi}`;
}

export function VesselMiniMap({ mmsi, vesselName, hoursBack = 24, segments = [], onClose }: Props) {
  const { t: tc } = useTranslation('common');
  const mapRef = useRef<MapHandle | null>(null);
  const [track, setTrack] = useState<VesselTrack | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const load = useCallback(async () => {
    setLoading(true); setError(''); setTrack(null);
    try {
      const end = new Date();
      const start = new Date(end.getTime() - hoursBack * 3600 * 1000);
      const res = await fetchVesselTracks(
        [mmsi],
        start.toISOString(),
        end.toISOString(),
      );
      setTrack(res[0] ?? null);
    } catch (e: unknown) {
      setError(e instanceof Error ? e.message : '궤적 조회 실패');
    } finally {
      setLoading(false);
    }
  }, [mmsi, hoursBack]);

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

  // 궤적 로드 후 bounds 로 지도 이동
  useEffect(() => {
    if (!track || track.geometry.length === 0) return;
    const map = mapRef.current?.map;
    if (!map) return;
    const lons = track.geometry.map((p) => p[0]);
    const lats = track.geometry.map((p) => p[1]);
    const w = Math.min(...lons), e = Math.max(...lons);
    const s = Math.min(...lats), n = Math.max(...lats);
    const span = Math.max(e - w, n - s);
    if (span < 0.001) {
      map.setCenter([(w + e) / 2, (s + n) / 2]);
      map.setZoom(11);
    } else {
      map.fitBounds([[w, s], [e, n]], { padding: 24, maxZoom: 11, duration: 0 });
    }
  }, [track]);

  // segment 의 [startTime, endTime] 범위에 들어오는 AIS 궤적 포인트를 뽑아 severity 색 path로 덧그린다.
  // 이게 사용자가 '어떤 시간대 궤적이 특이운항으로 판별됐는지' 를 바로 알게 해주는 핵심 표시.
  const segmentPaths = useMemo((): Array<{ id: string; path: [number, number][]; severity: AnomalySegment['severity'] }> => {
    if (!track || track.timestamps.length === 0 || segments.length === 0) return [];
    if (track.timestamps.length !== track.geometry.length) return [];
    const epochs = track.timestamps.map((t) => Number(t) * 1000);
    return segments
      .map((seg) => {
        const start = new Date(seg.startTime).getTime();
        const end = new Date(seg.endTime).getTime();
        const path: [number, number][] = [];
        for (let i = 0; i < epochs.length; i++) {
          if (epochs[i] >= start && epochs[i] <= end) path.push(track.geometry[i]);
        }
        return { id: seg.id, path, severity: seg.severity };
      })
      .filter((s) => s.path.length >= 2);
  }, [track, segments]);

  useMapLayers(mapRef, (): Layer[] => {
    const layers: Layer[] = [];
    if (track && track.geometry.length >= 2) {
      layers.push(
        new PathLayer({
          id: `mini-track-${mmsi}`,
          data: [{ path: track.geometry }],
          getPath: (d: { path: [number, number][] }) => d.path,
          getColor: [59, 130, 246, 140],
          getWidth: 2,
          widthUnits: 'pixels',
          jointRounded: true,
          capRounded: true,
        }),
      );
    }
    if (segmentPaths.length > 0) {
      layers.push(
        new PathLayer<{ id: string; path: [number, number][]; severity: AnomalySegment['severity'] }>({
          id: `mini-segment-paths-${mmsi}`,
          data: segmentPaths,
          getPath: (d) => d.path,
          getColor: (d) =>
            d.severity === 'critical' ? [239, 68, 68, 240]
            : d.severity === 'warning' ? [249, 115, 22, 230]
            : [59, 130, 246, 210],
          getWidth: 4,
          widthUnits: 'pixels',
          widthMinPixels: 3,
          jointRounded: true,
          capRounded: true,
        }),
      );
    }
    if (track && track.geometry.length >= 2) {
      layers.push(
        new PathLayer({
          id: `mini-track-head-${mmsi}`,
          data: [{ path: track.geometry.slice(-2) }],
          getPath: (d: { path: [number, number][] }) => d.path,
          getColor: [239, 68, 68, 255],
          getWidth: 4,
          widthUnits: 'pixels',
        }),
      );
    }
    // 이벤트 기준 좌표 (gap 시작점 등) — 분석 시각이 아니라 판별 근거가 된 과거 시점.
    // 반복 분석이 같은 좌표를 참조하는 경우가 많아 작게/반투명하게 표시한다.
    const geoSegments = segments.filter(
      (s): s is AnomalySegment & { representativeLat: number; representativeLon: number } =>
        s.representativeLat != null && s.representativeLon != null,
    );
    if (geoSegments.length > 0) {
      layers.push(
        new ScatterplotLayer<AnomalySegment & { representativeLat: number; representativeLon: number }>({
          id: `mini-segments-${mmsi}`,
          data: geoSegments,
          getPosition: (d) => [d.representativeLon, d.representativeLat],
          getRadius: 4,
          radiusUnits: 'pixels',
          radiusMinPixels: 4,
          radiusMaxPixels: 6,
          getFillColor: (d) =>
            d.severity === 'critical' ? [239, 68, 68, 180]
            : d.severity === 'warning' ? [249, 115, 22, 170]
            : [59, 130, 246, 160],
          getLineColor: [255, 255, 255, 220],
          lineWidthMinPixels: 1,
          stroked: true,
          pickable: true,
        }),
      );
    }
    return layers;
  }, [track, mmsi, segments, segmentPaths]);

  const tsList = track?.timestamps ?? [];
  const startTs = tsList[0];
  const endTs = tsList[tsList.length - 1];

  return (
    <Card className="bg-surface-raised border-slate-700/30">
      <CardContent className="p-3 space-y-2">
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-2 min-w-0">
            <Ship className="w-3.5 h-3.5 text-blue-400 shrink-0" />
            <div className="min-w-0">
              <div className="text-[11px] font-bold text-heading truncate flex items-center gap-1.5">
                {vesselName ?? mmsi}
                <span className="text-[9px] text-hint font-mono">{mmsi}</span>
                {segments.length > 0 && (
                  <Badge intent="critical" size="xs" className="font-normal">
                    특이 구간 {segments.length}
                  </Badge>
                )}
              </div>
              <div className="flex items-center gap-1 text-[9px] text-hint">
                <Clock className="w-2.5 h-2.5" />
                <span>{startTs ? fmt(startTs) : '-'}</span>
                <span>→</span>
                <span>{endTs ? fmt(endTs) : '-'}</span>
                <span className="ml-1 text-muted-foreground">· {track?.pointCount ?? 0} pts</span>
              </div>
            </div>
          </div>
          {onClose && (
            <button type="button" onClick={onClose} aria-label={tc('aria.miniMapClose')}
              className="p-1 rounded text-hint hover:text-heading hover:bg-surface-overlay shrink-0">
              <X className="w-3.5 h-3.5" />
            </button>
          )}
        </div>

        <div className="relative h-80 rounded overflow-hidden border border-slate-700/30">
          <BaseMap ref={mapRef} height={320} interactive={true} zoom={7} />
          {loading && (
            <div className="absolute inset-0 bg-background/60 flex items-center justify-center">
              <Loader2 className="w-5 h-5 animate-spin text-muted-foreground" />
            </div>
          )}
          {!loading && error && (
            <div className="absolute inset-0 bg-background/80 flex items-center justify-center text-xs text-red-400 px-3 text-center">
              {error}
            </div>
          )}
          {!loading && !error && track && track.geometry.length < 2 && (
            <div className="absolute inset-0 bg-background/60 flex items-center justify-center text-[11px] text-hint">
              24시간 내 궤적 없음 (AIS 미수신)
            </div>
          )}
        </div>

        {segments.length > 0 && (
          <div className="flex items-start gap-x-3 gap-y-0.5 flex-wrap text-[9px] text-hint">
            <span className="flex items-center gap-1">
              <span className="inline-block w-4 h-[2px] bg-red-500" />
              CRITICAL
            </span>
            <span className="flex items-center gap-1">
              <span className="inline-block w-4 h-[2px] bg-orange-500" />
              WARNING
            </span>
            <span className="flex items-center gap-1">
              <span className="inline-block w-4 h-[2px] bg-blue-500" />
              INFO
            </span>
            <span className="text-muted-foreground">굵은 색 구간 = 판별 시간대 AIS 궤적</span>
            <span className="flex items-center gap-1">
              <span className="w-1.5 h-1.5 rounded-full bg-white/80 inline-block border border-slate-600" />
              이벤트 기준점
            </span>
            {segmentPaths.length < segments.length && (
              <span className="text-muted-foreground">
                · 궤적 매칭 실패 {segments.length - segmentPaths.length}구간
              </span>
            )}
          </div>
        )}
      </CardContent>
    </Card>
  );
}
