import { useCallback, useMemo } from "react";
import { useAuth } from "../../shared/auth";
import { useAisTargetPolling } from "../../features/aisPolling/useAisTargetPolling";
import type { DerivedLegacyVessel } from "../../features/legacyDashboard/model/types";
import { 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 { queryTrackByMmsi } from "../../features/trackReplay/services/trackQueryService";
import { useTrackQueryStore } from "../../features/trackReplay/stores/trackQueryStore";
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 {
  buildLegacyHitMap,
  computeCountsByType,
  computeFcLinks,
  computeFleetCircles,
  computeLegacyAlarms,
  computePairLinks,
  deriveLegacyVessels,
  filterByShipCodes,
} from "../../features/legacyDashboard/model/derive";
import { useDashboardState } from "./useDashboardState";
import type { Bbox } from "./useDashboardState";
import { DashboardSidebar } from "./DashboardSidebar";

const AIS_CENTER = {
  lon: 126.95,
  lat: 35.95,
  radiusMeters: 2_000_000,
};

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 uid = user?.id ?? null;

  // ── 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;

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

  // ── AIS polling ──
  const { targets, snapshot } = useAisTargetPolling({
    chnprmshipMinutes: 120,
    incrementalMinutes: 2,
    intervalMs: 60_000,
    retentionMinutes: 120,
    bbox: useApiBbox ? apiBbox : undefined,
    centerLon: useApiBbox ? undefined : AIS_CENTER.lon,
    centerLat: useApiBbox ? undefined : AIS_CENTER.lat,
    radiusMeters: useApiBbox ? undefined : AIS_CENTER.radiusMeters,
  });

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

    try {
      const target = targets.find((item) => item.mmsi === mmsi);
      const tracks = await queryTrackByMmsi({
        mmsi,
        minutes,
        shipNameHint: target?.name,
      });

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

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

  const legacyHits = useMemo(() => buildLegacyHitMap(targetsInScope, legacyIndex), [targetsInScope, legacyIndex]);
  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 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 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 alarmFilterSummary = allAlarmKindsEnabled ? "전체" : `${enabledAlarmKinds.length}/${LEGACY_ALARM_KINDS.length}`;

  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="app">
      <Topbar
        total={legacyVesselsAll.length}
        fishing={fishingCount}
        transit={transitCount}
        pairLinks={pairLinksAll.length}
        alarms={alarms.length}
        pollingStatus={snapshot.status}
        lastFetchMinutes={snapshot.lastFetchMinutes}
        clock={clock}
        adminMode={adminMode}
        onLogoClick={onLogoClick}
        userName={user?.name}
        onLogout={logout}
      />

      <DashboardSidebar
        state={state}
        legacyVesselsAll={legacyVesselsAll}
        legacyVesselsFiltered={legacyVesselsFiltered}
        legacyCounts={legacyCounts}
        selectedLegacyVessel={selectedLegacyVessel}
        activeHighlightedMmsiSet={activeHighlightedMmsiSet}
        legacyHits={legacyHits}
        filteredAlarms={filteredAlarms}
        alarms={alarms}
        alarmKindCounts={alarmKindCounts}
        allAlarmKindsEnabled={allAlarmKindsEnabled}
        alarmFilterSummary={alarmFilterSummary}
        speedPanelType={speedPanelType}
        onFleetContextMenu={handleFleetContextMenu}
        snapshot={snapshot}
        legacyError={legacyError}
        legacyData={legacyData}
        targetsInScope={targetsInScope}
        zonesError={zonesError}
        zones={zones}
        legacyIndex={legacyIndex}
      />

      <div className="map-area">
        {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={setSelectedMmsi}
          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={handleMapReady}
        />
        <GlobalTrackReplayPanel />
        <WeatherPanel
          snapshot={weather.snapshot}
          isLoading={weather.isLoading}
          error={weather.error}
          onRefresh={weather.refresh}
        />
        <WeatherOverlayPanel {...weatherOverlay} />
        <MapSettingsPanel value={mapStyleSettings} onChange={setMapStyleSettings} />
        <DepthLegend depthStops={mapStyleSettings.depthStops} />
        <MapLegend />
        {selectedLegacyVessel ? (
          <VesselInfoPanel vessel={selectedLegacyVessel} allVessels={legacyVesselsAll} onClose={() => setSelectedMmsi(null)} onSelectMmsi={setSelectedMmsi} />
        ) : selectedTarget ? (
          <AisInfoPanel target={selectedTarget} legacy={selectedLegacyInfo} onClose={() => setSelectedMmsi(null)} />
        ) : 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>
  );
}
