import { useCallback, useEffect, useRef, type MutableRefObject } from 'react';
import type maplibregl from 'maplibre-gl';
import type { GeoJSONSource, GeoJSONSourceSpecification, LayerSpecification } from 'maplibre-gl';
import type { FcLink, FleetCircle } from '../../../features/legacyDashboard/model/types';
import type { MapToggleState } from '../../../features/mapToggles/MapToggles';
import type { DashSeg, MapProjectionId } from '../types';
import {
  FC_LINE_NORMAL_ML, FC_LINE_SUSPICIOUS_ML,
  FC_LINE_NORMAL_ML_HL, FC_LINE_SUSPICIOUS_ML_HL,
  FLEET_LINE_ML, FLEET_LINE_ML_HL,
} from '../constants';
import { makeUniqueSorted } from '../lib/setUtils';
import {
  makeFcSegmentFeatureId,
  makeFleetCircleFeatureId,
} from '../lib/featureIds';
import {
  makeMmsiAnyEndpointExpr,
  makeFleetOwnerMatchExpr,
  makeFleetMemberMatchExpr,
} from '../lib/mlExpressions';
import { kickRepaint, onMapStyleReady } from '../lib/mapCore';
import { circleRingLngLat } from '../lib/geometry';
import { dashifyLine } from '../lib/dashifyLine';

// ── Overlay line width constants ──
const FC_LINE_W_NORMAL = 2.2;
const FC_LINE_W_HL = 3.2;
const FLEET_LINE_W_NORMAL = 2.0;
const FLEET_LINE_W_HL = 3.0;

// ── Breathing animation constants ──
const BREATHE_AMP = 2.0;
const BREATHE_PERIOD_MS = 1200;

/** Globe FC lines + fleet circles 오버레이 (stroke only — fill 제거) */
export function useGlobeFcFleetOverlay(
  mapRef: MutableRefObject<maplibregl.Map | null>,
  _projectionBusyRef: MutableRefObject<boolean>,
  reorderGlobeFeatureLayers: () => void,
  opts: {
    overlays: MapToggleState;
    fcLinks: FcLink[] | undefined;
    fleetCircles: FleetCircle[] | undefined;
    projection: MapProjectionId;
    mapSyncEpoch: number;
    hoveredFleetMmsiList: number[];
    hoveredFleetOwnerKeyList: string[];
    hoveredPairMmsiList: number[];
  },
) {
  const {
    overlays, fcLinks, fleetCircles, projection, mapSyncEpoch,
    hoveredFleetMmsiList, hoveredFleetOwnerKeyList, hoveredPairMmsiList,
  } = opts;
  const breatheRafRef = useRef<number>(0);

  // paint state ref — 데이터 effect에서 레이어 생성 직후 최신 paint state를 즉시 적용하기 위해 사용
  const paintStateRef = useRef<() => void>(() => {});

  // ── FC lines 데이터 effect ──
  // projectionBusy/isStyleLoaded 선행 가드 제거 — try/catch로 처리
  // 실패 시 다음 AIS poll(mapSyncEpoch 변경)에서 자연스럽게 재시도
  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;

    const srcId = 'fc-lines-ml-src';
    const layerId = 'fc-lines-ml';

    const ensure = () => {
      if (projection !== 'globe') {
        try {
          if (map.getLayer(layerId)) map.setPaintProperty(layerId, 'line-opacity', 0);
        } catch { /* ignore */ }
        return;
      }

      const segs: DashSeg[] = [];
      for (const l of fcLinks || []) {
        segs.push(...dashifyLine(l.from, l.to, l.suspicious, l.distanceNm, l.fcMmsi, l.otherMmsi));
      }
      if (segs.length === 0) {
        try {
          if (map.getLayer(layerId)) map.setPaintProperty(layerId, 'line-opacity', 0);
        } catch { /* ignore */ }
        return;
      }

      const fc: GeoJSON.FeatureCollection<GeoJSON.LineString> = {
        type: 'FeatureCollection',
        features: segs.map((s, idx) => ({
          type: 'Feature',
          id: makeFcSegmentFeatureId(s.fromMmsi ?? -1, s.toMmsi ?? -1, idx),
          geometry: { type: 'LineString', coordinates: [s.from, s.to] },
            properties: {
              type: 'fc',
              suspicious: s.suspicious,
              distanceNm: s.distanceNm,
              fcMmsi: s.fromMmsi ?? -1,
              otherMmsi: s.toMmsi ?? -1,
            },
        })),
      };

      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 {
        return; // 다음 poll에서 재시도
      }

      const needReorder = !map.getLayer(layerId);
      if (needReorder) {
        try {
          map.addLayer(
            {
              id: layerId,
              type: 'line',
              source: srcId,
              layout: { 'line-cap': 'round', 'line-join': 'round', visibility: 'visible' },
              paint: {
                'line-color': FC_LINE_NORMAL_ML,
                'line-width': FC_LINE_W_NORMAL,
                'line-opacity': 0,
              },
            } as unknown as LayerSpecification,
            undefined,
          );
        } catch {
          return; // 다음 poll에서 재시도
        }
        reorderGlobeFeatureLayers();
      }

      paintStateRef.current();
      kickRepaint(map);
    };

    // 초기 style 로드 대기 — 이후에는 AIS poll 사이클이 재시도 보장
    const stop = onMapStyleReady(map, ensure);
    ensure();
    return () => { stop(); };
  }, [projection, fcLinks, mapSyncEpoch, reorderGlobeFeatureLayers]);

  // ── Fleet circles 데이터 effect (stroke only — fill 제거) ──
  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;

    const srcId = 'fleet-circles-ml-src';
    const layerId = 'fleet-circles-ml';

    const ensure = () => {
      if (projection !== 'globe' || (fleetCircles?.length ?? 0) === 0) {
        try {
          if (map.getLayer(layerId)) map.setPaintProperty(layerId, 'line-opacity', 0);
        } catch { /* ignore */ }
        return;
      }

      const circles = fleetCircles || [];

      const fcLine: GeoJSON.FeatureCollection<GeoJSON.LineString> = {
        type: 'FeatureCollection',
        features: circles.map((c) => {
          const ring = circleRingLngLat(c.center, c.radiusNm * 1852);
          return {
            type: 'Feature',
            id: makeFleetCircleFeatureId(c.ownerKey),
            geometry: { type: 'LineString', coordinates: ring },
            properties: {
              type: 'fleet',
              ownerKey: c.ownerKey,
              ownerLabel: c.ownerLabel,
              count: c.count,
              vesselMmsis: c.vesselMmsis,
            },
          };
        }),
      };

      try {
        const existing = map.getSource(srcId) as GeoJSONSource | undefined;
        if (existing) existing.setData(fcLine);
        else map.addSource(srcId, { type: 'geojson', data: fcLine } as GeoJSONSourceSpecification);
      } catch {
        return; // 다음 poll에서 재시도
      }

      const needReorder = !map.getLayer(layerId);
      if (needReorder) {
        try {
          map.addLayer(
            {
              id: layerId,
              type: 'line',
              source: srcId,
              layout: { 'line-cap': 'round', 'line-join': 'round', visibility: 'visible' },
              paint: {
                'line-color': FLEET_LINE_ML,
                'line-width': FLEET_LINE_W_NORMAL,
                'line-opacity': 0,
              },
            } as unknown as LayerSpecification,
            undefined,
          );
        } catch {
          return; // 다음 poll에서 재시도
        }
        reorderGlobeFeatureLayers();
      }

      paintStateRef.current();
      kickRepaint(map);
    };

    const stop = onMapStyleReady(map, ensure);
    ensure();
    return () => { stop(); };
  }, [projection, fleetCircles, mapSyncEpoch, reorderGlobeFeatureLayers]);

  // ── FC + Fleet paint state update (가시성 + 하이라이트 통합) ──
  // eslint-disable-next-line react-hooks/preserve-manual-memoization
  const updateFcFleetPaintStates = useCallback(() => {
    if (projection !== 'globe') return;
    const map = mapRef.current;
    if (!map) return;

    const fleetAwarePairMmsiList = makeUniqueSorted([...hoveredPairMmsiList, ...hoveredFleetMmsiList]);

    const fcEndpointHighlightExpr = fleetAwarePairMmsiList.length > 0
      ? makeMmsiAnyEndpointExpr('fcMmsi', 'otherMmsi', fleetAwarePairMmsiList)
      : false;

    const fleetOwnerMatchExpr =
      hoveredFleetOwnerKeyList.length > 0 ? makeFleetOwnerMatchExpr(hoveredFleetOwnerKeyList) : false;
    const fleetMemberExpr =
      hoveredFleetMmsiList.length > 0 ? makeFleetMemberMatchExpr(hoveredFleetMmsiList) : false;
    const fleetHighlightExpr =
      hoveredFleetOwnerKeyList.length > 0 || hoveredFleetMmsiList.length > 0
        ? (['any', fleetOwnerMatchExpr, fleetMemberExpr] as never)
        : false;

    // ── FC lines ──
    const fcVisible = overlays.fcLines;
    // ── Fleet circles ──
    const fleetVisible = overlays.fleetCircles;
    try {
      if (map.getLayer('fc-lines-ml')) {
        map.setPaintProperty('fc-lines-ml', 'line-opacity', fcVisible ? 0.9 : 0);
        if (fcVisible) {
          map.setPaintProperty(
            'fc-lines-ml', 'line-color',
            fcEndpointHighlightExpr !== false
              ? ['case', fcEndpointHighlightExpr, ['case', ['boolean', ['get', 'suspicious'], false], FC_LINE_SUSPICIOUS_ML_HL, FC_LINE_NORMAL_ML_HL], ['boolean', ['get', 'suspicious'], false], FC_LINE_SUSPICIOUS_ML, FC_LINE_NORMAL_ML] as never
              : ['case', ['boolean', ['get', 'suspicious'], false], FC_LINE_SUSPICIOUS_ML, FC_LINE_NORMAL_ML] as never,
          );
          map.setPaintProperty(
            'fc-lines-ml', 'line-width',
            fcEndpointHighlightExpr !== false
              ? ['case', fcEndpointHighlightExpr, FC_LINE_W_HL, FC_LINE_W_NORMAL] as never
              : FC_LINE_W_NORMAL,
          );
        }
      }
    } catch {
      // ignore
    }

    try {
      if (map.getLayer('fleet-circles-ml')) {
        map.setPaintProperty('fleet-circles-ml', 'line-opacity', fleetVisible ? 0.85 : 0);
        if (fleetVisible) {
          map.setPaintProperty('fleet-circles-ml', 'line-color',
            fleetHighlightExpr !== false
              ? ['case', fleetHighlightExpr, FLEET_LINE_ML_HL, FLEET_LINE_ML] as never
              : FLEET_LINE_ML,
          );
          map.setPaintProperty('fleet-circles-ml', 'line-width',
            fleetHighlightExpr !== false
              ? ['case', fleetHighlightExpr, FLEET_LINE_W_HL, FLEET_LINE_W_NORMAL] as never
              : FLEET_LINE_W_NORMAL,
          );
        }
      }
    } catch {
      // ignore
    }

    kickRepaint(map);
  }, [projection, hoveredFleetMmsiList, hoveredFleetOwnerKeyList, hoveredPairMmsiList, overlays.fcLines, overlays.fleetCircles]);

  // paintStateRef를 최신 콜백으로 유지
  useEffect(() => {
    paintStateRef.current = updateFcFleetPaintStates;
  }, [updateFcFleetPaintStates]);

  // paint state 동기화
  useEffect(() => {
    updateFcFleetPaintStates();
  }, [mapSyncEpoch, hoveredFleetMmsiList, hoveredFleetOwnerKeyList, hoveredPairMmsiList, projection, overlays.fcLines, overlays.fleetCircles, updateFcFleetPaintStates, fcLinks, fleetCircles]);

  // ── Breathing animation ──
  useEffect(() => {
    const map = mapRef.current;
    const hasFleetHover = hoveredFleetOwnerKeyList.length > 0 || hoveredFleetMmsiList.length > 0;
    const hasFcHover = hoveredPairMmsiList.length > 0 || hoveredFleetMmsiList.length > 0;
    if (!map || (!hasFleetHover && !hasFcHover) || projection !== 'globe') {
      if (breatheRafRef.current) cancelAnimationFrame(breatheRafRef.current);
      breatheRafRef.current = 0;
      return;
    }

    const fleetAwarePairMmsiList = makeUniqueSorted([...hoveredPairMmsiList, ...hoveredFleetMmsiList]);
    const fcEndpointHighlightExpr = fleetAwarePairMmsiList.length > 0
      ? makeMmsiAnyEndpointExpr('fcMmsi', 'otherMmsi', fleetAwarePairMmsiList)
      : false;
    const fleetOwnerMatchExpr = hoveredFleetOwnerKeyList.length > 0 ? makeFleetOwnerMatchExpr(hoveredFleetOwnerKeyList) : false;
    const fleetMemberExpr = hoveredFleetMmsiList.length > 0 ? makeFleetMemberMatchExpr(hoveredFleetMmsiList) : false;
    const fleetHighlightExpr =
      hoveredFleetOwnerKeyList.length > 0 || hoveredFleetMmsiList.length > 0
        ? (['any', fleetOwnerMatchExpr, fleetMemberExpr] as never)
        : false;

    const animate = () => {
      if (!map.isStyleLoaded()) {
        breatheRafRef.current = requestAnimationFrame(animate);
        return;
      }
      const t = (Math.sin(Date.now() / BREATHE_PERIOD_MS * Math.PI * 2) + 1) / 2;
      try {
        if (map.getLayer('fc-lines-ml') && fcEndpointHighlightExpr !== false) {
          const hlW = FC_LINE_W_HL + t * BREATHE_AMP;
          map.setPaintProperty('fc-lines-ml', 'line-width',
            ['case', fcEndpointHighlightExpr, hlW, FC_LINE_W_NORMAL] as never);
        }
        if (map.getLayer('fleet-circles-ml') && fleetHighlightExpr !== false) {
          const hlW = FLEET_LINE_W_HL + t * BREATHE_AMP;
          map.setPaintProperty('fleet-circles-ml', 'line-width',
            ['case', fleetHighlightExpr, hlW, FLEET_LINE_W_NORMAL] as never);
        }
      } catch {
        // ignore
      }
      breatheRafRef.current = requestAnimationFrame(animate);
    };
    breatheRafRef.current = requestAnimationFrame(animate);
    return () => {
      if (breatheRafRef.current) cancelAnimationFrame(breatheRafRef.current);
      breatheRafRef.current = 0;
    };
  }, [hoveredFleetMmsiList, hoveredFleetOwnerKeyList, hoveredPairMmsiList, projection]);
}
