import { useEffect, useRef, type MutableRefObject } from 'react';
import maplibregl from 'maplibre-gl';
import type { MapStyleSettings, MapLabelLanguage, DepthColorStop, DepthFontSize } from '../../../features/mapSettings/types';
import type { BaseMapId } from '../types';
import { kickRepaint, onMapStyleReady } from '../lib/mapCore';

/* ── Depth font size presets ──────────────────────────────────────── */
const DEPTH_FONT_SIZE_MAP: Record<DepthFontSize, unknown[]> = {
  small: ['interpolate', ['linear'], ['zoom'], 7, 8, 9, 9, 11, 11, 13, 13],
  medium: ['interpolate', ['linear'], ['zoom'], 7, 10, 9, 12, 11, 14, 13, 16],
  large: ['interpolate', ['linear'], ['zoom'], 7, 12, 9, 15, 11, 18, 13, 20],
};

/* ── Helpers ──────────────────────────────────────────────────────── */
function darkenHex(hex: string, factor = 0.85): string {
  const r = parseInt(hex.slice(1, 3), 16);
  const g = parseInt(hex.slice(3, 5), 16);
  const b = parseInt(hex.slice(5, 7), 16);
  return `#${[r, g, b].map((c) => Math.round(c * factor).toString(16).padStart(2, '0')).join('')}`;
}

function lightenHex(hex: string, factor = 1.3): string {
  const r = parseInt(hex.slice(1, 3), 16);
  const g = parseInt(hex.slice(3, 5), 16);
  const b = parseInt(hex.slice(5, 7), 16);
  return `#${[r, g, b].map((c) => Math.min(255, Math.round(c * factor)).toString(16).padStart(2, '0')).join('')}`;
}

/* ── Apply functions ──────────────────────────────────────────────── */

function applyLabelLanguage(map: maplibregl.Map, lang: MapLabelLanguage) {
  const style = map.getStyle();
  if (!style?.layers) return;
  for (const layer of style.layers) {
    if (layer.type !== 'symbol') continue;
    const layout = (layer as { layout?: Record<string, unknown> }).layout;
    if (!layout?.['text-field']) continue;
    if (layer.id.startsWith('bathymetry-labels')) continue;
    const textField =
      lang === 'local'
        ? ['get', 'name']
        : ['coalesce', ['get', `name:${lang}`], ['get', 'name']];
    try {
      map.setLayoutProperty(layer.id, 'text-field', textField);
    } catch {
      // ignore
    }
  }
}

function applyLandColor(map: maplibregl.Map, color: string) {
  const style = map.getStyle();
  if (!style?.layers) return;
  const waterRegex = /(water|sea|ocean|river|lake|coast|bay)/i;
  const darkVariant = darkenHex(color, 0.8);
  for (const layer of style.layers) {
    const id = layer.id;
    if (id.startsWith('bathymetry-')) continue;
    if (id.startsWith('subcables-')) continue;
    if (id.startsWith('zones-')) continue;
    if (id.startsWith('ships-')) continue;
    if (id.startsWith('pair-')) continue;
    if (id.startsWith('fc-')) continue;
    if (id.startsWith('fleet-')) continue;
    if (id.startsWith('predict-')) continue;
    if (id.startsWith('vessel-track-')) continue;
    if (id === 'deck-globe') continue;
    const sourceLayer = String((layer as Record<string, unknown>)['source-layer'] ?? '');
    const isWater = waterRegex.test(id) || waterRegex.test(sourceLayer);
    if (isWater) continue;
    try {
      if (layer.type === 'background') {
        map.setPaintProperty(id, 'background-color', color);
      } else if (layer.type === 'fill') {
        map.setPaintProperty(id, 'fill-color', darkVariant);
      }
    } catch {
      // ignore
    }
  }
}

function applyWaterBaseColor(map: maplibregl.Map, fillColor: string) {
  const style = map.getStyle();
  if (!style?.layers) return;
  const waterRegex = /(water|sea|ocean|river|lake|coast|bay)/i;
  const lineColor = darkenHex(fillColor, 0.85);
  for (const layer of style.layers) {
    const id = layer.id;
    if (id.startsWith('bathymetry-')) continue;
    const sourceLayer = String((layer as Record<string, unknown>)['source-layer'] ?? '');
    if (!waterRegex.test(id) && !waterRegex.test(sourceLayer)) continue;
    try {
      if (layer.type === 'fill') {
        map.setPaintProperty(id, 'fill-color', fillColor);
      } else if (layer.type === 'line') {
        map.setPaintProperty(id, 'line-color', lineColor);
      }
    } catch {
      // ignore
    }
  }
}

function applyDepthGradient(map: maplibregl.Map, stops: DepthColorStop[]) {
  const depth = ['to-number', ['get', 'depth']];
  const sorted = [...stops].sort((a, b) => a.depth - b.depth);
  if (sorted.length === 0) return;
  const expr: unknown[] = ['interpolate', ['linear'], depth];
  for (const s of sorted) {
    expr.push(s.depth, s.color);
  }
  // 0m까지 확장 (최천층 stop이 0보다 깊으면)
  const shallowest = sorted[sorted.length - 1];
  if (shallowest.depth < 0) {
    expr.push(0, lightenHex(shallowest.color, 1.8));
  }
  if (!map.getLayer('bathymetry-fill')) return;
  try {
    map.setPaintProperty('bathymetry-fill', 'fill-color', expr as never);
  } catch {
    // ignore
  }
}

function applyDepthFontSize(map: maplibregl.Map, size: DepthFontSize) {
  const expr = DEPTH_FONT_SIZE_MAP[size];
  for (const layerId of ['bathymetry-labels', 'bathymetry-labels-coarse', 'bathymetry-landforms']) {
    if (!map.getLayer(layerId)) continue;
    try {
      map.setLayoutProperty(layerId, 'text-size', expr);
    } catch {
      // ignore
    }
  }
}

function applyDepthFontColor(map: maplibregl.Map, color: string) {
  for (const layerId of ['bathymetry-labels', 'bathymetry-labels-coarse', 'bathymetry-landforms']) {
    if (!map.getLayer(layerId)) continue;
    try {
      map.setPaintProperty(layerId, 'text-color', color);
    } catch {
      // ignore
    }
  }
}

/* ── Hook ──────────────────────────────────────────────────────────── */
export function useMapStyleSettings(
  mapRef: MutableRefObject<maplibregl.Map | null>,
  settings: MapStyleSettings | undefined,
  opts: { baseMap: BaseMapId; mapSyncEpoch: number },
) {
  const settingsRef = useRef(settings);
  useEffect(() => {
    settingsRef.current = settings;
  });

  const { baseMap, mapSyncEpoch } = opts;

  useEffect(() => {
    const map = mapRef.current;
    const s = settingsRef.current;
    if (!map || !s) return;
    // Ocean/ENC 모드는 전용 훅에서 별도 처리
    if (baseMap === 'ocean' || baseMap === 'enc') return;

    const stop = onMapStyleReady(map, () => {
      applyLabelLanguage(map, s.labelLanguage);
      applyLandColor(map, s.landColor);
      applyWaterBaseColor(map, s.waterBaseColor);
      if (baseMap === 'enhanced') {
        applyDepthGradient(map, s.depthStops);
        applyDepthFontSize(map, s.depthFontSize);
        applyDepthFontColor(map, s.depthFontColor);
      }
      kickRepaint(map);
    });

    return () => stop();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [settings, baseMap, mapSyncEpoch]);
}
