import { useCallback, useEffect, useRef, type MutableRefObject } from 'react';
import maplibregl from 'maplibre-gl';
import type { AisTarget } from '../../../entities/aisTarget/model/types';
import type { LegacyVesselInfo } from '../../../entities/legacyVessel/model/types';
import type { MapToggleState } from '../../../features/mapToggles/MapToggles';
import type { Map3DSettings, MapProjectionId } from '../types';
import { toIntMmsi, toSafeNumber } from '../lib/setUtils';
import {
  getShipTooltipHtml,
  getPairLinkTooltipHtml,
  getFcLinkTooltipHtml,
  getRangeTooltipHtml,
  getFleetCircleTooltipHtml,
} from '../lib/tooltips';
import { getZoneIdFromProps, getZoneDisplayNameFromProps } from '../lib/zoneUtils';

// setData() 후 타일 재빌드 중 queryRenderedFeatures가 일시적으로 빈 배열을 반환.
// 즉시 clear 대신 딜레이를 두어 깜박임 방지.
const TOOLTIP_CLEAR_DELAY_MS = 150;

export function useGlobeInteraction(
  mapRef: MutableRefObject<maplibregl.Map | null>,
  projectionBusyRef: MutableRefObject<boolean>,
  opts: {
    projection: MapProjectionId;
    settings: Map3DSettings;
    overlays: MapToggleState;
    targets: AisTarget[];
    shipData: AisTarget[];
    shipByMmsi: Map<number, AisTarget>;
    selectedMmsi: number | null;
    hoveredZoneId: string | null;
    legacyHits: Map<number, LegacyVesselInfo> | null | undefined;
    clearDeckHoverPairs: () => void;
    clearDeckHoverMmsi: () => void;
    clearMapFleetHoverState: () => void;
    setDeckHoverPairs: (next: number[]) => void;
    setDeckHoverMmsi: (next: number[]) => void;
    setMapFleetHoverState: (ownerKey: string | null, vesselMmsis: number[]) => void;
    setHoveredZoneId: (updater: (prev: string | null) => string | null) => void;
  },
) {
  const {
    projection, legacyHits, shipByMmsi,
    clearDeckHoverPairs, clearDeckHoverMmsi, clearMapFleetHoverState,
    setDeckHoverPairs, setDeckHoverMmsi, setMapFleetHoverState, setHoveredZoneId,
  } = opts;

  const mapTooltipRef = useRef<maplibregl.Popup | null>(null);

  const clearGlobeTooltip = useCallback(() => {
    if (!mapTooltipRef.current) return;
    try {
      mapTooltipRef.current.remove();
    } catch {
      // ignore
    }
    mapTooltipRef.current = null;
  }, []);

  // eslint-disable-next-line react-hooks/preserve-manual-memoization
  const setGlobeTooltip = useCallback((lngLat: maplibregl.LngLatLike, tooltipHtml: string) => {
    const map = mapRef.current;
    if (!map) return;
    if (!mapTooltipRef.current) {
      mapTooltipRef.current = new maplibregl.Popup({
        closeButton: false,
        closeOnClick: false,
        maxWidth: '360px',
        className: 'maplibre-tooltip-popup',
      });
    }

    const container = document.createElement('div');
    container.className = 'maplibre-tooltip-popup__content';
    container.innerHTML = tooltipHtml;

    mapTooltipRef.current.setLngLat(lngLat).setDOMContent(container).addTo(map);
  }, []);

  // buildGlobeFeatureTooltip을 ref로 관리 — legacyHits/shipByMmsi가 매 AIS poll마다 변경되므로
  // useCallback 의존성으로 넣으면 effect가 재실행되어 cleanup에서 tooltip이 제거됨
  // ref로 관리하면 effect 재실행 없이 항상 최신 함수 참조
  type TooltipFeature = { properties?: Record<string, unknown> | null; layer?: { id?: string } } | null | undefined;
  const buildTooltipRef = useRef<(feature: TooltipFeature) => { html: string } | null>(() => null);

  const buildGlobeFeatureTooltip = useCallback(
    (feature: { properties?: Record<string, unknown> | null; layer?: { id?: string } } | null | undefined) => {
      if (!feature) return null;
      const props = feature.properties || {};
      const layerId = feature.layer?.id;

      const maybeMmsi = toIntMmsi(props.mmsi);
      if (maybeMmsi != null && maybeMmsi > 0) {
        return getShipTooltipHtml({ mmsi: maybeMmsi, targetByMmsi: shipByMmsi, legacyHits });
      }

      if (layerId === 'pair-lines-ml') {
        const warn = props.warn === true;
        const aMmsi = toIntMmsi(props.aMmsi);
        const bMmsi = toIntMmsi(props.bMmsi);
        if (aMmsi == null || bMmsi == null) return null;
        return getPairLinkTooltipHtml({
          warn, distanceNm: toSafeNumber(props.distanceNm),
          aMmsi, bMmsi, legacyHits, targetByMmsi: shipByMmsi,
        });
      }

      if (layerId === 'fc-lines-ml') {
        const fcMmsi = toIntMmsi(props.fcMmsi);
        const otherMmsi = toIntMmsi(props.otherMmsi);
        if (fcMmsi == null || otherMmsi == null) return null;
        return getFcLinkTooltipHtml({
          suspicious: props.suspicious === true,
          distanceNm: toSafeNumber(props.distanceNm),
          fcMmsi, otherMmsi, legacyHits, targetByMmsi: shipByMmsi,
        });
      }

      if (layerId === 'pair-range-ml') {
        const aMmsi = toIntMmsi(props.aMmsi);
        const bMmsi = toIntMmsi(props.bMmsi);
        if (aMmsi == null || bMmsi == null) return null;
        return getRangeTooltipHtml({
          warn: props.warn === true,
          distanceNm: toSafeNumber(props.distanceNm),
          aMmsi, bMmsi, legacyHits,
        });
      }

      if (layerId === 'fleet-circles-ml') {
        return getFleetCircleTooltipHtml({
          ownerKey: String(props.ownerKey ?? ''),
          ownerLabel: String(props.ownerLabel ?? props.ownerKey ?? ''),
          count: Number(props.count ?? 0),
        });
      }

      const zoneLabel = getZoneDisplayNameFromProps(props);
      if (zoneLabel) {
        return { html: `<div style="font-size: 12px; font-family: system-ui;">${zoneLabel}</div>` };
      }

      return null;
    },
    [legacyHits, shipByMmsi],
  );

  useEffect(() => {
    buildTooltipRef.current = buildGlobeFeatureTooltip;
  }, [buildGlobeFeatureTooltip]);

  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;

    const resetGlobeHoverStates = () => {
      clearDeckHoverMmsi();
      clearDeckHoverPairs();
      setHoveredZoneId((prev) => (prev === null ? prev : null));
      clearMapFleetHoverState();
    };

    const normalizeMmsiList = (value: unknown): number[] => {
      let arr = value;
      // MapLibre는 GeoJSON 배열 프로퍼티를 JSON 문자열로 반환할 수 있음
      if (typeof arr === 'string') {
        try { arr = JSON.parse(arr); } catch { return []; }
      }
      if (!Array.isArray(arr)) return [];
      const out: number[] = [];
      for (const n of arr) {
        const m = toIntMmsi(n);
        if (m != null) out.push(m);
      }
      return out;
    };

    // 지연 clear 타이머 — setData() 타일 재빌드 중 일시적 빈 결과를 무시
    let clearTimer: ReturnType<typeof setTimeout> | null = null;

    const scheduleClear = () => {
      if (clearTimer) return; // 이미 예약됨
      clearTimer = setTimeout(() => {
        clearTimer = null;
        resetGlobeHoverStates();
        clearGlobeTooltip();
      }, TOOLTIP_CLEAR_DELAY_MS);
    };

    const cancelClear = () => {
      if (clearTimer) { clearTimeout(clearTimer); clearTimer = null; }
    };

    const onMouseMove = (e: maplibregl.MapMouseEvent) => {
      if (projection !== 'globe') {
        cancelClear();
        clearGlobeTooltip();
        resetGlobeHoverStates();
        return;
      }
      if (projectionBusyRef.current) {
        return; // 전환 중에는 기존 상태 유지 (clear하면 깜박임)
      }

      let candidateLayerIds: string[] = [];
      try {
        candidateLayerIds = [
          'ships-globe', 'ships-globe-lite', 'ships-globe-halo', 'ships-globe-outline',
          'ships-globe-alarm-pulse', 'ships-globe-alarm-badge',
          'pair-lines-ml', 'fc-lines-ml',
          'fleet-circles-ml',
          'pair-range-ml',
          'zones-fill', 'zones-line', 'zones-label',
        ].filter((id) => map.getLayer(id));
      } catch {
        candidateLayerIds = [];
      }

      if (candidateLayerIds.length === 0) {
        scheduleClear();
        return;
      }

      let rendered: Array<{ properties?: Record<string, unknown> | null; layer?: { id?: string } }> = [];
      try {
        const tolerance = 10;
        const bbox: [maplibregl.PointLike, maplibregl.PointLike] = [
          [e.point.x - tolerance, e.point.y - tolerance],
          [e.point.x + tolerance, e.point.y + tolerance],
        ];
        rendered = map.queryRenderedFeatures(bbox, { layers: candidateLayerIds }) as unknown as Array<{
          properties?: Record<string, unknown> | null;
          layer?: { id?: string };
        }>;
      } catch {
        rendered = [];
      }

      const priority = [
        'ships-globe', 'ships-globe-lite', 'ships-globe-halo', 'ships-globe-outline',
        'ships-globe-alarm-pulse', 'ships-globe-alarm-badge',
        'pair-lines-ml', 'fc-lines-ml', 'pair-range-ml',
        'fleet-circles-ml',
        'zones-fill', 'zones-line', 'zones-label',
      ];

      const first = priority.map((id) => rendered.find((r) => r.layer?.id === id)).find(Boolean) as
        | { properties?: Record<string, unknown> | null; layer?: { id?: string } }
        | undefined;

      if (!first) {
        // 피처 없음 — 타일 재빌드 중 일시적 누락일 수 있으므로 지연 clear
        scheduleClear();
        return;
      }

      // 피처 발견 — 지연 clear 취소
      cancelClear();

      const layerId = first.layer?.id;
      const props = first.properties || {};
      const isShipLayer =
        layerId === 'ships-globe' ||
        layerId === 'ships-globe-lite' ||
        layerId === 'ships-globe-halo' ||
        layerId === 'ships-globe-outline' ||
        layerId === 'ships-globe-alarm-pulse' ||
        layerId === 'ships-globe-alarm-badge';
      const isPairLayer = layerId === 'pair-lines-ml' || layerId === 'pair-range-ml';
      const isFcLayer = layerId === 'fc-lines-ml';
      const isFleetLayer = layerId === 'fleet-circles-ml';
      const isZoneLayer = layerId === 'zones-fill' || layerId === 'zones-line' || layerId === 'zones-label';

      if (isShipLayer) {
        const mmsi = toIntMmsi(props.mmsi);
        setDeckHoverMmsi(mmsi == null ? [] : [mmsi]);
        clearDeckHoverPairs();
        clearMapFleetHoverState();
        setHoveredZoneId((prev) => (prev === null ? prev : null));
      } else if (isPairLayer) {
        const aMmsi = toIntMmsi(props.aMmsi);
        const bMmsi = toIntMmsi(props.bMmsi);
        setDeckHoverPairs([...(aMmsi == null ? [] : [aMmsi]), ...(bMmsi == null ? [] : [bMmsi])]);
        clearDeckHoverMmsi();
        clearMapFleetHoverState();
        setHoveredZoneId((prev) => (prev === null ? prev : null));
      } else if (isFcLayer) {
        const from = toIntMmsi(props.fcMmsi);
        const to = toIntMmsi(props.otherMmsi);
        const fromTo = [from, to].filter((v): v is number => v != null);
        setDeckHoverPairs(fromTo);
        setDeckHoverMmsi(fromTo);
        clearMapFleetHoverState();
        setHoveredZoneId((prev) => (prev === null ? prev : null));
      } else if (isFleetLayer) {
        const ownerKey = String(props.ownerKey ?? '');
        const list = normalizeMmsiList(props.vesselMmsis);
        setMapFleetHoverState(ownerKey || null, list);
        clearDeckHoverMmsi();
        clearDeckHoverPairs();
        setHoveredZoneId((prev) => (prev === null ? prev : null));
      } else if (isZoneLayer) {
        clearMapFleetHoverState();
        clearDeckHoverMmsi();
        clearDeckHoverPairs();
        const zoneId = getZoneIdFromProps(props);
        setHoveredZoneId(() => zoneId || null);
      } else {
        resetGlobeHoverStates();
      }

      const tooltip = buildTooltipRef.current(first);
      if (!tooltip) {
        if (!isZoneLayer) {
          resetGlobeHoverStates();
        }
        clearGlobeTooltip();
        return;
      }

      const content = tooltip?.html ?? '';
      if (content) {
        setGlobeTooltip(e.lngLat, content);
        return;
      }
      clearGlobeTooltip();
    };

    const onMouseOut = () => {
      cancelClear();
      resetGlobeHoverStates();
      clearGlobeTooltip();
    };

    map.on('mousemove', onMouseMove);
    map.on('mouseout', onMouseOut);

    return () => {
      cancelClear();
      map.off('mousemove', onMouseMove);
      map.off('mouseout', onMouseOut);
      // cleanup에서 tooltip 제거하지 않음 — 의존성 변경(AIS poll 등)으로 effect가
      // 재실행될 때 tooltip이 사라지는 문제 방지. tooltip은 mousemove/mouseout 이벤트가 처리.
    };
  }, [
    projection,
    clearGlobeTooltip,
    clearMapFleetHoverState,
    clearDeckHoverPairs,
    clearDeckHoverMmsi,
    setDeckHoverPairs,
    setDeckHoverMmsi,
    setMapFleetHoverState,
    setGlobeTooltip,
  ]);

  // 컴포넌트 unmount 시에만 tooltip 제거
  useEffect(() => {
    return () => { clearGlobeTooltip(); };
  }, [clearGlobeTooltip]);
}
