import { useCallback, useMemo, useRef, useState } from "react";
import { useAuth } from "../../shared/auth";
import { useTheme } from "../../shared/hooks";
import { useAisTargetPolling } from "../../features/aisPolling/useAisTargetPolling";
import type { DerivedLegacyVessel, LegacyAlarmKind } from "../../features/legacyDashboard/model/types";
import { ALARM_KIND_PRIORITY, LEGACY_ALARM_KINDS } from "../../features/legacyDashboard/model/types";
import { useLegacyVessels } from "../../entities/legacyVessel/api/useLegacyVessels";
import { buildLegacyVesselIndex } from "../../entities/legacyVessel/lib";
import type { LegacyVesselDataset } from "../../entities/legacyVessel/model/types";
import type { VesselTypeCode } from "../../entities/vessel/model/types";
import { VESSEL_TYPE_ORDER } from "../../entities/vessel/model/meta";
import { useZones } from "../../entities/zone/api/useZones";
import { useSubcables } from "../../entities/subcable/api/useSubcables";
import { AisInfoPanel } from "../../widgets/aisInfo/AisInfoPanel";
import { MapLegend } from "../../widgets/legend/MapLegend";
import { Map3D } from "../../widgets/map3d/Map3D";
import { Topbar } from "../../widgets/topbar/Topbar";
import { VesselInfoPanel } from "../../widgets/info/VesselInfoPanel";
import { SubcableInfoPanel } from "../../widgets/subcableInfo/SubcableInfoPanel";
import { DepthLegend } from "../../widgets/legend/DepthLegend";
import type { ShipImageInfo } from "../../entities/shipImage/model/types";
import ShipImageModal from "../../widgets/shipImage/ShipImageModal";
import { queryTrackByMmsi } from "../../features/trackReplay/services/trackQueryService";
import { useTrackQueryStore } from "../../features/trackReplay/stores/trackQueryStore";
import type { TrackQueryContext } from "../../features/trackReplay/model/track.types";
import { GlobalTrackReplayPanel } from "../../widgets/trackReplay/GlobalTrackReplayPanel";
import { useWeatherPolling } from "../../features/weatherOverlay/useWeatherPolling";
import { useWeatherOverlay } from "../../features/weatherOverlay/useWeatherOverlay";
import { WeatherPanel } from "../../widgets/weatherPanel/WeatherPanel";
import { WeatherOverlayPanel } from "../../widgets/weatherOverlay/WeatherOverlayPanel";
import { MapSettingsPanel } from "../../features/mapSettings/MapSettingsPanel";
import { OceanMapSettingsPanel } from "../../features/oceanMap/ui/OceanMapSettingsPanel";
import { EncMapSettingsPanel } from "../../features/encMap/ui/EncMapSettingsPanel";
import { useEncMapSettings } from "../../features/encMap/hooks/useEncMapSettings";
import { useAnnouncementPopup, AnnouncementModal } from "../../features/announcement";
import { useVesselSelectModal } from "../../features/vesselSelect";
import { VesselSelectModal } from "../../widgets/vesselSelect/VesselSelectModal";
import {
  buildLegacyHitMap,
  computeCountsByType,
  computeFcLinks,
  computeFleetCircles,
  computeLegacyAlarms,
  computePairLinks,
  deriveLegacyVessels,
  filterByShipCodes,
} from "../../features/legacyDashboard/model/derive";
import { MOCK_AIS_TARGETS, MOCK_LEGACY_ENTRIES } from "../../features/legacyDashboard/dev/mockOverlayData";
import { useDashboardState } from "./useDashboardState";
import type { Bbox } from "./useDashboardState";
import { DashboardSidebar } from "./DashboardSidebar";

function inBbox(lon: number, lat: number, bbox: Bbox) {
  const [lonMin, latMin, lonMax, latMax] = bbox;
  if (lat < latMin || lat > latMax) return false;
  if (lonMin <= lonMax) return lon >= lonMin && lon <= lonMax;
  return lon >= lonMin || lon <= lonMax;
}

function useLegacyIndex(data: LegacyVesselDataset | null) {
  return useMemo(() => (data ? buildLegacyVesselIndex(data.vessels) : null), [data]);
}

export function DashboardPage() {
  const { user, logout } = useAuth();
  const { theme, toggleTheme } = useTheme();
  const [isSidebarOpen, setIsSidebarOpen] = useState(false);
  const uid = user?.id ?? null;
  const isDevMode = user?.name?.includes('(DEV)') ?? false;

  // ── Announcement popup ──
  const { hasUnread, unreadAnnouncements, acknowledge } = useAnnouncementPopup(uid);

  // ── Vessel select modal (multi-track) ──
  const vesselSelectModal = useVesselSelectModal(uid);

  // ── Data fetching ──
  const { data: zones, error: zonesError } = useZones();
  const { data: legacyData, error: legacyError } = useLegacyVessels();
  const { data: subcableData } = useSubcables();
  const legacyIndex = useLegacyIndex(legacyData);

  // ── UI state ──
  const state = useDashboardState(uid);
  const {
    mapInstance, handleMapReady,
    viewBbox, setViewBbox,
    useViewportFilter,
    useApiBbox, apiBbox,
    selectedMmsi, setSelectedMmsi,
    highlightedMmsiSet,
    hoveredMmsiSet, setHoveredMmsiSet,
    hoveredFleetMmsiSet, setHoveredFleetMmsiSet,
    hoveredPairMmsiSet, setHoveredPairMmsiSet,
    hoveredFleetOwnerKey, setHoveredFleetOwnerKey,
    typeEnabled,
    showTargets, showOthers,
    baseMap, projection,
    mapStyleSettings, setMapStyleSettings,
    overlays, settings,
    mapView, setMapView,
    fleetFocus, setFleetFocus,
    hoveredCableId, setHoveredCableId,
    selectedCableId, setSelectedCableId,
    trackContextMenu, handleOpenTrackMenu, handleCloseTrackMenu,
    handleProjectionLoadingChange,
    setIsGlobeShipsReady,
    showMapLoader,
    clock, adminMode, onLogoClick,
    setUniqueSorted, setSortedIfChanged, toggleHighlightedMmsi,
    alarmKindEnabled,
  } = state;

  // ── ENC map settings (runtime updates) ──
  const mapRefForEnc = useRef<import('maplibre-gl').Map | null>(null);
  const handleMapReadyWithRef = useCallback((map: import('maplibre-gl').Map) => {
    mapRefForEnc.current = map;
    handleMapReady(map);
  }, [handleMapReady]);
  useEncMapSettings(mapRefForEnc, baseMap, state.encMapSettings);

  // ── Weather ──
  const weather = useWeatherPolling(zones);
  const weatherOverlay = useWeatherOverlay(mapInstance);

  // ── AIS polling ──
  const { targets, snapshot } = useAisTargetPolling({
    chnprmship: { initialMinutes: 120, pollMinutes: 2, intervalMs: 60_000, retentionMinutes: 120 },
    recent: { initialMinutes: 15, pollMinutes: 12, intervalMs: 600_000, retentionMinutes: 72 },
    bbox: useApiBbox ? apiBbox : undefined,
  });

  // ── Derived data ──
  const targetsInScope = useMemo(() => {
    const base = (!useViewportFilter || !viewBbox)
      ? targets
      : targets.filter((t) => typeof t.lon === "number" && typeof t.lat === "number" && inBbox(t.lon, t.lat, viewBbox));
    return isDevMode ? [...base, ...MOCK_AIS_TARGETS] : base;
  }, [targets, useViewportFilter, viewBbox, isDevMode]);

  const legacyHits = useMemo(() => {
    const hits = buildLegacyHitMap(targetsInScope, legacyIndex);
    if (isDevMode) {
      for (const [mmsi, info] of MOCK_LEGACY_ENTRIES) hits.set(mmsi, info);
    }
    return hits;
  }, [targetsInScope, legacyIndex, isDevMode]);

  // ── Track request ──
  const handleRequestTrack = useCallback(async (mmsi: number, minutes: number) => {
    const trackStore = useTrackQueryStore.getState();
    const queryKey = `${mmsi}:${minutes}:${Date.now()}`;

    const target = targets.find((item) => item.mmsi === mmsi);
    const isPermitted = legacyHits.has(mmsi);

    const endDate = new Date();
    const startDate = new Date(endDate.getTime() - minutes * 60_000);
    const legacy = legacyHits.get(mmsi);
    const context: TrackQueryContext = {
      mmsi,
      startTimeIso: startDate.toISOString(),
      endTimeIso: endDate.toISOString(),
      shipNameHint: target?.name,
      shipKindCodeHint: target?.shipKindCode,
      nationalCodeHint: target?.nationalCode,
      isPermitted,
      vesselType: legacy?.shipCode,
      ownerCn: legacy?.ownerCn,
      ownerRoman: legacy?.ownerRoman,
      permitNo: legacy?.permitNo,
      pairPermitNo: legacy?.pairPermitNo,
      ton: legacy?.ton,
      callSign: legacy?.callSign,
      workSeaArea: legacy?.workSeaArea,
    };
    trackStore.beginQuery(queryKey, context);

    try {
      const tracks = await queryTrackByMmsi({
        mmsi,
        minutes,
        shipNameHint: target?.name,
        shipKindCodeHint: target?.shipKindCode,
        nationalCodeHint: target?.nationalCode,
        isPermitted,
      });

      if (tracks.length > 0) {
        trackStore.applyTracksSuccess(tracks, queryKey);
      } else {
        trackStore.applyQueryError('항적 데이터가 없습니다.', queryKey);
      }
    } catch (e) {
      trackStore.applyQueryError(e instanceof Error ? e.message : '항적 조회에 실패했습니다.', queryKey);
    }
  }, [targets, legacyHits]);

  const legacyVesselsAll = useMemo(() => deriveLegacyVessels({ targets: targetsInScope, legacyHits, zones }), [targetsInScope, legacyHits, zones]);
  const legacyCounts = useMemo(() => computeCountsByType(legacyVesselsAll), [legacyVesselsAll]);

  const legacyVesselsFiltered = useMemo(() => {
    if (!showTargets) return [];
    return filterByShipCodes(legacyVesselsAll, typeEnabled);
  }, [legacyVesselsAll, showTargets, typeEnabled]);

  const legacyMmsiForMap = useMemo(() => new Set(legacyVesselsFiltered.map((v) => v.mmsi)), [legacyVesselsFiltered]);

  const targetsForMap = useMemo(() => {
    const out = [];
    for (const t of targetsInScope) {
      const mmsi = t.mmsi;
      if (typeof mmsi !== "number") continue;
      const isLegacy = legacyHits.has(mmsi);
      if (isLegacy) {
        if (!showTargets) continue;
        if (!legacyMmsiForMap.has(mmsi)) continue;
        out.push(t);
        continue;
      }
      if (showOthers) out.push(t);
    }
    return out;
  }, [targetsInScope, legacyHits, showTargets, showOthers, legacyMmsiForMap]);

  const pairLinksAll = useMemo(() => computePairLinks(legacyVesselsAll), [legacyVesselsAll]);
  const fcLinksAll = useMemo(() => computeFcLinks(legacyVesselsAll), [legacyVesselsAll]);
  const alarms = useMemo(() => computeLegacyAlarms({ vessels: legacyVesselsAll, pairLinks: pairLinksAll, fcLinks: fcLinksAll }), [legacyVesselsAll, pairLinksAll, fcLinksAll]);

  const alarmKindCounts = useMemo(() => {
    const base = Object.fromEntries(LEGACY_ALARM_KINDS.map((k) => [k, 0])) as Record<typeof LEGACY_ALARM_KINDS[number], number>;
    for (const a of alarms) {
      base[a.kind] = (base[a.kind] ?? 0) + 1;
    }
    return base;
  }, [alarms]);

  const enabledAlarmKinds = useMemo(() => LEGACY_ALARM_KINDS.filter((k) => alarmKindEnabled[k]), [alarmKindEnabled]);
  const allAlarmKindsEnabled = enabledAlarmKinds.length === LEGACY_ALARM_KINDS.length;

  const filteredAlarms = useMemo(() => {
    if (allAlarmKindsEnabled) return alarms;
    const enabled = new Set(enabledAlarmKinds);
    return alarms.filter((a) => enabled.has(a.kind));
  }, [alarms, enabledAlarmKinds, allAlarmKindsEnabled]);

  const alarmMmsiMap = useMemo(() => {
    const m = new Map<number, LegacyAlarmKind>();
    for (const kind of ALARM_KIND_PRIORITY) {
      for (const alarm of filteredAlarms) {
        if (alarm.kind !== kind) continue;
        for (const mmsi of alarm.relatedMmsi) m.set(mmsi, kind);
      }
    }
    return m;
  }, [filteredAlarms]);

  const pairLinksForMap = useMemo(() => computePairLinks(legacyVesselsFiltered), [legacyVesselsFiltered]);
  const fcLinksForMap = useMemo(() => computeFcLinks(legacyVesselsFiltered), [legacyVesselsFiltered]);
  const fleetCirclesForMap = useMemo(() => computeFleetCircles(legacyVesselsFiltered), [legacyVesselsFiltered]);

  const selectedLegacyVessel = useMemo(() => {
    if (!selectedMmsi) return null;
    return legacyVesselsAll.find((v) => v.mmsi === selectedMmsi) ?? null;
  }, [legacyVesselsAll, selectedMmsi]);

  const selectedTarget = useMemo(() => {
    if (!selectedMmsi) return null;
    return targetsInScope.find((t) => t.mmsi === selectedMmsi) ?? null;
  }, [targetsInScope, selectedMmsi]);

  const selectedLegacyInfo = useMemo(() => {
    if (!selectedMmsi) return null;
    return legacyHits.get(selectedMmsi) ?? null;
  }, [selectedMmsi, legacyHits]);

  const availableTargetMmsiSet = useMemo(
    () => new Set(targetsInScope.map((t) => t.mmsi).filter((mmsi) => Number.isFinite(mmsi))),
    [targetsInScope],
  );
  const activeHighlightedMmsiSet = useMemo(
    () => highlightedMmsiSet.filter((mmsi) => availableTargetMmsiSet.has(mmsi)),
    [highlightedMmsiSet, availableTargetMmsiSet],
  );

  // 모달 상태
  const [imageModal, setImageModal] = useState<{
    images?: ShipImageInfo[];
    initialIndex?: number;
    initialImagePath?: string;
    totalCount?: number;
    imo?: number;
    vesselName?: string;
  } | null>(null);

  const handleOpenImageModal = useCallback((mmsi: number) => {
    const target = targetsInScope.find((t) => t.mmsi === mmsi);
    if (!target?.shipImagePath) return;
    const vessel = legacyVesselsAll.find((v) => v.mmsi === mmsi);
    const vesselName = vessel?.name || target.name || '';
    setImageModal({
      initialImagePath: target.shipImagePath,
      totalCount: target.shipImageCount ?? 1,
      imo: target.imo > 0 ? target.imo : undefined,
      vesselName,
    });
  }, [targetsInScope, legacyVesselsAll]);

  // 지도에서 선박 클릭 시: 선택 + 사진이 있으면 자동으로 모달 표시
  const handleMapSelectMmsi = useCallback((mmsi: number | null) => {
    setSelectedMmsi(mmsi);
    if (mmsi) handleOpenImageModal(mmsi);
  }, [setSelectedMmsi, handleOpenImageModal]);

  const handlePanelOpenImageModal = useCallback((index: number, images?: ShipImageInfo[]) => {
    if (!selectedMmsi) return;
    const target = targetsInScope.find((t) => t.mmsi === selectedMmsi);
    const vessel = legacyVesselsAll.find((v) => v.mmsi === selectedMmsi);
    const vesselName = vessel?.name || target?.name || '';
    setImageModal({
      images,
      initialIndex: index,
      initialImagePath: target?.shipImagePath ?? undefined,
      totalCount: target?.shipImageCount ?? 1,
      imo: target && target.imo > 0 ? target.imo : undefined,
      vesselName,
    });
  }, [selectedMmsi, targetsInScope, legacyVesselsAll]);

  const fishingCount = legacyVesselsAll.filter((v) => v.state.isFishing).length;
  const transitCount = legacyVesselsAll.filter((v) => v.state.isTransit).length;

  const enabledTypes = useMemo(() => VESSEL_TYPE_ORDER.filter((c) => typeEnabled[c]), [typeEnabled]);
  const speedPanelType = (selectedLegacyVessel?.shipCode ?? (enabledTypes.length === 1 ? enabledTypes[0] : "PT")) as VesselTypeCode;
  const handleFleetContextMenu = (ownerKey: string, mmsis: number[]) => {
    if (!mmsis.length) return;
    const members = mmsis
      .map((mmsi) => legacyVesselsFiltered.find((v): v is DerivedLegacyVessel => v.mmsi === mmsi))
      .filter(
        (v): v is DerivedLegacyVessel & { lat: number; lon: number } =>
          v != null && typeof v.lat === "number" && typeof v.lon === "number" && Number.isFinite(v.lat) && Number.isFinite(v.lon),
      );

    if (members.length === 0) return;
    const sumLon = members.reduce((acc, v) => acc + v.lon, 0);
    const sumLat = members.reduce((acc, v) => acc + v.lat, 0);
    const center: [number, number] = [sumLon / members.length, sumLat / members.length];
    setFleetFocus({ id: `${ownerKey}-${Date.now()}`, center, zoom: 9 });
  };

  // ── Render ──
  return (
    <div className="grid h-screen grid-cols-[310px_1fr] grid-rows-[44px_1fr] max-md:grid-cols-[1fr] max-md:grid-rows-[auto_1fr]">
      <Topbar
        total={legacyVesselsAll.length}
        fishing={fishingCount}
        transit={transitCount}
        pairLinks={pairLinksAll.length}
        alarms={alarms.length}
        clock={clock}
        adminMode={adminMode}
        onLogoClick={onLogoClick}
        userName={user?.name}
        onLogout={logout}
        theme={theme}
        onToggleTheme={toggleTheme}
        isSidebarOpen={isSidebarOpen}
        onMenuToggle={() => setIsSidebarOpen((v) => !v)}
        onOpenMultiTrack={vesselSelectModal.open}
      />

      <DashboardSidebar
        isOpen={isSidebarOpen}
        onClose={() => setIsSidebarOpen(false)}
        state={state}
        legacyVesselsAll={legacyVesselsAll}
        legacyVesselsFiltered={legacyVesselsFiltered}
        legacyCounts={legacyCounts}
        selectedLegacyVessel={selectedLegacyVessel}
        activeHighlightedMmsiSet={activeHighlightedMmsiSet}
        legacyHits={legacyHits}
        filteredAlarms={filteredAlarms}
        alarms={alarms}
        alarmKindCounts={alarmKindCounts}
        speedPanelType={speedPanelType}
        onFleetContextMenu={handleFleetContextMenu}
        snapshot={snapshot}
        legacyError={legacyError}
        legacyData={legacyData}
        targetsInScope={targetsInScope}
        zonesError={zonesError}
        zones={zones}
        legacyIndex={legacyIndex}
      />

      <div className="relative bg-[#010610]">
        {showMapLoader ? (
          <div className="map-loader-overlay" role="status" aria-live="polite">
            <div className="map-loader-overlay__panel">
              <div className="map-loader-overlay__spinner" />
              <div className="map-loader-overlay__text">지도 모드 동기화 중...</div>
              <div className="map-loader-overlay__bar">
                <div className="map-loader-overlay__fill" />
              </div>
            </div>
          </div>
        ) : null}
        <Map3D
          targets={targetsForMap}
          zones={zones}
          selectedMmsi={selectedMmsi}
          highlightedMmsiSet={activeHighlightedMmsiSet}
          hoveredMmsiSet={hoveredMmsiSet}
          hoveredFleetMmsiSet={hoveredFleetMmsiSet}
          hoveredPairMmsiSet={hoveredPairMmsiSet}
          hoveredFleetOwnerKey={hoveredFleetOwnerKey}
          settings={settings}
          baseMap={baseMap}
          projection={projection}
          overlays={overlays}
          onSelectMmsi={handleMapSelectMmsi}
          onToggleHighlightMmsi={toggleHighlightedMmsi}
          onViewBboxChange={setViewBbox}
          legacyHits={legacyHits}
          pairLinks={pairLinksForMap}
          fcLinks={fcLinksForMap}
          fleetCircles={fleetCirclesForMap}
          fleetFocus={fleetFocus}
          onProjectionLoadingChange={handleProjectionLoadingChange}
          onGlobeShipsReady={setIsGlobeShipsReady}
          onHoverMmsi={(mmsis) => setHoveredMmsiSet(setUniqueSorted(mmsis))}
          onClearMmsiHover={() => setHoveredMmsiSet((prev) => (prev.length === 0 ? prev : []))}
          onHoverPair={(pairMmsis) => setHoveredPairMmsiSet(setUniqueSorted(pairMmsis))}
          onClearPairHover={() => setHoveredPairMmsiSet([])}
          onHoverFleet={(ownerKey, fleetMmsis) => {
            setHoveredFleetOwnerKey(ownerKey);
            setHoveredFleetMmsiSet(setSortedIfChanged(fleetMmsis));
          }}
          onClearFleetHover={() => {
            setHoveredFleetOwnerKey(null);
            setHoveredFleetMmsiSet((prev) => (prev.length === 0 ? prev : []));
          }}
          subcableGeo={subcableData?.geo ?? null}
          hoveredCableId={hoveredCableId}
          onHoverCable={setHoveredCableId}
          onClickCable={(id) => setSelectedCableId((prev) => (prev === id ? null : id))}
          mapStyleSettings={mapStyleSettings}
          initialView={mapView}
          onViewStateChange={setMapView}
          activeTrack={null}
          trackContextMenu={trackContextMenu}
          onRequestTrack={handleRequestTrack}
          onCloseTrackMenu={handleCloseTrackMenu}
          onOpenTrackMenu={handleOpenTrackMenu}
          onMapReady={handleMapReadyWithRef}
          alarmMmsiMap={alarmMmsiMap}
          onClickShipPhoto={handleOpenImageModal}
          freeCamera={state.freeCamera}
          oceanMapSettings={state.oceanMapSettings}
          encMapSettings={state.encMapSettings}
        />
        <GlobalTrackReplayPanel
          isVesselListOpen={vesselSelectModal.isOpen}
          onToggleVesselList={() => {
            if (vesselSelectModal.isOpen) vesselSelectModal.close();
            else vesselSelectModal.reopen();
          }}
        />
        <WeatherPanel
          snapshot={weather.snapshot}
          isLoading={weather.isLoading}
          error={weather.error}
          onRefresh={weather.refresh}
        />
        <WeatherOverlayPanel {...weatherOverlay} />
        {baseMap === 'ocean' ? (
          <OceanMapSettingsPanel value={state.oceanMapSettings} onChange={state.setOceanMapSettings} />
        ) : baseMap === 'enc' ? (
          <EncMapSettingsPanel value={state.encMapSettings} onChange={state.setEncMapSettings} />
        ) : (
          <MapSettingsPanel value={mapStyleSettings} onChange={setMapStyleSettings} />
        )}
        {baseMap !== 'ocean' && baseMap !== 'enc' && <DepthLegend depthStops={mapStyleSettings.depthStops} />}
        <MapLegend />
        {selectedLegacyVessel ? (
          <VesselInfoPanel vessel={selectedLegacyVessel} allVessels={legacyVesselsAll} onClose={() => setSelectedMmsi(null)} onSelectMmsi={setSelectedMmsi} imo={selectedTarget && selectedTarget.imo > 0 ? selectedTarget.imo : undefined} shipImagePath={selectedTarget?.shipImagePath} shipImageCount={selectedTarget?.shipImageCount} onOpenImageModal={handlePanelOpenImageModal} />
        ) : selectedTarget ? (
          <AisInfoPanel target={selectedTarget} legacy={selectedLegacyInfo} onClose={() => setSelectedMmsi(null)} onOpenImageModal={handlePanelOpenImageModal} />
        ) : null}
        {hasUnread && (
          <AnnouncementModal announcements={unreadAnnouncements} onConfirm={acknowledge} />
        )}
        {vesselSelectModal.isOpen && (
          <VesselSelectModal modal={vesselSelectModal} vessels={legacyVesselsAll} />
        )}
        {imageModal && (
          <ShipImageModal
            images={imageModal.images}
            initialIndex={imageModal.initialIndex}
            initialImagePath={imageModal.initialImagePath}
            totalCount={imageModal.totalCount}
            imo={imageModal.imo}
            vesselName={imageModal.vesselName}
            onClose={() => setImageModal(null)}
          />
        )}
        {selectedCableId && subcableData?.details.get(selectedCableId) ? (
          <SubcableInfoPanel
            detail={subcableData.details.get(selectedCableId)!}
            color={subcableData.geo.features.find((f) => f.properties.id === selectedCableId)?.properties.color}
            onClose={() => setSelectedCableId(null)}
          />
        ) : null}
      </div>
    </div>
  );
}
