import { useEffect, useRef, type MutableRefObject } from 'react';
import type maplibregl from 'maplibre-gl';
import type { BaseMapId, MapProjectionId } from '../types';
import { kickRepaint, onMapStyleReady, getLayerId } from '../lib/mapCore';
import { ensureSeamarkOverlay } from '../layers/seamark';
import { applyBathymetryZoomProfile, resolveMapStyle } from '../layers/bathymetry';

export function useBaseMapToggle(
  mapRef: MutableRefObject<maplibregl.Map | null>,
  opts: {
    baseMap: BaseMapId;
    projection: MapProjectionId;
    showSeamark: boolean;
    mapSyncEpoch: number;
    pulseMapSync: () => void;
  },
) {
  const { baseMap, projection, showSeamark, mapSyncEpoch, pulseMapSync } = opts;

  const showSeamarkRef = useRef(showSeamark);
  const bathyZoomProfileKeyRef = useRef<string>('');
  const initialLoadRef = useRef(true);

  useEffect(() => {
    showSeamarkRef.current = showSeamark;
  }, [showSeamark]);

  // Base map style toggle — skip first run (useMapInit handles initial style)
  useEffect(() => {
    if (initialLoadRef.current) {
      initialLoadRef.current = false;
      return;
    }
    const map = mapRef.current;
    if (!map) return;

    let cancelled = false;
    const controller = new AbortController();

    (async () => {
      try {
        const style = await resolveMapStyle(baseMap, controller.signal);
        if (cancelled) return;
        map.setStyle(style, { diff: false });

        map.once('style.load', () => {
          if (cancelled) return;
          kickRepaint(map);
          requestAnimationFrame(() => kickRepaint(map));
          pulseMapSync();
        });
      } catch (e) {
        if (cancelled) return;
        console.warn('Base map switch failed:', e);
      }
    })();

    return () => {
      cancelled = true;
      controller.abort();
    };
  }, [baseMap]);

  // Bathymetry zoom profile + water layer visibility
  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;

    const apply = () => {
      if (!map.isStyleLoaded()) return;
      if (baseMap === 'enc') return;
      const seaVisibility = 'visible' as const;
      const seaRegex = /(water|sea|ocean|river|lake|coast|bay)/i;

      const nextProfileKey = `bathyZoomV1|${baseMap}|${projection}`;
      if (bathyZoomProfileKeyRef.current !== nextProfileKey) {
        applyBathymetryZoomProfile(map, baseMap, projection);
        bathyZoomProfileKeyRef.current = nextProfileKey;
        kickRepaint(map);
      }

      try {
        const style = map.getStyle();
        const styleLayers = style && Array.isArray(style.layers) ? style.layers : [];
        for (const layer of styleLayers) {
          const id = getLayerId(layer);
          if (!id) continue;
          const sourceLayer = String((layer as Record<string, unknown>)['source-layer'] ?? '').toLowerCase();
          const source = String((layer as { source?: unknown }).source ?? '').toLowerCase();
          const type = String((layer as { type?: unknown }).type ?? '').toLowerCase();
          const isSea = seaRegex.test(id) || seaRegex.test(sourceLayer) || seaRegex.test(source);
          const isRaster = type === 'raster';
          if (!isSea) continue;
          if (!map.getLayer(id)) continue;
          if (isRaster && id === 'seamark') continue;
          try {
            map.setLayoutProperty(id, 'visibility', seaVisibility);
          } catch {
            // ignore
          }
        }
      } catch {
        // ignore
      }
    };

    const stop = onMapStyleReady(map, apply);
    return () => {
      stop();
    };
  }, [projection, baseMap, mapSyncEpoch]);

  // Seamark toggle
  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;
    if (showSeamark) {
      try {
        ensureSeamarkOverlay(map, 'bathymetry-lines-coarse');
        map.setPaintProperty('seamark', 'raster-opacity', 0.85);
      } catch {
        // ignore until style is ready
      }
      return;
    }

    try {
      if (map.getLayer('seamark')) map.removeLayer('seamark');
    } catch {
      // ignore
    }
    try {
      if (map.getSource('seamark')) map.removeSource('seamark');
    } catch {
      // ignore
    }
  }, [showSeamark]);
}
