import { useCallback, useEffect, useRef, type MutableRefObject, type Dispatch, type SetStateAction } from 'react';
import maplibregl, { type StyleSpecification } from 'maplibre-gl';
import { MapboxOverlay } from '@deck.gl/mapbox';
import { MaplibreDeckCustomLayer } from '../MaplibreDeckCustomLayer';
import type { BaseMapId, MapProjectionId, MapViewState } from '../types';
import { DECK_VIEW_ID, ANCHORED_SHIP_ICON_ID } from '../constants';
import { kickRepaint, onMapStyleReady } from '../lib/mapCore';
import { ensureSeamarkOverlay } from '../layers/seamark';
import { resolveMapStyle } from '../layers/bathymetry';

export function useMapInit(
  containerRef: MutableRefObject<HTMLDivElement | null>,
  mapRef: MutableRefObject<maplibregl.Map | null>,
  overlayRef: MutableRefObject<MapboxOverlay | null>,
  overlayInteractionRef: MutableRefObject<MapboxOverlay | null>,
  globeDeckLayerRef: MutableRefObject<MaplibreDeckCustomLayer | null>,
  baseMapRef: MutableRefObject<BaseMapId>,
  projectionRef: MutableRefObject<MapProjectionId>,
  opts: {
    baseMap: BaseMapId;
    projection: MapProjectionId;
    showSeamark: boolean;
    onViewBboxChange?: (bbox: [number, number, number, number]) => void;
    setMapSyncEpoch: Dispatch<SetStateAction<number>>;
    initialView?: MapViewState | null;
    onViewStateChange?: (view: MapViewState) => void;
  },
) {
  const { onViewBboxChange, setMapSyncEpoch, showSeamark } = opts;
  const showSeamarkRef = useRef(showSeamark);
  const onViewStateChangeRef = useRef(opts.onViewStateChange);
  useEffect(() => { onViewStateChangeRef.current = opts.onViewStateChange; }, [opts.onViewStateChange]);
  useEffect(() => {
    showSeamarkRef.current = showSeamark;
  }, [showSeamark]);

  const ensureMercatorOverlay = useCallback(() => {
    const map = mapRef.current;
    if (!map) return null;
    if (overlayRef.current) return overlayRef.current;
    try {
      const next = new MapboxOverlay({ interleaved: true, layers: [] } as unknown as never);
      map.addControl(next);
      overlayRef.current = next;
      return next;
    } catch (e) {
      console.warn('Deck overlay create failed:', e);
      return null;
    }
  }, []);

  const pulseMapSync = useCallback(() => {
    setMapSyncEpoch((prev) => prev + 1);
    requestAnimationFrame(() => {
      kickRepaint(mapRef.current);
      setMapSyncEpoch((prev) => prev + 1);
    });
  }, [setMapSyncEpoch]);

  useEffect(() => {
    if (!containerRef.current || mapRef.current) return;

    let map: maplibregl.Map | null = null;
    let cancelled = false;
    let viewSaveTimer: ReturnType<typeof setInterval> | null = null;
    const controller = new AbortController();

    (async () => {
      let style: string | StyleSpecification = '/map/styles/osm-seamark.json';
      try {
        style = await resolveMapStyle(baseMapRef.current, controller.signal);
      } catch (e) {
        console.warn('Map style init failed, falling back to local raster style:', e);
        style = '/map/styles/osm-seamark.json';
      }
      if (cancelled || !containerRef.current) return;

      const iv = opts.initialView;
      map = new maplibregl.Map({
        container: containerRef.current,
        style,
        center: iv?.center ?? [126.5, 34.2],
        zoom: iv?.zoom ?? 7,
        pitch: 0,
        bearing: 0,
        maxPitch: 85,
        dragRotate: false,
        pitchWithRotate: true,
        touchPitch: false,
        scrollZoom: { around: 'center' },
      });

      map.addControl(new maplibregl.NavigationControl({ showZoom: true, showCompass: true }), 'top-left');
      map.addControl(new maplibregl.ScaleControl({ maxWidth: 120, unit: 'metric' }), 'bottom-left');

      // MapLibre 내부 placement TypeError 방어 + globe easing 경고 억제
      // symbol layer 추가/제거와 render cycle 간 타이밍 이슈로 발생하는 에러 억제
      // globe projection에서 scrollZoom이 easeTo(around)를 호출하면 경고 발생 → 구조적 한계로 억제
      {
        const origRender = (map as unknown as { _render: (arg?: number) => void })._render;
        const origWarn = console.warn;
        (map as unknown as { _render: (arg?: number) => void })._render = function (arg?: number) {
          // globe 모드에서 scrollZoom의 easeTo around 경고 억제
          // eslint-disable-next-line no-console
          console.warn = function (...args: unknown[]) {
            if (typeof args[0] === 'string') {
              const msg = args[0] as string;
              if (msg.includes('Easing around a point')) return;
              // vertex 경고는 디버그용으로 1회만 출력 후 억제
              if (msg.includes('Max vertices per segment')) {
                origWarn.apply(console, args as [unknown, ...unknown[]]);
                return;
              }
            }
            origWarn.apply(console, args as [unknown, ...unknown[]]);
          };
          try {
            origRender.call(this, arg);
          } catch (e) {
            if (e instanceof TypeError && (e.message?.includes("reading 'get'") || e.message?.includes('placement'))) {
              return;
            }
            throw e;
          } finally {
            // eslint-disable-next-line no-console
            console.warn = origWarn;
          }
        };
      }

      // Globe 모드 전환 시 지연을 제거하기 위해 ship.svg를 미리 로드
      {
        const SHIP_IMG_ID = 'ship-globe-icon';
        const localMap = map;
        void localMap
          .loadImage('/assets/ship.svg')
          .then((response) => {
            if (cancelled || !localMap) return;
            const img = (response as { data?: HTMLImageElement | ImageBitmap } | undefined)?.data;
            if (!img) return;
            try {
              if (!localMap.hasImage(SHIP_IMG_ID)) localMap.addImage(SHIP_IMG_ID, img, { pixelRatio: 2, sdf: true });
              if (!localMap.hasImage(ANCHORED_SHIP_ICON_ID)) localMap.addImage(ANCHORED_SHIP_ICON_ID, img, { pixelRatio: 2, sdf: true });
            } catch {
              // ignore — fallback canvas icon이 useGlobeShips에서 사용됨
            }
          })
          .catch(() => {
            // ignore — useGlobeShips에서 fallback 처리
          });
      }

      mapRef.current = map;

      // 양쪽 overlay를 모두 초기화 — projection 전환 시 재생성 비용 제거
      ensureMercatorOverlay();
      globeDeckLayerRef.current = new MaplibreDeckCustomLayer({
        id: 'deck-globe',
        viewId: DECK_VIEW_ID,
        deckProps: { layers: [] },
      });

      function applyProjection() {
        if (!map) return;
        const next = projectionRef.current;
        if (next === 'mercator') return;
        try {
          map.setProjection({ type: next });
          map.setRenderWorldCopies(next !== 'globe');
        } catch (e) {
          console.warn('Projection apply failed:', e);
        }
      }

      onMapStyleReady(map, () => {
        applyProjection();
        // deck-globe를 항상 추가 (projection과 무관)
        const deckLayer = globeDeckLayerRef.current;
        if (deckLayer && !map!.getLayer(deckLayer.id)) {
          try {
            map!.addLayer(deckLayer);
          } catch {
            // ignore
          }
        }
        if (!showSeamarkRef.current) return;
        try {
          ensureSeamarkOverlay(map!, 'bathymetry-lines-coarse');
        } catch {
          // ignore
        }
      });

      const emitBbox = () => {
        const cb = onViewBboxChange;
        if (!cb || !map) return;
        const b = map.getBounds();
        cb([b.getWest(), b.getSouth(), b.getEast(), b.getNorth()]);
      };
      map.on('load', emitBbox);
      map.on('moveend', emitBbox);

      // 60초 인터벌로 뷰 상태 저장 (mercator일 때만)
      viewSaveTimer = setInterval(() => {
        const cb = onViewStateChangeRef.current;
        if (!cb || !map || projectionRef.current !== 'mercator') return;
        const c = map.getCenter();
        cb({ center: [c.lng, c.lat], zoom: map.getZoom(), bearing: map.getBearing(), pitch: map.getPitch() });
      }, 60_000);

      map.once('load', () => {
        // Globe 배경(타일 밖)을 심해 색상과 맞춰 타일 경계 seam을 비가시화
        try {
          map!.setSky({
            'sky-color': '#010610',
            'horizon-color': '#010610',
            'fog-color': '#010610',
            'fog-ground-blend': 1,
            'sky-horizon-blend': 0,
            'atmosphere-blend': 0,
          });
        } catch {
          // ignore
        }
        // 캔버스 배경도 심해색으로 통일
        try {
          map!.getCanvas().style.background = '#010610';
        } catch {
          // ignore
        }

        if (showSeamarkRef.current) {
          try {
            ensureSeamarkOverlay(map!, 'bathymetry-lines-coarse');
          } catch {
            // ignore
          }
          try {
            const opacity = showSeamarkRef.current ? 0.85 : 0;
            map!.setPaintProperty('seamark', 'raster-opacity', opacity);
          } catch {
            // ignore
          }
        }
        // 종속 hook들(useMapStyleSettings 등)이 저장된 설정을 적용하도록 트리거
        setMapSyncEpoch((prev) => prev + 1);
      });
    })();

    return () => {
      cancelled = true;
      controller.abort();
      if (viewSaveTimer) clearInterval(viewSaveTimer);

      // 최종 뷰 상태 저장 (mercator일 때만 — globe 위치는 영속화하지 않음)
      const cb = onViewStateChangeRef.current;
      if (cb && map && projectionRef.current === 'mercator') {
        const c = map.getCenter();
        cb({ center: [c.lng, c.lat], zoom: map.getZoom(), bearing: map.getBearing(), pitch: map.getPitch() });
      }

      try {
        globeDeckLayerRef.current?.requestFinalize();
      } catch {
        // ignore
      }

      if (map) {
        map.remove();
        map = null;
      }
      if (overlayRef.current) {
        overlayRef.current.finalize();
        overlayRef.current = null;
      }
      if (overlayInteractionRef.current) {
        overlayInteractionRef.current.finalize();
        overlayInteractionRef.current = null;
      }
      globeDeckLayerRef.current = null;
      mapRef.current = null;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return { ensureMercatorOverlay, pulseMapSync };
}
