import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { ReplayMap } from './components/ReplayMap';
import type { FlyToTarget } from './components/ReplayMap';
import { GlobeMap } from './components/GlobeMap';
import { SatelliteMap } from './components/SatelliteMap';
import { KoreaMap } from './components/KoreaMap';
import { TimelineSlider } from './components/TimelineSlider';
import { ReplayControls } from './components/ReplayControls';
import { LiveControls } from './components/LiveControls';
import { SensorChart } from './components/SensorChart';
import { EventLog } from './components/EventLog';
import { LayerPanel } from './components/LayerPanel';
import { useReplay } from './hooks/useReplay';
import { useMonitor } from './hooks/useMonitor';
import { fetchEvents, fetchSensorData } from './services/api';
import { fetchAircraftOpenSky } from './services/opensky';
import { fetchMilitaryAircraft, fetchAllAircraftLive, fetchMilitaryAircraftKorea, fetchAllAircraftLiveKorea } from './services/airplaneslive';
import { fetchSatelliteTLE, fetchSatelliteTLEKorea, propagateAll } from './services/celestrak';
import { fetchAircraftOpenSkyKorea } from './services/opensky';
import { fetchShips, fetchShipsKorea } from './services/ships';
import { fetchOsintFeed } from './services/osint';
import { KOREA_SUBMARINE_CABLES } from './services/submarineCable';
import type { OsintItem } from './services/osint';
import { propagateAircraft, propagateShips } from './services/propagation';
import type { GeoEvent, SensorLog, Aircraft, Ship, Satellite, SatellitePosition, LayerVisibility, AppMode } from './types';
import { useTheme } from './hooks/useTheme';
import { useAuth } from './hooks/useAuth';
import { useTranslation } from 'react-i18next';
import LoginPage from './components/auth/LoginPage';
import './App.css';

// MarineTraffic-style ship classification
// Maps S&P STAT5CODE prefixes and our custom typecodes to MT categories
function getMarineTrafficCategory(typecode?: string, category?: string): string {
  if (!typecode) {
    // Fallback to our internal category
    if (category === 'tanker') return 'tanker';
    if (category === 'cargo') return 'cargo';
    if (category === 'destroyer' || category === 'warship' || category === 'carrier' || category === 'patrol') return 'military';
    return 'unspecified';
  }
  const code = typecode.toUpperCase();

  // Our custom typecodes
  if (code === 'VLCC' || code === 'LNG' || code === 'LPG') return 'tanker';
  if (code === 'CONT' || code === 'BULK') return 'cargo';
  if (code === 'DDH' || code === 'DDG' || code === 'CVN' || code === 'FFG' || code === 'LCS' || code === 'MCM' || code === 'PC') return 'military';

  // S&P STAT5CODE (IHS StatCode5) — first 2 chars determine main category
  // A1x = Tankers (crude, products, chemical, LPG, LNG)
  if (code.startsWith('A1')) return 'tanker';
  // A2x = Bulk carriers
  if (code.startsWith('A2')) return 'cargo';
  // A3x = General cargo / Container / Reefer / Ro-Ro
  if (code.startsWith('A3')) return 'cargo';
  // B1x / B2x = Passenger / Cruise / Ferry
  if (code.startsWith('B')) return 'passenger';
  // C1x = Fishing
  if (code.startsWith('C')) return 'fishing';
  // D1x = Offshore (tugs, supply, etc.)
  if (code.startsWith('D')) return 'tug_special';
  // E = Other activities (research, cable layers, dredgers)
  if (code.startsWith('E')) return 'tug_special';
  // X = Non-propelled (barges)
  if (code.startsWith('X')) return 'unspecified';

  // S&P VesselType strings
  const lower = code.toLowerCase();
  if (lower.includes('tanker')) return 'tanker';
  if (lower.includes('cargo') || lower.includes('container') || lower.includes('bulk')) return 'cargo';
  if (lower.includes('passenger') || lower.includes('cruise') || lower.includes('ferry')) return 'passenger';
  if (lower.includes('fishing')) return 'fishing';
  if (lower.includes('tug') || lower.includes('supply') || lower.includes('offshore')) return 'tug_special';
  if (lower.includes('high speed')) return 'high_speed';
  if (lower.includes('pleasure') || lower.includes('yacht') || lower.includes('sailing')) return 'pleasure';

  return 'unspecified';
}

function App() {
  const { user, isLoading: authLoading, isAuthenticated, login, devLogin, logout } = useAuth();

  if (authLoading) {
    return (
      <div
        className="flex min-h-screen items-center justify-center"
        style={{ backgroundColor: 'var(--kcg-bg)', color: 'var(--kcg-muted)' }}
      >
        Loading...
      </div>
    );
  }

  if (!isAuthenticated) {
    return <LoginPage onGoogleLogin={login} onDevLogin={devLogin} />;
  }

  return <AuthenticatedApp user={user} onLogout={logout} />;
}

interface AuthenticatedAppProps {
  user: { email: string; name: string; picture?: string } | null;
  onLogout: () => Promise<void>;
}

function AuthenticatedApp(_props: AuthenticatedAppProps) {
  const [appMode, setAppMode] = useState<AppMode>('live');
  const [events, setEvents] = useState<GeoEvent[]>([]);
  const [sensorData, setSensorData] = useState<SensorLog[]>([]);
  const [baseAircraft, setBaseAircraft] = useState<Aircraft[]>([]);
  const [baseShips, setBaseShips] = useState<Ship[]>([]);
  const [baseShipsKorea, setBaseShipsKorea] = useState<Ship[]>([]);
  const [baseAircraftKorea, setBaseAircraftKorea] = useState<Aircraft[]>([]);
  const [satellitesKorea, setSatellitesKorea] = useState<Satellite[]>([]);
  const [satPositionsKorea, setSatPositionsKorea] = useState<SatellitePosition[]>([]);
  const [osintFeed, setOsintFeed] = useState<OsintItem[]>([]);
  const [satellites, setSatellites] = useState<Satellite[]>([]);
  const [satPositions, setSatPositions] = useState<SatellitePosition[]>([]);
  const [mapMode, setMapMode] = useState<'flat' | 'globe' | 'satellite'>('satellite');
  const [dashboardTab, setDashboardTab] = useState<'iran' | 'korea'>('iran');
  const [layers, setLayers] = useState<LayerVisibility>({
    events: true,
    aircraft: true,
    satellites: true,
    ships: true,
    koreanShips: false,
    airports: true,
    sensorCharts: true,
    oilFacilities: true,
    militaryOnly: false,
  });

  // Korea tab layer visibility (lifted from KoreaMap)
  const [koreaLayers, setKoreaLayers] = useState<Record<string, boolean>>({
    ships: true,
    aircraft: true,
    satellites: true,
    infra: true,
    cables: true,
    cctv: true,
    airports: true,
    coastGuard: true,
    navWarning: true,
    osint: true,
    eez: true,
    piracy: true,
    militaryOnly: false,
  });

  const toggleKoreaLayer = useCallback((key: string) => {
    setKoreaLayers(prev => ({ ...prev, [key]: !prev[key] }));
  }, []);

  // Category filter state (shared across tabs)
  const [hiddenAcCategories, setHiddenAcCategories] = useState<Set<string>>(new Set());
  const [hiddenShipCategories, setHiddenShipCategories] = useState<Set<string>>(new Set());

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

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

  const [flyToTarget, setFlyToTarget] = useState<FlyToTarget | null>(null);

  // 1시간마다 전체 데이터 강제 리프레시 (호르무즈 해협 실시간 정보 업데이트)
  const [refreshKey, setRefreshKey] = useState(0);
  useEffect(() => {
    const HOUR_MS = 3600_000;
    const interval = setInterval(() => {
      console.log('[REFRESH] 1시간 주기 전체 데이터 갱신');
      setRefreshKey(k => k + 1);
    }, HOUR_MS);
    return () => clearInterval(interval);
  }, []);

  // Korea monitoring filters (independent toggles)
  const [koreaFilters, setKoreaFilters] = useState({
    illegalFishing: false,
    illegalTransship: false,
    darkVessel: false,
    cableWatch: false,
    dokdoWatch: false,
    ferryWatch: false,
  });

  const replay = useReplay();
  const monitor = useMonitor();
  const { theme, toggleTheme } = useTheme();
  const { t, i18n } = useTranslation();
  const toggleLang = useCallback(() => {
    i18n.changeLanguage(i18n.language === 'ko' ? 'en' : 'ko');
  }, [i18n]);

  const isLive = appMode === 'live';

  // Unified time values based on current mode
  const currentTime = appMode === 'live' ? monitor.state.currentTime : replay.state.currentTime;
  const startTime = appMode === 'live' ? monitor.startTime : replay.state.startTime;
  const endTime = appMode === 'live' ? monitor.endTime : replay.state.endTime;

  // Load initial data — each source independently to avoid one failure blocking all
  useEffect(() => {
    fetchEvents().then(setEvents).catch(() => {});
    fetchSensorData().then(setSensorData).catch(() => {});
    fetchSatelliteTLE().then(setSatellites).catch(() => {});
  }, [refreshKey]);

  // Fetch base aircraft data
  // LIVE: OpenSky (민간기) + Airplanes.live (모든 항공기 + 군용기) → 실시간 병합
  // REPLAY: OpenSky (샘플 폴백) + military → 리플레이
  useEffect(() => {
    const load = async () => {
      if (appMode === 'live') {
        // 라이브: 3개 소스 동시 가져오기 — OpenSky + Airplanes.live + Military
        const [opensky, allLive, mil] = await Promise.all([
          fetchAircraftOpenSky().catch(() => [] as Aircraft[]),
          fetchAllAircraftLive().catch(() => [] as Aircraft[]),
          fetchMilitaryAircraft().catch(() => [] as Aircraft[]),
        ]);

        // 1) Airplanes.live 기본 + mil 카테고리 보강
        const milMap = new Map(mil.map(a => [a.icao24, a]));
        const merged = new Map<string, Aircraft>();

        for (const ac of allLive) {
          const milAc = milMap.get(ac.icao24);
          if (milAc) {
            merged.set(ac.icao24, { ...ac, category: milAc.category, typecode: milAc.typecode || ac.typecode });
          } else {
            merged.set(ac.icao24, ac);
          }
        }

        // 2) mil에만 있는 항공기 추가
        for (const m of mil) {
          if (!merged.has(m.icao24)) merged.set(m.icao24, m);
        }

        // 3) OpenSky 데이터 추가 (Airplanes.live에 없는 항공기만)
        for (const ac of opensky) {
          if (!merged.has(ac.icao24)) merged.set(ac.icao24, ac);
        }

        const result = Array.from(merged.values());
        if (result.length > 0) setBaseAircraft(result);
      } else {
        // 리플레이: 기존 로직 (OpenSky 샘플 + military)
        const [opensky, mil] = await Promise.all([
          fetchAircraftOpenSky(),
          fetchMilitaryAircraft(),
        ]);
        const milIcaos = new Set(mil.map(a => a.icao24));
        const merged = [...mil, ...opensky.filter(a => !milIcaos.has(a.icao24))];
        setBaseAircraft(merged);
      }
    };
    load();
    const interval = setInterval(load, 15_000);
    return () => clearInterval(interval);
  }, [appMode, refreshKey]);

  // Fetch base ship data — never overwrite with empty to prevent flicker
  useEffect(() => {
    const load = async () => {
      try {
        const data = await fetchShips();
        if (data.length > 0) {
          setBaseShips(data);
        }
      } catch {
        // keep previous data
      }
    };
    load();
    const interval = setInterval(load, 15_000);
    return () => clearInterval(interval);
  }, [appMode, refreshKey]);

  // Fetch Korea region ship data (signal-batch, 4-min cycle)
  useEffect(() => {
    const load = async () => {
      try {
        const data = await fetchShipsKorea();
        if (data.length > 0) setBaseShipsKorea(data);
      } catch { /* keep previous */ }
    };
    load();
    const interval = setInterval(load, 240_000);
    return () => clearInterval(interval);
  }, [appMode, refreshKey]);

  // Fetch Korea satellite TLE data
  useEffect(() => {
    fetchSatelliteTLEKorea().then(setSatellitesKorea).catch(() => {});
  }, [refreshKey]);

  // Fetch Korea aircraft data
  useEffect(() => {
    const load = async () => {
      const [opensky, allLive, mil] = await Promise.all([
        fetchAircraftOpenSkyKorea().catch(() => [] as Aircraft[]),
        fetchAllAircraftLiveKorea().catch(() => [] as Aircraft[]),
        fetchMilitaryAircraftKorea().catch(() => [] as Aircraft[]),
      ]);
      const milMap = new Map(mil.map(a => [a.icao24, a]));
      const merged = new Map<string, Aircraft>();
      for (const ac of allLive) {
        const milAc = milMap.get(ac.icao24);
        merged.set(ac.icao24, milAc ? { ...ac, category: milAc.category, typecode: milAc.typecode || ac.typecode } : ac);
      }
      for (const m of mil) { if (!merged.has(m.icao24)) merged.set(m.icao24, m); }
      for (const ac of opensky) { if (!merged.has(ac.icao24)) merged.set(ac.icao24, ac); }
      const result = Array.from(merged.values());
      if (result.length > 0) setBaseAircraftKorea(result);
    };
    load();
    const interval = setInterval(load, 25_000);
    return () => clearInterval(interval);
  }, [refreshKey]);

  // Fetch OSINT feed — live mode (both tabs) + replay mode (iran tab)
  useEffect(() => {
    const shouldFetch = isLive || dashboardTab === 'iran';
    if (!shouldFetch) { setOsintFeed([]); return; }
    const load = async () => {
      try {
        const data = await fetchOsintFeed(dashboardTab);
        if (data.length > 0) setOsintFeed(data);
      } catch { /* keep previous */ }
    };
    setOsintFeed([]); // clear while loading new focus
    load();
    const interval = setInterval(load, 120_000);
    return () => clearInterval(interval);
  }, [isLive, dashboardTab, refreshKey]);

  // OSINT → GeoEvent 변환: 피격/군사 정보를 타임라인 이벤트로 반영
  const osintEvents = useMemo((): GeoEvent[] => {
    if (dashboardTab !== 'iran' || osintFeed.length === 0) return [];

    // OSINT 카테고리 → GeoEvent 타입 매핑 (피격 정보 기본)
    const categoryToType: Record<string, GeoEvent['type']> = {
      military: 'osint',
      shipping: 'osint',
      oil: 'osint',
      nuclear: 'osint',
      diplomacy: 'osint',
    };

    // 피격/공습 키워드 → 구체적 이벤트 타입 분류
    const STRIKE_PATTERN = /strike|attack|bomb|airstrike|hit|destroy|blast|공습|타격|폭격|파괴|피격/i;
    const MISSILE_PATTERN = /missile|launch|drone|발사|미사일|드론/i;
    const EXPLOSION_PATTERN = /explo|blast|deton|fire|폭발|화재|폭파/i;
    const INTERCEPT_PATTERN = /intercept|shoot.*down|defense|요격|격추|방어/i;
    const IMPACT_PATTERN = /impact|hit|struck|damage|casualt|피격|타격|피해|사상/i;

    return osintFeed
      .filter(item => {
        // lat/lng가 있는 항목만 타임라인에 반영
        if (!item.lat || !item.lng) return false;
        // 관련 카테고리만
        return item.category in categoryToType;
      })
      .map((item): GeoEvent => {
        // 피격 키워드 기반으로 구체적 이벤트 타입 분류
        let eventType: GeoEvent['type'] = 'osint';
        const title = item.title;
        if (IMPACT_PATTERN.test(title)) eventType = 'impact';
        else if (STRIKE_PATTERN.test(title)) eventType = 'airstrike';
        else if (MISSILE_PATTERN.test(title)) eventType = 'missile_launch';
        else if (EXPLOSION_PATTERN.test(title)) eventType = 'explosion';
        else if (INTERCEPT_PATTERN.test(title)) eventType = 'intercept';

        // 소스 추정
        let source: GeoEvent['source'] | undefined;
        if (/US|미국|America|Pentagon|CENTCOM/i.test(title)) source = 'US';
        else if (/Israel|이스라엘|IAF|IDF/i.test(title)) source = 'IL';
        else if (/Iran|이란|IRGC/i.test(title)) source = 'IR';
        else if (/Houthi|후티|Hezbollah|헤즈볼라|PMF|proxy|대리/i.test(title)) source = 'proxy';

        return {
          id: `osint-${item.id}`,
          timestamp: item.timestamp,
          lat: item.lat!,
          lng: item.lng!,
          type: eventType,
          source,
          label: `[OSINT] ${item.title}`,
          description: `출처: ${item.source} | ${item.url}`,
          intensity: eventType === 'impact' ? 80 : eventType === 'airstrike' ? 70 : 50,
        };
      });
  }, [osintFeed, dashboardTab]);

  // 기본 이벤트 + OSINT 이벤트 병합 (시간순 정렬)
  const mergedEvents = useMemo(() => {
    if (osintEvents.length === 0) return events;
    return [...events, ...osintEvents].sort((a, b) => a.timestamp - b.timestamp);
  }, [events, osintEvents]);

  // Propagate satellite positions — throttle to every 2s of real time
  const satTimeRef = useRef(0);
  useEffect(() => {
    if (satellites.length === 0) return;
    const now = Date.now();
    if (now - satTimeRef.current < 2000) return;
    satTimeRef.current = now;
    const positions = propagateAll(satellites, new Date(currentTime));
    setSatPositions(positions);
  }, [satellites, currentTime]);

  // Propagate Korea satellite positions
  const satTimeKoreaRef = useRef(0);
  useEffect(() => {
    if (satellitesKorea.length === 0) return;
    const now = Date.now();
    if (now - satTimeKoreaRef.current < 2000) return;
    satTimeKoreaRef.current = now;
    const positions = propagateAll(satellitesKorea, new Date(currentTime));
    setSatPositionsKorea(positions);
  }, [satellitesKorea, currentTime]);

  // Propagate Korea aircraft (live only — no waypoint propagation needed)
  const aircraftKorea = useMemo(() => baseAircraftKorea, [baseAircraftKorea]);

  // Propagate aircraft positions based on current time
  const aircraft = useMemo(
    () => propagateAircraft(baseAircraft, currentTime),
    [baseAircraft, currentTime],
  );

  // Propagate ship positions based on current time
  const ships = useMemo(
    () => propagateShips(baseShips, currentTime, isLive),
    [baseShips, currentTime, isLive],
  );

  // Korea region ships (separate data)
  const koreaShips = useMemo(
    () => propagateShips(baseShipsKorea, currentTime, isLive),
    [baseShipsKorea, currentTime, isLive],
  );

  // Category-filtered data for map rendering
  const visibleAircraft = useMemo(
    () => aircraft.filter(a => !hiddenAcCategories.has(a.category)),
    [aircraft, hiddenAcCategories],
  );
  const visibleShips = useMemo(
    () => ships.filter(s => !hiddenShipCategories.has(getMarineTrafficCategory(s.typecode, s.category))),
    [ships, hiddenShipCategories],
  );
  const visibleAircraftKorea = useMemo(
    () => aircraftKorea.filter(a => !hiddenAcCategories.has(a.category)),
    [aircraftKorea, hiddenAcCategories],
  );
  const visibleKoreaShips = useMemo(
    () => koreaShips.filter(s => !hiddenShipCategories.has(getMarineTrafficCategory(s.typecode, s.category))),
    [koreaShips, hiddenShipCategories],
  );

  const toggleLayer = useCallback((key: keyof LayerVisibility) => {
    setLayers(prev => ({ ...prev, [key]: !prev[key] }));
  }, []);

  // Handle event card click from timeline: fly to location on map
  const handleEventFlyTo = useCallback((event: GeoEvent) => {
    setFlyToTarget({ lat: event.lat, lng: event.lng, zoom: 8 });
  }, []);

  // Aircraft stats
  const aircraftByCategory = useMemo(() => {
    const counts: Record<string, number> = {};
    for (const ac of aircraft) {
      counts[ac.category] = (counts[ac.category] || 0) + 1;
    }
    return counts;
  }, [aircraft]);

  const militaryCount = useMemo(
    () => aircraft.filter(a => a.category !== 'civilian' && a.category !== 'unknown').length,
    [aircraft],
  );
  const koreaMilitaryCount = useMemo(
    () => aircraftKorea.filter(a => a.category !== 'civilian' && a.category !== 'unknown').length,
    [aircraftKorea],
  );

  // Ship stats — MT classification (matches map icon colors)
  const shipsByCategory = useMemo(() => {
    const counts: Record<string, number> = {};
    for (const s of ships) {
      const mtCat = getMarineTrafficCategory(s.typecode, s.category);
      counts[mtCat] = (counts[mtCat] || 0) + 1;
    }
    return counts;
  }, [ships]);

  // Korean ship stats — MarineTraffic-style classification
  const koreanShips = useMemo(() => ships.filter(s => s.flag === 'KR'), [ships]);
  const koreanShipsByCategory = useMemo(() => {
    const counts: Record<string, number> = {};
    for (const s of koreanShips) {
      const mtCat = getMarineTrafficCategory(s.typecode, s.category);
      counts[mtCat] = (counts[mtCat] || 0) + 1;
    }
    return counts;
  }, [koreanShips]);

  // Korea region stats (for Korea dashboard)
  const koreaKoreanShips = useMemo(() => koreaShips.filter(s => s.flag === 'KR'), [koreaShips]);
  const koreaChineseShips = useMemo(() => koreaShips.filter(s => s.flag === 'CN'), [koreaShips]);
  const koreaShipsByCategory = useMemo(() => {
    const counts: Record<string, number> = {};
    for (const s of koreaShips) {
      const mtCat = getMarineTrafficCategory(s.typecode, s.category);
      counts[mtCat] = (counts[mtCat] || 0) + 1;
    }
    return counts;
  }, [koreaShips]);

  // Korea aircraft stats
  const koreaAircraftByCategory = useMemo(() => {
    const counts: Record<string, number> = {};
    for (const ac of aircraftKorea) {
      counts[ac.category] = (counts[ac.category] || 0) + 1;
    }
    return counts;
  }, [aircraftKorea]);

  // Korea filtered ships by monitoring mode (independent toggles, additive highlight)
  const anyFilterOn = koreaFilters.illegalFishing || koreaFilters.illegalTransship || koreaFilters.darkVessel || koreaFilters.cableWatch || koreaFilters.dokdoWatch || koreaFilters.ferryWatch;

  // 불법환적 의심 선박 탐지: 100m 이내 근접 + 저속/정박 + 1시간 이상 유지 + 연안 제외
  // 근접 쌍별 최초 감지 시각 추적 (pairKey → timestamp)
  const proximityStartRef = useRef<Map<string, number>>(new Map());
  const TRANSSHIP_DURATION_MS = 60 * 60 * 1000; // 1시간

  const transshipSuspects = useMemo(() => {
    if (!koreaFilters.illegalTransship) return new Set<string>();

    const suspects = new Set<string>();
    const isOffshore = (s: Ship) => {
      const nearCoastWest = s.lng > 125.5 && s.lng < 130.0 && s.lat > 33.5 && s.lat < 38.5;
      if (nearCoastWest) {
        const distFromEastCoast = s.lng - 129.5;
        const distFromWestCoast = 126.0 - s.lng;
        const distFromSouthCoast = 34.5 - s.lat;
        if (distFromEastCoast > 0.15 || distFromWestCoast > 0.15 || distFromSouthCoast > 0.15) return true;
        return false;
      }
      return true;
    };

    const isNearForeignCoast = (s: Ship) => {
      if (s.lng < 123.5 && s.lat > 25 && s.lat < 40) return true;
      if (s.lng > 130.5 && s.lat > 30 && s.lat < 46) return true;
      if (s.lng > 129.1 && s.lng < 129.6 && s.lat > 34.0 && s.lat < 34.8) return true;
      if (s.lng > 129.5 && s.lat > 31 && s.lat < 34) return true;
      return false;
    };

    const candidates = koreaShips.filter(s => {
      if (s.speed >= 2) return false;
      const mtCat = getMarineTrafficCategory(s.typecode, s.category);
      if (mtCat !== 'tanker' && mtCat !== 'cargo' && mtCat !== 'fishing') return false;
      if (isNearForeignCoast(s)) return false;
      return isOffshore(s);
    });

    const now = currentTime;
    const prevMap = proximityStartRef.current;
    const currentPairs = new Set<string>();
    const PROXIMITY_DEG = 0.001; // ~110m

    for (let i = 0; i < candidates.length; i++) {
      for (let j = i + 1; j < candidates.length; j++) {
        const a = candidates[i];
        const b = candidates[j];
        const dlat = Math.abs(a.lat - b.lat);
        const dlng = Math.abs(a.lng - b.lng) * Math.cos((a.lat * Math.PI) / 180);
        if (dlat < PROXIMITY_DEG && dlng < PROXIMITY_DEG) {
          const pairKey = [a.mmsi, b.mmsi].sort().join(':');
          currentPairs.add(pairKey);
          // 최초 감지 시 시작 시각 기록
          if (!prevMap.has(pairKey)) {
            prevMap.set(pairKey, now);
          }
          // 1시간 이상 근접 유지 시 환적 의심
          const startTime = prevMap.get(pairKey)!;
          if (now - startTime >= TRANSSHIP_DURATION_MS) {
            suspects.add(a.mmsi);
            suspects.add(b.mmsi);
          }
        }
      }
    }

    // 더 이상 근접하지 않는 쌍은 추적 해제
    for (const key of prevMap.keys()) {
      if (!currentPairs.has(key)) prevMap.delete(key);
    }

    return suspects;
  }, [koreaShips, koreaFilters.illegalTransship, currentTime]);

  // 다크베셀 탐지: AIS 신호 이력 추적
  // mmsi → { lastSeenTs[], gapTotal, toggleCount }
  const aisHistoryRef = useRef<Map<string, { seen: number[]; lastGapStart: number | null }>>(new Map());
  const ONE_HOUR_MS = 60 * 60 * 1000;

  const darkVesselSet = useMemo(() => {
    if (!koreaFilters.darkVessel) return new Set<string>();

    const now = currentTime;
    const history = aisHistoryRef.current;
    const result = new Set<string>();

    // 현재 보이는 선박 mmsi 집합
    const currentMmsis = new Set(koreaShips.map(s => s.mmsi));

    // 현재 보이는 선박: 신호 기록 갱신
    for (const s of koreaShips) {
      let h = history.get(s.mmsi);
      if (!h) {
        h = { seen: [], lastGapStart: null };
        history.set(s.mmsi, h);
      }
      // AIS 신호가 꺼졌다 다시 켜진 경우 (gap이 있었으면 toggle +1)
      if (h.lastGapStart !== null) {
        const gapDuration = now - h.lastGapStart;
        // 1시간 이상 신호 끊김 후 재등장 = 다크베셀
        if (gapDuration >= ONE_HOUR_MS) {
          result.add(s.mmsi);
        }
        h.lastGapStart = null;
      }
      // seen 타임스탬프 기록 (최근 20개만 유지)
      h.seen.push(now);
      if (h.seen.length > 20) h.seen = h.seen.slice(-20);

      // 신호 껐다켰다 패턴: lastSeen이 현재보다 많이 이전 (불규칙 AIS)
      // lastSeen 대비 현재 시간 차이가 크면 신호 불안정
      const aisAge = now - s.lastSeen;
      if (aisAge > ONE_HOUR_MS) {
        result.add(s.mmsi);
      }

      // 신호 온오프 패턴: seen 기록에서 간격 분석
      if (h.seen.length >= 4) {
        let gapCount = 0;
        for (let k = 1; k < h.seen.length; k++) {
          const gap = h.seen[k] - h.seen[k - 1];
          // 정상 갱신 주기(~30초)보다 5배 이상 차이 = 신호 끊김
          if (gap > 150_000) gapCount++;
        }
        // 3회 이상 신호 끊김 패턴 = 껐다켰다
        if (gapCount >= 3) {
          result.add(s.mmsi);
        }
      }
    }

    // 이전에 보였지만 지금 안 보이는 선박: gap 시작 기록
    for (const [mmsi, h] of history.entries()) {
      if (!currentMmsis.has(mmsi) && h.lastGapStart === null) {
        h.lastGapStart = now;
      }
    }

    // 오래된 이력 정리 (6시간 이상 미관측)
    const SIX_HOURS = 6 * ONE_HOUR_MS;
    for (const [mmsi, h] of history.entries()) {
      if (h.seen.length > 0 && now - h.seen[h.seen.length - 1] > SIX_HOURS && !currentMmsis.has(mmsi)) {
        history.delete(mmsi);
      }
    }

    return result;
  }, [koreaShips, koreaFilters.darkVessel, currentTime]);

  // 해저케이블 감시: 케이블 라인 ~1km 이내 + 0.6노트 이하 + 3시간 이상 체류
  const cableNearStartRef = useRef<Map<string, number>>(new Map());
  const CABLE_DURATION_MS = 3 * 60 * 60 * 1000; // 3시간

  const cableWatchSet = useMemo(() => {
    if (!koreaFilters.cableWatch) return new Set<string>();
    const result = new Set<string>();
    const CABLE_PROX_DEG = 0.01; // ~1.1km

    // 케이블 세그먼트 수집
    const segments: [number, number, number, number][] = [];
    for (const cable of KOREA_SUBMARINE_CABLES) {
      for (let k = 0; k < cable.route.length - 1; k++) {
        segments.push([cable.route[k][0], cable.route[k][1], cable.route[k + 1][0], cable.route[k + 1][1]]);
      }
    }

    // 점-선분 최소 거리 (도 단위 근사)
    const distToSegment = (px: number, py: number, x1: number, y1: number, x2: number, y2: number) => {
      const dx = x2 - x1;
      const dy = y2 - y1;
      if (dx === 0 && dy === 0) return Math.hypot(px - x1, py - y1);
      const t = Math.max(0, Math.min(1, ((px - x1) * dx + (py - y1) * dy) / (dx * dx + dy * dy)));
      const cx = x1 + t * dx;
      const cy = y1 + t * dy;
      const dlng = (px - cx) * Math.cos((py * Math.PI) / 180);
      return Math.hypot(dlng, py - cy);
    };

    const now = currentTime;
    const prevMap = cableNearStartRef.current;
    const currentNear = new Set<string>();

    for (const s of koreaShips) {
      if (s.speed > 0.6) continue; // 0.6노트 이하만
      let nearCable = false;
      for (const [x1, y1, x2, y2] of segments) {
        if (distToSegment(s.lng, s.lat, x1, y1, x2, y2) < CABLE_PROX_DEG) {
          nearCable = true;
          break;
        }
      }
      if (!nearCable) continue;

      currentNear.add(s.mmsi);
      // 최초 감지 시 시작 시각 기록
      if (!prevMap.has(s.mmsi)) {
        prevMap.set(s.mmsi, now);
      }
      // 3시간 이상 케이블 위 체류 시 의심 선박
      const startTime = prevMap.get(s.mmsi)!;
      if (now - startTime >= CABLE_DURATION_MS) {
        result.add(s.mmsi);
      }
    }

    // 케이블 근처 벗어난 선박은 추적 해제
    for (const mmsi of prevMap.keys()) {
      if (!currentNear.has(mmsi)) prevMap.delete(mmsi);
    }

    return result;
  }, [koreaShips, koreaFilters.cableWatch, currentTime]);

  // 독도감시: 독도 영해(12해리≈22km) 접근 일본 선박 탐지 + 알림
  const DOKDO = { lat: 37.2417, lng: 131.8647 };
  const TERRITORIAL_DEG = 0.2; // ~22km (12해리)
  const ALERT_DEG = 0.4; // ~44km (접근 경고 범위)
  const dokdoAlertedRef = useRef<Set<string>>(new Set());
  const [dokdoAlerts, setDokdoAlerts] = useState<{ mmsi: string; name: string; dist: number; time: number }[]>([]);

  const dokdoWatchSet = useMemo(() => {
    if (!koreaFilters.dokdoWatch) return new Set<string>();
    const result = new Set<string>();
    const newAlerts: { mmsi: string; name: string; dist: number; time: number }[] = [];
    const alerted = dokdoAlertedRef.current;

    for (const s of koreaShips) {
      // 일본 국적 선박만 감시
      if (s.flag !== 'JP') continue;
      const dDokdo = Math.hypot(
        (s.lng - DOKDO.lng) * Math.cos((DOKDO.lat * Math.PI) / 180),
        s.lat - DOKDO.lat,
      );
      // 영해 내 진입
      if (dDokdo < TERRITORIAL_DEG) {
        result.add(s.mmsi);
        if (!alerted.has(s.mmsi)) {
          alerted.add(s.mmsi);
          const distKm = Math.round(dDokdo * 111);
          newAlerts.push({ mmsi: s.mmsi, name: s.name || s.mmsi, dist: distKm, time: currentTime });
        }
      }
      // 접근 경고 (영해 밖이지만 가까움)
      else if (dDokdo < ALERT_DEG) {
        result.add(s.mmsi);
        if (!alerted.has(`warn-${s.mmsi}`)) {
          alerted.add(`warn-${s.mmsi}`);
          const distKm = Math.round(dDokdo * 111);
          newAlerts.push({ mmsi: s.mmsi, name: s.name || s.mmsi, dist: distKm, time: currentTime });
        }
      }
    }

    // 벗어난 선박 추적 해제
    const currentJP = new Set(koreaShips.filter(s => s.flag === 'JP').map(s => s.mmsi));
    for (const key of alerted) {
      const mmsi = key.replace('warn-', '');
      if (!currentJP.has(mmsi)) alerted.delete(key);
    }

    if (newAlerts.length > 0) {
      setDokdoAlerts(prev => [...newAlerts, ...prev].slice(0, 10));
    }

    return result;
  }, [koreaShips, koreaFilters.dokdoWatch, currentTime]);

  const koreaFilteredShips = useMemo(() => {
    if (!anyFilterOn) return visibleKoreaShips;
    return visibleKoreaShips.filter(s => {
      const mtCat = getMarineTrafficCategory(s.typecode, s.category);
      if (koreaFilters.illegalFishing && mtCat === 'fishing' && s.flag !== 'KR') return true;
      if (koreaFilters.illegalTransship && transshipSuspects.has(s.mmsi)) return true;
      if (koreaFilters.darkVessel && darkVesselSet.has(s.mmsi)) return true;
      if (koreaFilters.cableWatch && cableWatchSet.has(s.mmsi)) return true;
      if (koreaFilters.dokdoWatch && dokdoWatchSet.has(s.mmsi)) return true;
      if (koreaFilters.ferryWatch && getMarineTrafficCategory(s.typecode, s.category) === 'passenger') return true;
      return false;
    });
  }, [visibleKoreaShips, koreaFilters, anyFilterOn, transshipSuspects, darkVesselSet, cableWatchSet, dokdoWatchSet]);

  return (
    <div className={`app ${isLive ? 'app-live' : ''}`}>
      <header className="app-header">
        {/* Dashboard Tabs (replaces title) */}
        <div className="dash-tabs">
          <button
            className={`dash-tab ${dashboardTab === 'iran' ? 'active' : ''}`}
            onClick={() => setDashboardTab('iran')}
          >
            <span className="dash-tab-flag">🇮🇷</span>
            {t('tabs.iran')}
          </button>
          <button
            className={`dash-tab ${dashboardTab === 'korea' ? 'active' : ''}`}
            onClick={() => setDashboardTab('korea')}
          >
            <span className="dash-tab-flag">🇰🇷</span>
            {t('tabs.korea')}
          </button>
        </div>

        {/* Mode Toggle */}
        {dashboardTab === 'iran' && (
          <div className="mode-toggle">
            <div className="flex items-center gap-1.5 font-mono text-[11px] text-kcg-danger bg-kcg-danger-bg border border-kcg-danger/30 rounded-[6px] px-2.5 py-1 font-bold animate-pulse">
              <span className="text-[13px]">⚔️</span>
              D+{Math.floor((currentTime - new Date('2026-03-01T00:00:00Z').getTime()) / (1000 * 60 * 60 * 24))}
            </div>
            <button
              className={`mode-btn ${appMode === 'live' ? 'active live' : ''}`}
              onClick={() => setAppMode('live')}
            >
              <span className="mode-dot-icon" />
              {t('mode.live')}
            </button>
            <button
              className={`mode-btn ${appMode === 'replay' ? 'active' : ''}`}
              onClick={() => setAppMode('replay')}
            >
              <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><polygon points="5,3 19,12 5,21" /></svg>
              {t('mode.replay')}
            </button>
          </div>
        )}

        {dashboardTab === 'korea' && (
          <div className="mode-toggle">
            <button
              className={`mode-btn ${koreaFilters.illegalFishing ? 'active live' : ''}`}
              onClick={() => setKoreaFilters(prev => ({ ...prev, illegalFishing: !prev.illegalFishing }))}
              title={t('filters.illegalFishing')}
            >
              <span className="text-[11px]">🚫🐟</span>
              {t('filters.illegalFishing')}
            </button>
            <button
              className={`mode-btn ${koreaFilters.illegalTransship ? 'active live' : ''}`}
              onClick={() => setKoreaFilters(prev => ({ ...prev, illegalTransship: !prev.illegalTransship }))}
              title={t('filters.illegalTransship')}
            >
              <span className="text-[11px]">⚓</span>
              {t('filters.illegalTransship')}
            </button>
            <button
              className={`mode-btn ${koreaFilters.darkVessel ? 'active live' : ''}`}
              onClick={() => setKoreaFilters(prev => ({ ...prev, darkVessel: !prev.darkVessel }))}
              title={t('filters.darkVessel')}
            >
              <span className="text-[11px]">👻</span>
              {t('filters.darkVessel')}
            </button>
            <button
              className={`mode-btn ${koreaFilters.cableWatch ? 'active live' : ''}`}
              onClick={() => setKoreaFilters(prev => ({ ...prev, cableWatch: !prev.cableWatch }))}
              title={t('filters.cableWatch')}
            >
              <span className="text-[11px]">🔌</span>
              {t('filters.cableWatch')}
            </button>
            <button
              className={`mode-btn ${koreaFilters.dokdoWatch ? 'active live' : ''}`}
              onClick={() => setKoreaFilters(prev => ({ ...prev, dokdoWatch: !prev.dokdoWatch }))}
              title={t('filters.dokdoWatch')}
            >
              <span className="text-[11px]">🏝️</span>
              {t('filters.dokdoWatch')}
            </button>
            <button
              className={`mode-btn ${koreaFilters.ferryWatch ? 'active live' : ''}`}
              onClick={() => setKoreaFilters(prev => ({ ...prev, ferryWatch: !prev.ferryWatch }))}
              title={t('filters.ferryWatch')}
            >
              <span className="text-[11px]">🚢</span>
              {t('filters.ferryWatch')}
            </button>
          </div>
        )}

        {dashboardTab === 'iran' && (
          <div className="map-mode-toggle">
            <button
              className={`map-mode-btn ${mapMode === 'flat' ? 'active' : ''}`}
              onClick={() => setMapMode('flat')}
            >
              <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1" y="1" width="12" height="12" rx="1" stroke="currentColor" strokeWidth="1.5"/><line x1="1" y1="5" x2="13" y2="5" stroke="currentColor" strokeWidth="1"/><line x1="1" y1="9" x2="13" y2="9" stroke="currentColor" strokeWidth="1"/><line x1="5" y1="1" x2="5" y2="13" stroke="currentColor" strokeWidth="1"/><line x1="9" y1="1" x2="9" y2="13" stroke="currentColor" strokeWidth="1"/></svg>
              {t('mapMode.flat')}
            </button>
            <button
              className={`map-mode-btn ${mapMode === 'globe' ? 'active' : ''}`}
              onClick={() => setMapMode('globe')}
            >
              <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="6" stroke="currentColor" strokeWidth="1.5"/><ellipse cx="7" cy="7" rx="3" ry="6" stroke="currentColor" strokeWidth="1"/><line x1="1" y1="7" x2="13" y2="7" stroke="currentColor" strokeWidth="1"/></svg>
              {t('mapMode.globe')}
            </button>
            <button
              className={`map-mode-btn ${mapMode === 'satellite' ? 'active' : ''}`}
              onClick={() => setMapMode('satellite')}
            >
              <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="6" stroke="currentColor" strokeWidth="1.5"/><path d="M3 4.5C4.5 3 6 2.5 7 2.5s3 1 4.5 2.5" stroke="currentColor" strokeWidth="0.8"/><path d="M2.5 7.5C4 6 6 5 7 5s3.5 1 5 2.5" stroke="currentColor" strokeWidth="0.8"/><path d="M3 10C4.5 8.5 6 8 7 8s3 .5 4 1.5" stroke="currentColor" strokeWidth="0.8"/><circle cx="7" cy="6" r="1.5" fill="currentColor" opacity="0.3"/></svg>
              {t('mapMode.satellite')}
            </button>
          </div>
        )}

        <div className="header-info">
          <div className="header-counts">
            <span className="count-item ac-count">{dashboardTab === 'iran' ? aircraft.length : aircraftKorea.length} AC</span>
            <span className="count-item mil-count">{dashboardTab === 'iran' ? militaryCount : koreaMilitaryCount} MIL</span>
            <span className="count-item ship-count">{dashboardTab === 'iran' ? ships.length : koreaShips.length} SHIP</span>
            <span className="count-item sat-count">{dashboardTab === 'iran' ? satPositions.length : satPositionsKorea.length} SAT</span>
          </div>
          <div className="header-toggles">
            <button type="button" className="header-toggle-btn" onClick={toggleLang} title="Language">
              {i18n.language === 'ko' ? 'KO' : 'EN'}
            </button>
            <button type="button" className="header-toggle-btn" onClick={toggleTheme} title="Theme">
              {theme === 'dark' ? '🌙' : '☀️'}
            </button>
          </div>
          <div className="header-status">
            <span className={`status-dot ${isLive ? 'live' : replay.state.isPlaying ? 'live' : ''}`} />
            {isLive ? t('header.live') : replay.state.isPlaying ? t('header.replaying') : t('header.paused')}
          </div>
        </div>
      </header>

      {/* ═══════════════════════════════════════
           IRAN DASHBOARD
         ═══════════════════════════════════════ */}
      {dashboardTab === 'iran' && (
        <>
          <main className="app-main">
            <div className="map-panel">
              {mapMode === 'flat' ? (
                <ReplayMap
                  key="map-iran"
                  events={isLive ? [] : mergedEvents}
                  currentTime={currentTime}
                  aircraft={visibleAircraft}
                  satellites={satPositions}
                  ships={visibleShips}
                  layers={layers}
                  flyToTarget={flyToTarget}
                  onFlyToDone={() => setFlyToTarget(null)}
                />
              ) : mapMode === 'globe' ? (
                <GlobeMap
                  events={isLive ? [] : mergedEvents}
                  currentTime={currentTime}
                  aircraft={visibleAircraft}
                  satellites={satPositions}
                  ships={visibleShips}
                  layers={layers}
                />
              ) : (
                <SatelliteMap
                  events={isLive ? [] : mergedEvents}
                  currentTime={currentTime}
                  aircraft={visibleAircraft}
                  satellites={satPositions}
                  ships={visibleShips}
                  layers={layers}
                />
              )}
              <div className="map-overlay-left">
                <LayerPanel
                  layers={layers as unknown as Record<string, boolean>}
                  onToggle={toggleLayer as (key: string) => void}
                  aircraftByCategory={aircraftByCategory}
                  aircraftTotal={aircraft.length}
                  shipsByMtCategory={shipsByCategory}
                  shipTotal={ships.length}
                  satelliteCount={satPositions.length}
                  extraLayers={[
                    { key: 'events', label: t('layers.events'), color: '#a855f7' },
                    { key: 'koreanShips', label: `\u{1F1F0}\u{1F1F7} ${t('layers.koreanShips')}`, color: '#00e5ff', count: koreanShips.length },
                    { key: 'airports', label: t('layers.airports'), color: '#f59e0b' },
                    { key: 'oilFacilities', label: t('layers.oilFacilities'), color: '#d97706' },
                    { key: 'sensorCharts', label: t('layers.sensorCharts'), color: '#22c55e' },
                  ]}
                  hiddenAcCategories={hiddenAcCategories}
                  hiddenShipCategories={hiddenShipCategories}
                  onAcCategoryToggle={toggleAcCategory}
                  onShipCategoryToggle={toggleShipCategory}
                />
              </div>
            </div>

            <aside className="side-panel">
              <EventLog
                events={isLive ? [] : mergedEvents}
                currentTime={currentTime}
                totalShipCount={ships.length}
                koreanShips={koreanShips}
                koreanShipsByCategory={koreanShipsByCategory}
                osintFeed={osintFeed}
                isLive={isLive}
                dashboardTab={dashboardTab}
                onTabChange={setDashboardTab}
                ships={ships}
              />
            </aside>
          </main>

          {layers.sensorCharts && (
            <section className="charts-panel">
              <SensorChart
                data={sensorData}
                currentTime={currentTime}
                startTime={startTime}
                endTime={endTime}
              />
            </section>
          )}

          <footer className="app-footer">
            {isLive ? (
              <LiveControls
                currentTime={monitor.state.currentTime}
                historyMinutes={monitor.state.historyMinutes}
                onHistoryChange={monitor.setHistoryMinutes}
                aircraftCount={aircraft.length}
                shipCount={ships.length}
                satelliteCount={satPositions.length}
              />
            ) : (
              <>
                <ReplayControls
                  isPlaying={replay.state.isPlaying}
                  speed={replay.state.speed}
                  startTime={replay.state.startTime}
                  endTime={replay.state.endTime}
                  onPlay={replay.play}
                  onPause={replay.pause}
                  onReset={replay.reset}
                  onSpeedChange={replay.setSpeed}
                  onRangeChange={replay.setRange}
                />
                <TimelineSlider
                  currentTime={replay.state.currentTime}
                  startTime={replay.state.startTime}
                  endTime={replay.state.endTime}
                  events={mergedEvents}
                  onSeek={replay.seek}
                  onEventFlyTo={handleEventFlyTo}
                />
              </>
            )}

          </footer>
        </>
      )}

      {/* ═══════════════════════════════════════
           KOREA DASHBOARD
         ═══════════════════════════════════════ */}
      {dashboardTab === 'korea' && (
        <>
          <main className="app-main">
            <div className="map-panel">
              <KoreaMap
                ships={koreaFilteredShips}
                aircraft={visibleAircraftKorea}
                satellites={satPositionsKorea}
                layers={koreaLayers}
                osintFeed={osintFeed}
                currentTime={currentTime}
                koreaFilters={koreaFilters}
                transshipSuspects={transshipSuspects}
                cableWatchSuspects={cableWatchSet}
                dokdoWatchSuspects={dokdoWatchSet}
                dokdoAlerts={dokdoAlerts}
              />
              <div className="map-overlay-left">
                <LayerPanel
                  layers={koreaLayers}
                  onToggle={toggleKoreaLayer}
                  aircraftByCategory={koreaAircraftByCategory}
                  aircraftTotal={aircraftKorea.length}
                  shipsByMtCategory={koreaShipsByCategory}
                  shipTotal={koreaShips.length}
                  satelliteCount={satPositionsKorea.length}
                  extraLayers={[
                    { key: 'infra', label: t('layers.infra'), color: '#ffc107' },
                    { key: 'cables', label: t('layers.cables'), color: '#00e5ff' },
                    { key: 'cctv', label: t('layers.cctv'), color: '#ff6b6b', count: 15 },
                    { key: 'airports', label: t('layers.airports'), color: '#a78bfa', count: 16 },
                    { key: 'coastGuard', label: t('layers.coastGuard'), color: '#4dabf7', count: 46 },
                    { key: 'navWarning', label: t('layers.navWarning'), color: '#eab308' },
                    { key: 'osint', label: t('layers.osint'), color: '#ef4444' },
                    { key: 'eez', label: t('layers.eez'), color: '#3b82f6' },
                    { key: 'piracy', label: t('layers.piracy'), color: '#ef4444' },
                  ]}
                  hiddenAcCategories={hiddenAcCategories}
                  hiddenShipCategories={hiddenShipCategories}
                  onAcCategoryToggle={toggleAcCategory}
                  onShipCategoryToggle={toggleShipCategory}
                />
              </div>
            </div>

            <aside className="side-panel">
              <EventLog
                events={isLive ? [] : events}
                currentTime={currentTime}
                totalShipCount={koreaShips.length}
                koreanShips={koreaKoreanShips}
                koreanShipsByCategory={koreaShipsByCategory}
                chineseShips={koreaChineseShips}
                osintFeed={osintFeed}
                isLive={isLive}
                dashboardTab={dashboardTab}
                onTabChange={setDashboardTab}
                ships={koreaShips}
              />
            </aside>
          </main>

          <footer className="app-footer">
            {isLive ? (
              <LiveControls
                currentTime={monitor.state.currentTime}
                historyMinutes={monitor.state.historyMinutes}
                onHistoryChange={monitor.setHistoryMinutes}
                aircraftCount={aircraftKorea.length}
                shipCount={koreaShips.length}
                satelliteCount={satPositionsKorea.length}
              />
            ) : (
              <>
                <ReplayControls
                  isPlaying={replay.state.isPlaying}
                  speed={replay.state.speed}
                  startTime={replay.state.startTime}
                  endTime={replay.state.endTime}
                  onPlay={replay.play}
                  onPause={replay.pause}
                  onReset={replay.reset}
                  onSpeedChange={replay.setSpeed}
                  onRangeChange={replay.setRange}
                />
                <TimelineSlider
                  currentTime={replay.state.currentTime}
                  startTime={replay.state.startTime}
                  endTime={replay.state.endTime}
                  events={mergedEvents}
                  onSeek={replay.seek}
                  onEventFlyTo={handleEventFlyTo}
                />
              </>
            )}
          </footer>
        </>
      )}
    </div>
  );
}

export default App;
