import { useEffect, useRef, useMemo, useState } from 'react';
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';

export interface MapMarker {
  id: number;
  role: 'supplier' | 'authority';
  cui: string;
  name: string;
  lat: number;
  lng: number;
}

interface Props {
  markers: MapMarker[];
  height?: number;
  hoveredId?: number | null;
}

const ROMANIA_CENTER: [number, number] = [25.0, 45.9];

function escapeHtml(s: string): string {
  return s.replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]!));
}

export default function SearchMap({ markers, height = 520, hoveredId: propHoveredId = null }: Props) {
  const mapContainer = useRef<HTMLDivElement>(null);
  const mapRef = useRef<maplibregl.Map | null>(null);
  const popupRef = useRef<maplibregl.Popup | null>(null);

  // Internal hover state subscribed to window CustomEvent('cauta:hover').
  // Allows result-row hover in parent Astro page to trigger marker highlight
  // without prop-drilling state across Astro/React boundary.
  const [eventHoveredId, setEventHoveredId] = useState<number | null>(null);
  useEffect(() => {
    const handler = (e: Event) => {
      const ce = e as CustomEvent<{ id: number | null }>;
      setEventHoveredId(ce.detail?.id ?? null);
    };
    window.addEventListener('cauta:hover', handler);
    return () => window.removeEventListener('cauta:hover', handler);
  }, []);
  const hoveredId = propHoveredId ?? eventHoveredId;

  const geojson = useMemo(() => {
    return {
      type: 'FeatureCollection' as const,
      features: markers.map((m, idx) => ({
        type: 'Feature' as const,
        id: idx,
        properties: {
          id: m.id,
          role: m.role,
          cui: m.cui,
          name: m.name,
        },
        geometry: { type: 'Point' as const, coordinates: [m.lng, m.lat] },
      })),
    };
  }, [markers]);

  // Initialise the map exactly once
  useEffect(() => {
    if (!mapContainer.current) return;
    const map = new maplibregl.Map({
      container: mapContainer.current,
      style: {
        version: 8,
        sources: {
          osm: {
            type: 'raster',
            tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
            tileSize: 256,
            attribution: '&copy; OpenStreetMap',
            maxzoom: 19,
          },
        },
        layers: [{
          id: 'osm-tiles',
          type: 'raster',
          source: 'osm',
          paint: {
            'raster-saturation': -0.5,
            'raster-brightness-max': 0.95,
            'raster-opacity': 0.85,
          },
        }],
      },
      center: ROMANIA_CENTER,
      zoom: 5.8,
      maxBounds: [[18, 42], [32, 50]],
    });
    map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');
    requestAnimationFrame(() => map.resize());
    setTimeout(() => map.resize(), 250);

    map.on('load', () => {
      map.addSource('points', {
        type: 'geojson',
        data: geojson,
        cluster: true,
        clusterRadius: 40,
        clusterMaxZoom: 11,
      });

      // Cluster bubbles
      map.addLayer({
        id: 'clusters',
        type: 'circle',
        source: 'points',
        filter: ['has', 'point_count'],
        paint: {
          'circle-color': '#0F172A',
          'circle-opacity': 0.85,
          'circle-stroke-color': '#FAF8F2',
          'circle-stroke-width': 2,
          'circle-radius': [
            'step', ['get', 'point_count'],
            14, 10,
            18, 50,
            24, 200,
            30,
          ],
        },
      });

      map.addLayer({
        id: 'cluster-count',
        type: 'symbol',
        source: 'points',
        filter: ['has', 'point_count'],
        layout: {
          'text-field': '{point_count_abbreviated}',
          'text-size': 11,
          'text-font': ['Open Sans Bold', 'Arial Unicode MS Bold'],
        },
        paint: { 'text-color': '#FAF8F2' },
      });

      // Unclustered points: red for supplier, blue for authority
      map.addLayer({
        id: 'unclustered',
        type: 'circle',
        source: 'points',
        filter: ['!', ['has', 'point_count']],
        paint: {
          'circle-color': [
            'match', ['get', 'role'],
            'supplier', '#DC2626',
            'authority', '#1F58A6',
            '#525252',
          ],
          'circle-radius': [
            'case',
            ['boolean', ['feature-state', 'hover'], false], 11,
            5,
          ],
          'circle-stroke-color': [
            'case',
            ['boolean', ['feature-state', 'hover'], false], '#1F2A37',
            '#FAF8F2',
          ],
          'circle-stroke-width': [
            'case',
            ['boolean', ['feature-state', 'hover'], false], 2.5,
            1.5,
          ],
          'circle-opacity': [
            'case',
            ['boolean', ['feature-state', 'hover'], false], 1.0,
            0.9,
          ],
        },
      });

      // Hover popup on point
      map.on('mouseenter', 'unclustered', (e) => {
        if (!e.features?.length) return;
        map.getCanvas().style.cursor = 'pointer';
        const f = e.features[0];
        const p = f.properties as any;
        const coords = (f.geometry as any).coordinates.slice();
        const html = `
          <div style="font-family:ui-monospace,monospace;font-size:11px;line-height:1.4;padding:2px 4px;">
            <div style="text-transform:uppercase;letter-spacing:.04em;font-size:9px;color:${p.role === 'supplier' ? '#DC2626' : '#1F58A6'};">
              ${p.role === 'supplier' ? 'FURNIZOR' : 'AUTORITATE'} · CUI ${escapeHtml(p.cui)}
            </div>
            <div style="font-weight:600;color:#0F172A;margin-top:2px;max-width:240px;">${escapeHtml(p.name || '')}</div>
            <div style="color:#525252;margin-top:3px;">click → /anunt/${p.id}</div>
          </div>
        `;
        if (!popupRef.current) {
          popupRef.current = new maplibregl.Popup({
            closeButton: false,
            closeOnClick: false,
            offset: 8,
            maxWidth: '300px',
          });
        }
        popupRef.current.setLngLat(coords).setHTML(html).addTo(map);
      });

      map.on('mouseleave', 'unclustered', () => {
        map.getCanvas().style.cursor = '';
        popupRef.current?.remove();
      });

      // Click point → open announcement
      map.on('click', 'unclustered', (e) => {
        if (!e.features?.length) return;
        const p = e.features[0].properties as any;
        const role = p.role === 'supplier' ? 'firma' : 'autoritate';
        // Prefer profile page; open in new tab
        window.open(`/${role}/${p.cui}`, '_blank', 'noopener');
      });

      // Click cluster → zoom in
      map.on('click', 'clusters', (e) => {
        const f = map.queryRenderedFeatures(e.point, { layers: ['clusters'] });
        if (!f.length) return;
        const clusterId = f[0].properties?.cluster_id;
        const src = map.getSource('points') as any;
        src.getClusterExpansionZoom(clusterId, (err: any, zoom: number) => {
          if (err) return;
          map.easeTo({ center: (f[0].geometry as any).coordinates, zoom });
        });
      });
      map.on('mouseenter', 'clusters', () => { map.getCanvas().style.cursor = 'pointer'; });
      map.on('mouseleave', 'clusters', () => { map.getCanvas().style.cursor = ''; });

      // Fit to data on first paint
      if (markers.length > 0) {
        const bounds = new maplibregl.LngLatBounds();
        for (const m of markers) bounds.extend([m.lng, m.lat]);
        try { map.fitBounds(bounds, { padding: 40, maxZoom: 12, duration: 600 }); } catch { /* noop */ }
      }
    });

    mapRef.current = map;
    return () => {
      map.remove();
      mapRef.current = null;
      popupRef.current = null;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Update data when markers change
  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;
    const apply = () => {
      const src = map.getSource('points') as any;
      if (!src) return;
      src.setData(geojson);
      if (markers.length > 0) {
        const bounds = new maplibregl.LngLatBounds();
        for (const m of markers) bounds.extend([m.lng, m.lat]);
        try { map.fitBounds(bounds, { padding: 40, maxZoom: 12, duration: 600 }); } catch { /* noop */ }
      }
    };
    if (map.isStyleLoaded()) apply();
    else map.once('load', apply);
  }, [geojson, markers]);

  // Hover highlight from list
  useEffect(() => {
    const map = mapRef.current;
    if (!map?.isStyleLoaded()) return;
    // Clear old hover states
    for (let i = 0; i < markers.length; i++) {
      try { map.setFeatureState({ source: 'points', id: i }, { hover: false }); } catch { /* */ }
    }
    if (hoveredId == null) return;
    markers.forEach((m, idx) => {
      if (m.id === hoveredId) {
        try { map.setFeatureState({ source: 'points', id: idx }, { hover: true }); } catch { /* */ }
      }
    });
  }, [hoveredId, markers]);

  return (
    <div style={{ position: 'relative', width: '100%', height }}>
      <div ref={mapContainer} style={{ position: 'absolute', inset: 0 }} />
      <div style={{
        position: 'absolute', bottom: 8, left: 8, zIndex: 10,
        background: 'rgba(250,248,242,0.95)',
        border: '1px solid #0F172A',
        padding: '6px 8px',
        fontFamily: 'ui-monospace, monospace',
        fontSize: 10,
        textTransform: 'uppercase',
        letterSpacing: '0.04em',
        lineHeight: 1.6,
      }}>
        <div><span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: 5, background: '#DC2626', marginRight: 6, verticalAlign: 'middle' }} />furnizor</div>
        <div><span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: 5, background: '#1F58A6', marginRight: 6, verticalAlign: 'middle' }} />autoritate</div>
      </div>
    </div>
  );
}
