import maplibregl from 'maplibre-gl';
import type { MapProjectionId } from '../types';

export function kickRepaint(map: maplibregl.Map | null) {
  if (!map) return;
  try {
    if (map.isStyleLoaded()) map.triggerRepaint();
  } catch {
    // ignore
  }
  try {
    requestAnimationFrame(() => {
      try {
        if (map.isStyleLoaded()) map.triggerRepaint();
      } catch {
        // ignore
      }
    });
    requestAnimationFrame(() => {
      try {
        if (map.isStyleLoaded()) map.triggerRepaint();
      } catch {
        // ignore
      }
    });
  } catch {
    // ignore (e.g., non-browser env)
  }
}

export function onMapStyleReady(map: maplibregl.Map | null, callback: () => void) {
  if (!map) {
    return () => {
      // noop
    };
  }
  if (map.isStyleLoaded()) {
    callback();
    return () => {
      // noop
    };
  }

  let fired = false;
  const runOnce = () => {
    if (!map || fired || !map.isStyleLoaded()) return;
    fired = true;
    callback();
    try {
      map.off('style.load', runOnce);
      map.off('styledata', runOnce);
    } catch {
      // ignore
    }
  };

  map.on('style.load', runOnce);
  map.on('styledata', runOnce);

  return () => {
    if (fired) return;
    fired = true;
    try {
      if (!map) return;
      map.off('style.load', runOnce);
      map.off('styledata', runOnce);
    } catch {
      // ignore
    }
  };
}

export function extractProjectionType(map: maplibregl.Map): MapProjectionId | undefined {
  const projection = map.getProjection?.();
  if (!projection || typeof projection !== 'object') return undefined;

  const rawType = (projection as { type?: unknown; name?: unknown }).type ?? (projection as { type?: unknown; name?: unknown }).name;
  if (rawType === 'globe') return 'globe';
  if (rawType === 'mercator') return 'mercator';
  return undefined;
}

// Canonical source: shared/lib/map/mapTilerKey.ts (re-exported for local usage)
export { getMapTilerKey } from '../../../shared/lib/map/mapTilerKey';

export function getLayerId(value: unknown): string | null {
  if (!value || typeof value !== 'object') return null;
  const candidate = (value as { id?: unknown }).id;
  return typeof candidate === 'string' ? candidate : null;
}

export function sanitizeDeckLayerList(value: unknown): unknown[] {
  if (!Array.isArray(value)) return [];
  const seen = new Set<string>();
  const out: unknown[] = [];
  let dropped = 0;

  for (const layer of value) {
    // Deck layer instances expose `id`, `props`, and `clone`.
    // Filter out MapLibre native layer specs that only share an `id`.
    const isDeckLayerLike =
      !!layer &&
      typeof layer === 'object' &&
      typeof (layer as { id?: unknown }).id === 'string' &&
      typeof (layer as { clone?: unknown }).clone === 'function' &&
      typeof (layer as { props?: unknown }).props === 'object';
    if (!isDeckLayerLike) {
      dropped += 1;
      continue;
    }

    const layerId = getLayerId(layer);
    if (!layerId) {
      dropped += 1;
      continue;
    }
    if (seen.has(layerId)) {
      dropped += 1;
      continue;
    }
    seen.add(layerId);
    out.push(layer);
  }

  if (dropped > 0 && import.meta.env.DEV) {
    console.warn(`Sanitized deck layer list: dropped ${dropped} invalid/duplicate entries`);
  }

  return out;
}
