import { useEffect, 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 { MapToggleState } from '../../../features/mapToggles/MapToggles';
import type { Map3DSettings, MapProjectionId } from '../types';
import { LEGACY_CODE_COLORS_RGB, OTHER_AIS_SPEED_RGB, rgba as rgbaCss } from '../../../shared/lib/map/palette';
import { kickRepaint, onMapStyleReady } from '../lib/mapCore';
import { destinationPointLngLat } from '../lib/geometry';
import { isFiniteNumber } from '../lib/setUtils';
import { toValidBearingDeg, lightenColor } from '../lib/shipUtils';

export function usePredictionVectors(
  mapRef: MutableRefObject<maplibregl.Map | null>,
  projectionBusyRef: MutableRefObject<boolean>,
  reorderGlobeFeatureLayers: () => void,
  opts: {
    overlays: MapToggleState;
    settings: Map3DSettings;
    shipData: AisTarget[];
    legacyHits: Map<number, LegacyVesselInfo> | null | undefined;
    selectedMmsi: number | null;
    externalHighlightedSetRef: Set<number>;
    projection: MapProjectionId;
    baseMap: string;
    mapSyncEpoch: number;
  },
) {
  const { overlays, settings, shipData, legacyHits, selectedMmsi, externalHighlightedSetRef, projection, baseMap, mapSyncEpoch } = opts;

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

    const srcId = 'predict-vectors-src';
    const outlineId = 'predict-vectors-outline';
    const lineId = 'predict-vectors';
    const hlOutlineId = 'predict-vectors-hl-outline';
    const hlId = 'predict-vectors-hl';

    const ensure = () => {
      if (projectionBusyRef.current) return;
      if (!map.isStyleLoaded()) return;

      const visibility = overlays.predictVectors ? 'visible' : 'none';

      const horizonMinutes = 15;
      const horizonSeconds = horizonMinutes * 60;
      const metersPerSecondPerKnot = 0.514444;

      const features: GeoJSON.Feature<GeoJSON.LineString>[] = [];
      if (overlays.predictVectors && settings.showShips && shipData.length > 0) {
        for (const t of shipData) {
          const legacy = legacyHits?.get(t.mmsi) ?? null;
          const isTarget = !!legacy;
          const isSelected = selectedMmsi != null && t.mmsi === selectedMmsi;
          const isPinnedHighlight = externalHighlightedSetRef.has(t.mmsi);
          if (!isTarget && !isSelected && !isPinnedHighlight) continue;

          const sog = isFiniteNumber(t.sog) ? t.sog : null;
          const bearing = toValidBearingDeg(t.cog) ?? toValidBearingDeg(t.heading);
          if (sog == null || bearing == null) continue;
          if (sog < 0.2) continue;

          const distM = sog * metersPerSecondPerKnot * horizonSeconds;
          if (!Number.isFinite(distM) || distM <= 0) continue;

          const to = destinationPointLngLat([t.lon, t.lat], bearing, distM);

          const baseRgb = isTarget
            ? LEGACY_CODE_COLORS_RGB[legacy?.shipCode ?? ''] ?? OTHER_AIS_SPEED_RGB.moving
            : OTHER_AIS_SPEED_RGB.moving;
          const rgb = lightenColor(baseRgb, isTarget ? 0.55 : 0.62);
          const alpha = isTarget ? 0.72 : 0.52;
          const alphaHl = isTarget ? 0.92 : 0.84;
          const hl = isSelected || isPinnedHighlight ? 1 : 0;

          features.push({
            type: 'Feature',
            id: `pred-${t.mmsi}`,
            geometry: { type: 'LineString', coordinates: [[t.lon, t.lat], to] },
            properties: {
              mmsi: t.mmsi,
              minutes: horizonMinutes,
              sog,
              cog: bearing,
              target: isTarget ? 1 : 0,
              hl,
              color: rgbaCss(rgb, alpha),
              colorHl: rgbaCss(rgb, alphaHl),
            },
          });
        }
      }

      const fc: GeoJSON.FeatureCollection<GeoJSON.LineString> = { type: 'FeatureCollection', features };

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

      const ensureLayer = (id: string, paint: LayerSpecification['paint'], filter: unknown[]) => {
        if (!map.getLayer(id)) {
          try {
            map.addLayer(
              {
                id,
                type: 'line',
                source: srcId,
                filter: filter as never,
                layout: {
                  visibility,
                  'line-cap': 'round',
                  'line-join': 'round',
                },
                paint,
              } as unknown as LayerSpecification,
              undefined,
            );
          } catch (e) {
            console.warn('Prediction vector layer add failed:', e);
          }
        } else {
          try {
            map.setLayoutProperty(id, 'visibility', visibility);
            map.setFilter(id, filter as never);
            if (paint && typeof paint === 'object') {
              for (const [key, value] of Object.entries(paint)) {
                map.setPaintProperty(id, key as never, value as never);
              }
            }
          } catch {
            // ignore
          }
        }
      };

      const baseFilter = ['==', ['to-number', ['get', 'hl'], 0], 0] as unknown as unknown[];
      const hlFilter = ['==', ['to-number', ['get', 'hl'], 0], 1] as unknown as unknown[];

      ensureLayer(
        outlineId,
        {
          'line-color': 'rgba(2,6,23,0.86)',
          'line-width': 4.8,
          'line-opacity': 1,
          'line-blur': 0.2,
          'line-dasharray': [1.2, 1.8] as never,
        } as never,
        baseFilter,
      );
      ensureLayer(
        lineId,
        {
          'line-color': ['coalesce', ['get', 'color'], 'rgba(226,232,240,0.62)'] as never,
          'line-width': 2.4,
          'line-opacity': 1,
          'line-dasharray': [1.2, 1.8] as never,
        } as never,
        baseFilter,
      );
      ensureLayer(
        hlOutlineId,
        {
          'line-color': 'rgba(2,6,23,0.92)',
          'line-width': 6.4,
          'line-opacity': 1,
          'line-blur': 0.25,
          'line-dasharray': [1.2, 1.8] as never,
        } as never,
        hlFilter,
      );
      ensureLayer(
        hlId,
        {
          'line-color': ['coalesce', ['get', 'colorHl'], ['get', 'color'], 'rgba(241,245,249,0.92)'] as never,
          'line-width': 3.6,
          'line-opacity': 1,
          'line-dasharray': [1.2, 1.8] as never,
        } as never,
        hlFilter,
      );

      reorderGlobeFeatureLayers();
      kickRepaint(map);
    };

    const stop = onMapStyleReady(map, ensure);
    return () => {
      stop();
    };
  }, [
    overlays.predictVectors,
    settings.showShips,
    shipData,
    legacyHits,
    selectedMmsi,
    externalHighlightedSetRef,
    projection,
    baseMap,
    mapSyncEpoch,
    reorderGlobeFeatureLayers,
  ]);
}
