// file: src/map/Markers.tsx
import 'leaflet.markercluster';
import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
import 'leaflet.markercluster/dist/MarkerCluster.css';
import { useMap } from 'react-leaflet';
import L, { Marker as LMarker } from 'leaflet';
import React, { useEffect, useMemo, useRef } from 'react';
import { pinIcon } from './pinIcon';

const iconDataCache = new Map<string, string>();

async function fetchIconDataUri(type: string): Promise<string> {
  if (!type) return '';
  if (iconDataCache.has(type)) return iconDataCache.get(type)!;
  const iconName = `${type.toUpperCase()}24.png`;
  try {
    const response = await fetch(`/api/icon/${iconName}`);
    if (response.ok) {
      const { iconBase64, mimeType } = await response.json();
      const dataUri = `data:${mimeType};base64,${iconBase64}`;
      iconDataCache.set(type, dataUri);
      return dataUri;
    } else {
      iconDataCache.set(type, '');
      return '';
    }
  } catch {
    iconDataCache.set(type, '');
    return '';
  }
}

interface Dya {
  id: string | number;
  latitude: number;
  longitude: number;
  dya_text: string;
  agb_dyncol: string | null;
  html_info: string | null;
  type: string;
}

interface MarkersProps {
  dyas: Dya[];
  onMarkerClick: (dya: Dya) => void;
}

function iconSizeForZoom(zoom: number): number {
  const size = 18 + (zoom - 6) * 3.2;
  return Math.max(20, Math.min(58, Math.round(size)));
}

function clusterBubbleSize(count: number, zoom: number): number {
  const base = count < 10 ? 30 : count < 50 ? 36 : count < 100 ? 42 : 50;
  const zoomBump = 1 + Math.max(0, zoom - 10) * 0.06;
  return Math.round(Math.min(base * zoomBump, 68));
}

const Markers: React.FC<MarkersProps> = ({ dyas, onMarkerClick }) => {
  const map = useMap();
  const markersRef = useRef<LMarker[]>([]);
  const hasFittedBounds = useRef(false);

  const clusterGroup = useMemo(() => {
    const iconCreateFunction = (cluster: any) => {
      const count: number = cluster.getChildCount();
      const z = cluster._group?._map?.getZoom?.() ?? 10;
      const size = clusterBubbleSize(count, z);
      return L.divIcon({
        html: `<div style="border-radius:9999px;background:rgba(0,122,255,0.15);border:2px solid rgba(0,122,255,0.35);display:grid;place-items:center;width:${size}px;height:${size}px;font-weight:700;line-height:1;"><span style="background:#fff;border-radius:9999px;padding:2px 6px;font-size:12px;border:1px solid rgba(0,0,0,0.1);">${count}</span></div>`,
        className: 'tasko-cluster',
        iconSize: L.point(size, size, true),
      });
    };
    return L.markerClusterGroup({
      maxClusterRadius: 40,
      chunkedLoading: true,
      spiderfyOnMaxZoom: true,
      showCoverageOnHover: false,
      zoomToBoundsOnClick: true,
      iconCreateFunction,
    });
  }, []);

  useEffect(() => {
    map.addLayer(clusterGroup);
    return () => {
      map.removeLayer(clusterGroup);
      clusterGroup.clearLayers();
    };
  }, [map, clusterGroup]);

  useEffect(() => {
    clusterGroup.clearLayers();
    markersRef.current = [];
    if (dyas.length === 0) return;

    const z = map.getZoom();
    const size = iconSizeForZoom(z);

    const createMarkers = async () => {
      const uniqueIconTypes = [
        ...new Set(dyas.map((d) => d.type).filter(Boolean)),
      ];
      await Promise.all(uniqueIconTypes.map(fetchIconDataUri));
      const markers: LMarker[] = dyas.map((d) => {
        const glyphDataUri = iconDataCache.get(d.type) || '';
        const marker = L.marker([d.latitude, d.longitude], {
          icon: pinIcon(d.agb_dyncol, size, glyphDataUri),
          title: String(d.id),
          riseOnHover: true,
        }) as LMarker & {
          __taskoColor?: string | null;
          __taskoGlyphDataUri?: string;
        };
        (marker as any).__taskoColor = d.agb_dyncol ?? null;
        (marker as any).__taskoGlyphDataUri = glyphDataUri;
        marker.on('click', () => onMarkerClick(d));
        markersRef.current.push(marker);
        return marker;
      });
      clusterGroup.addLayers(markers);
      if (!hasFittedBounds.current && dyas.length > 0) {
        const bounds = clusterGroup.getBounds();
        if (bounds.isValid()) {
          map.fitBounds(bounds, { padding: [24, 24] });
          hasFittedBounds.current = true;
        }
      }
    };

    createMarkers();
  }, [dyas, clusterGroup, map]);

  useEffect(() => {
    const handleZoom = () => {
      const z = map.getZoom();
      const newSize = iconSizeForZoom(z);
      markersRef.current.forEach((m: any) => {
        if (typeof m.getChildCount === 'function') return;
        const color = m.__taskoColor ?? null;
        const glyphDataUri = m.__taskoGlyphDataUri ?? undefined;
        m.setIcon(pinIcon(color, newSize, glyphDataUri));
      });
      clusterGroup.refreshClusters();
    };
    map.on('zoomend', handleZoom);
    return () => {
      map.off('zoomend', handleZoom);
    };
  }, [map, clusterGroup]);

  return null;
};

export default Markers;
