import { useEffect, useRef, type MutableRefObject } from 'react';
import type maplibregl from 'maplibre-gl';
import type { GeoJSONSource, GeoJSONSourceSpecification, LayerSpecification } from 'maplibre-gl';
import type { AisTarget } from '../../../entities/aisTarget/model/types';
import type { LegacyVesselInfo } from '../../../entities/legacyVessel/model/types';
import type { Map3DSettings, MapProjectionId } from '../types';
import {
  GLOBE_ICON_HEADING_OFFSET_DEG,
  DEG2RAD,
} from '../constants';
import { isFiniteNumber } from '../lib/setUtils';
import { GLOBE_SHIP_CIRCLE_RADIUS_PROP_EXPR } from '../lib/mlExpressions';
import { kickRepaint, onMapStyleReady } from '../lib/mapCore';
import { getDisplayHeading, getGlobeBaseShipColor } from '../lib/shipUtils';
import { ensureFallbackShipImage } from '../lib/globeShipIcon';
import { clampNumber } from '../lib/geometry';
import { guardedSetVisibility } from '../lib/layerHelpers';

/** Globe 호버 오버레이 + 클릭 선택 */
export function useGlobeShipHover(
  mapRef: MutableRefObject<maplibregl.Map | null>,
  _projectionBusyRef: MutableRefObject<boolean>,
  reorderGlobeFeatureLayers: () => void,
  opts: {
    projection: MapProjectionId;
    settings: Map3DSettings;
    shipLayerData: AisTarget[];
    shipHoverOverlaySet: Set<number>;
    legacyHits: Map<number, LegacyVesselInfo> | null | undefined;
    selectedMmsi: number | null;
    mapSyncEpoch: number;
    onSelectMmsi: (mmsi: number | null) => void;
    onToggleHighlightMmsi?: (mmsi: number) => void;
    targets: AisTarget[];
    hasAuxiliarySelectModifier: (ev?: { shiftKey?: boolean; ctrlKey?: boolean; metaKey?: boolean } | null) => boolean;
  },
) {
  const {
    projection, settings, shipLayerData, shipHoverOverlaySet, legacyHits,
    selectedMmsi, mapSyncEpoch, onSelectMmsi, onToggleHighlightMmsi,
    targets, hasAuxiliarySelectModifier,
  } = opts;

  const epochRef = useRef(-1);
  const hoverSignatureRef = useRef('');

  // Globe hover overlay ships
  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;

    const imgId = 'ship-globe-icon';
    const srcId = 'ships-globe-hover-src';
    const haloId = 'ships-globe-hover-halo';
    const outlineId = 'ships-globe-hover-outline';
    const symbolId = 'ships-globe-hover';

    const hideHover = () => {
      for (const id of [symbolId, outlineId, haloId]) {
        guardedSetVisibility(map, id, 'none');
      }
    };

    const ensure = () => {
      if (projection !== 'globe' || !settings.showShips || shipHoverOverlaySet.size === 0) {
        hideHover();
        return;
      }

      if (epochRef.current !== mapSyncEpoch) {
        epochRef.current = mapSyncEpoch;
      }

      try {
        ensureFallbackShipImage(map, imgId);
      } catch { /* ignore */ }
      if (!map.hasImage(imgId)) {
        return;
      }

      const hovered = shipLayerData.filter((t) => shipHoverOverlaySet.has(t.mmsi) && !!legacyHits?.has(t.mmsi));
      if (hovered.length === 0) {
        hideHover();
        return;
      }
      const hoverSignature = hovered
        .map((t) => `${t.mmsi}:${t.lon.toFixed(6)}:${t.lat.toFixed(6)}:${t.heading ?? 0}`)
        .join('|');
      const hasHoverSource = map.getSource(srcId) != null;
      const hasHoverLayers = [symbolId, outlineId, haloId].every((id) => map.getLayer(id));
      if (hoverSignature === hoverSignatureRef.current && hasHoverSource && hasHoverLayers) {
        return;
      }
      hoverSignatureRef.current = hoverSignature;
      const needReorder = !hasHoverSource || !hasHoverLayers;

      const hoverGeojson: GeoJSON.FeatureCollection<GeoJSON.Point> = {
        type: 'FeatureCollection',
        features: hovered.map((t) => {
          const legacy = legacyHits?.get(t.mmsi) ?? null;
          const heading = getDisplayHeading({
            cog: t.cog,
            heading: t.heading,
            offset: GLOBE_ICON_HEADING_OFFSET_DEG,
          });
          const hull = clampNumber(
            (isFiniteNumber(t.length) ? t.length : 0) + (isFiniteNumber(t.width) ? t.width : 0) * 3,
            50,
            420,
          );
          const sizeScale = clampNumber(0.85 + (hull - 50) / 420, 0.82, 1.85);
          const selected = t.mmsi === selectedMmsi;
          const scale = selected ? 1.16 : 1.1;
          return {
            type: 'Feature',
            ...(isFiniteNumber(t.mmsi) ? { id: Math.trunc(t.mmsi) } : {}),
            geometry: { type: 'Point', coordinates: [t.lon, t.lat] },
            properties: {
              mmsi: t.mmsi,
              name: t.name || '',
              cog: heading,
              heading,
              sog: isFiniteNumber(t.sog) ? t.sog : 0,
              shipColor: getGlobeBaseShipColor({
                legacy: legacy?.shipCode || null,
                signalKindCode: t.signalKindCode || t.shipKindCode || '000027',
              }),
              iconSize3: clampNumber(0.35 * sizeScale * scale, 0.25, 1.45),
              iconSize7: clampNumber(0.45 * sizeScale * scale, 0.3, 1.7),
              iconSize10: clampNumber(0.58 * sizeScale * scale, 0.35, 2.1),
              iconSize14: clampNumber(0.85 * sizeScale * scale, 0.45, 3.0),
              iconSize18: clampNumber(2.5 * sizeScale * scale, 1.0, 7.0),
              selected: selected ? 1 : 0,
              permitted: legacy ? 1 : 0,
            },
          };
        }),
      };

      try {
        const existing = map.getSource(srcId) as GeoJSONSource | undefined;
        if (existing) existing.setData(hoverGeojson);
        else map.addSource(srcId, { type: 'geojson', data: hoverGeojson } as GeoJSONSourceSpecification);
      } catch (e) {
        console.warn('Ship hover source setup failed:', e);
        return;
      }

      const before = undefined;

      if (!map.getLayer(haloId)) {
        try {
          map.addLayer(
            {
              id: haloId,
              type: 'circle',
              source: srcId,
              layout: {
                visibility: 'visible',
                'circle-sort-key': [
                  'case',
                  ['==', ['get', 'selected'], 1], 120,
                  ['==', ['get', 'permitted'], 1], 115,
                  110,
                ] as never,
              },
              paint: {
                'circle-radius': GLOBE_SHIP_CIRCLE_RADIUS_PROP_EXPR,
                'circle-color': [
                  'case',
                  ['==', ['get', 'selected'], 1], 'rgba(14,234,255,1)',
                  'rgba(245,158,11,1)',
                ] as never,
                'circle-opacity': 0.42,
              },
            } as unknown as LayerSpecification,
            before,
          );
        } catch (e) {
          console.warn('Ship hover halo layer add failed:', e);
        }
      } else {
        guardedSetVisibility(map, haloId, 'visible');
      }

      if (!map.getLayer(outlineId)) {
        try {
          map.addLayer(
            {
              id: outlineId,
              type: 'circle',
              source: srcId,
              paint: {
                'circle-radius': GLOBE_SHIP_CIRCLE_RADIUS_PROP_EXPR,
                'circle-color': 'rgba(0,0,0,0)',
                'circle-stroke-color': [
                  'case',
                  ['==', ['get', 'selected'], 1], 'rgba(14,234,255,0.95)',
                  'rgba(245,158,11,0.95)',
                ] as never,
                'circle-stroke-width': [
                  'case',
                  ['==', ['get', 'selected'], 1], 3.8,
                  2.2,
                ] as never,
                'circle-stroke-opacity': 0.9,
              },
              layout: {
                visibility: 'visible',
                'circle-sort-key': [
                  'case',
                  ['==', ['get', 'selected'], 1], 121,
                  ['==', ['get', 'permitted'], 1], 116,
                  111,
                ] as never,
              },
            } as unknown as LayerSpecification,
            before,
          );
        } catch (e) {
          console.warn('Ship hover outline layer add failed:', e);
        }
      } else {
        guardedSetVisibility(map, outlineId, 'visible');
      }

      if (!map.getLayer(symbolId)) {
        try {
          map.addLayer(
            {
              id: symbolId,
              type: 'symbol',
              source: srcId,
              layout: {
                visibility: 'visible',
                'symbol-sort-key': [
                  'case',
                  ['==', ['get', 'selected'], 1], 122,
                  ['==', ['get', 'permitted'], 1], 117,
                  112,
                ] as never,
                'icon-image': imgId,
                'icon-size': [
                  'interpolate', ['linear'], ['zoom'],
                  3, ['to-number', ['get', 'iconSize3'], 0.35],
                  7, ['to-number', ['get', 'iconSize7'], 0.45],
                  10, ['to-number', ['get', 'iconSize10'], 0.58],
                  14, ['to-number', ['get', 'iconSize14'], 0.85],
                  18, ['to-number', ['get', 'iconSize18'], 2.5],
                ] as unknown as number[],
                'icon-allow-overlap': true,
                'icon-ignore-placement': true,
                'icon-anchor': 'center',
                'icon-rotate': ['to-number', ['get', 'heading'], 0],
                'icon-rotation-alignment': 'map',
                'icon-pitch-alignment': 'map',
              },
              paint: {
                'icon-color': ['coalesce', ['get', 'shipColor'], '#64748b'] as never,
                'icon-opacity': 1,
              },
            } as unknown as LayerSpecification,
            before,
          );
        } catch (e) {
          console.warn('Ship hover symbol layer add failed:', e);
        }
      } else {
        guardedSetVisibility(map, symbolId, 'visible');
      }

      if (needReorder) {
        reorderGlobeFeatureLayers();
      }
      kickRepaint(map);
    };

    const stop = onMapStyleReady(map, ensure);
    return () => {
      stop();
    };
  }, [
    projection,
    settings.showShips,
    shipLayerData,
    legacyHits,
    shipHoverOverlaySet,
    selectedMmsi,
    mapSyncEpoch,
    reorderGlobeFeatureLayers,
  ]);

  // Globe ship click selection
  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;
    if (projection !== 'globe' || !settings.showShips) return;

    const symbolId = 'ships-globe';
    const symbolLiteId = 'ships-globe-lite';
    const haloId = 'ships-globe-halo';
    const outlineId = 'ships-globe-outline';
    const clickedRadiusDeg2 = Math.pow(0.12, 2);

    const onClick = (e: maplibregl.MapMouseEvent) => {
      try {
        const layerIds = [symbolId, symbolLiteId, haloId, outlineId, 'ships-globe-alarm-pulse', 'ships-globe-alarm-badge'].filter((id) => map.getLayer(id));
        let feats: unknown[] = [];
        if (layerIds.length > 0) {
          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],
            ];
            feats = map.queryRenderedFeatures(bbox, { layers: layerIds }) as unknown[];
          } catch {
            feats = [];
          }
        }
        const f = feats?.[0];
        const props = ((f as { properties?: Record<string, unknown> } | undefined)?.properties || {}) as Record<
          string,
          unknown
        >;
        const mmsi = Number(props.mmsi);
        if (Number.isFinite(mmsi)) {
          if (hasAuxiliarySelectModifier(e as unknown as { shiftKey?: boolean; ctrlKey?: boolean; metaKey?: boolean })) {
            onToggleHighlightMmsi?.(mmsi);
            return;
          }
          onSelectMmsi(mmsi);
          return;
        }

        const clicked = { lat: e.lngLat.lat, lon: e.lngLat.lng };
        const cosLat = Math.cos(clicked.lat * DEG2RAD);
        let bestMmsi: number | null = null;
        let bestD2 = Number.POSITIVE_INFINITY;
        for (const t of targets) {
          if (!isFiniteNumber(t.lat) || !isFiniteNumber(t.lon)) continue;
          const dLon = (clicked.lon - t.lon) * cosLat;
          const dLat = clicked.lat - t.lat;
          const d2 = dLon * dLon + dLat * dLat;
          if (d2 <= clickedRadiusDeg2 && d2 < bestD2) {
            bestD2 = d2;
            bestMmsi = t.mmsi;
          }
        }
        if (bestMmsi != null) {
          if (hasAuxiliarySelectModifier(e as unknown as { shiftKey?: boolean; ctrlKey?: boolean; metaKey?: boolean })) {
            onToggleHighlightMmsi?.(bestMmsi);
            return;
          }
          onSelectMmsi(bestMmsi);
          return;
        }
      } catch {
        // ignore
      }
      onSelectMmsi(null);
    };

    map.on('click', onClick);
    return () => {
      try {
        map.off('click', onClick);
      } catch {
        // ignore
      }
    };
  }, [projection, settings.showShips, onSelectMmsi, onToggleHighlightMmsi, mapSyncEpoch, targets]);
}
