// MapView — MapLibre GL met PDOK BRT-Achtergrondkaart (grijs).
// - Native GeoJSON-clustering voor pin-punten (uit /v1/vergunningen/pins)
// - Custom NL-overheid-pin (cirkel met punt-onder) per type_besluit-kleur
// - Sync met externe state: pan/zoom-events → onViewport, klik → onSelect
// - Truncatie-banner als de API meer dan `cap` matches heeft
//
// Sinds de API-omzetting krijgt deze component lichtgewicht pins
// ({ id, tb, lon, lat }) ipv volledige records. Polygonen worden niet meer
// op de hoofdkaart getoond — die staan alleen in DetailPanel's mini-kaart.

const { useEffect, useRef, useMemo } = React;

// Lookup type_besluit → kleur (uit waardelijsten, niet in pins-response).
const TB_COLOR = WAARDELIJSTEN.type_besluit.reduce((m, t) => {
  m[t.code] = t.color;
  return m;
}, {});

function MapView({ pins, truncated, totalMatching, cap, selectedId, selectedRec, onSelect, onViewportChange, initialViewport, fitTrigger }) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const readyRef = useRef(false);
  // selectedId via ref voor de click-handler binnen map.on('load'), zodat
  // we de handler maar één keer hoeven te registreren.
  const onSelectRef = useRef(onSelect);
  useEffect(() => { onSelectRef.current = onSelect; }, [onSelect]);

  function buildGeoJSON(pinList) {
    const features = new Array(pinList.length);
    for (let i = 0; i < pinList.length; i++) {
      const p = pinList[i];
      features[i] = {
        type: 'Feature',
        properties: {
          id: p.id,
          color: TB_COLOR[p.tb] || '#6B7280',
          tb: p.tb,
          selected: p.id === selectedId ? 1 : 0,
        },
        geometry: { type: 'Point', coordinates: [p.lon, p.lat] },
      };
    }
    return { type: 'FeatureCollection', features };
  }

  // --- init map (once) -----------------------------------------------------
  useEffect(() => {
    if (mapRef.current) return;
    const style = {
      version: 8,
      glyphs: 'https://fonts.openmaptiles.org/{fontstack}/{range}.pbf',
      sources: {
        'pdok-brt-grijs': {
          type: 'raster',
          tiles: [
            'https://service.pdok.nl/brt/achtergrondkaart/wmts/v2_0/grijs/EPSG:3857/{z}/{x}/{y}.png',
          ],
          tileSize: 256,
          attribution: '© <a href="https://www.kadaster.nl/" target="_blank" rel="noopener">Kadaster</a> – PDOK BRT-Achtergrondkaart',
          minzoom: 0,
          maxzoom: 19,
        },
      },
      layers: [
        { id: 'bg', type: 'background', paint: { 'background-color': '#F2EFE8' } },
        { id: 'pdok', type: 'raster', source: 'pdok-brt-grijs' },
      ],
    };

    const v = initialViewport || { lat: 52.15, lon: 5.3, zoom: 7.2 };
    const map = new maplibregl.Map({
      container: containerRef.current,
      style,
      center: [v.lon, v.lat],
      zoom: v.zoom,
      minZoom: 6,
      maxZoom: 18,
      attributionControl: false,
    });
    map.addControl(new maplibregl.AttributionControl({ compact: true }), 'bottom-right');
    map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');

    map.on('load', () => {
      const empty = { type: 'FeatureCollection', features: [] };

      // Points source with clustering
      map.addSource('pts', {
        type: 'geojson',
        data: empty,
        cluster: true,
        clusterRadius: 48,
        clusterMaxZoom: 13,
      });

      // Cluster bubbles
      map.addLayer({
        id: 'clusters',
        type: 'circle',
        source: 'pts',
        filter: ['has', 'point_count'],
        paint: {
          'circle-color': '#FFFFFF',
          'circle-stroke-color': '#0F4A37',
          'circle-stroke-width': 2,
          'circle-radius': [
            'step', ['get', 'point_count'],
            16, 5, 20, 15, 24, 50, 30,
          ],
        },
      });
      map.addLayer({
        id: 'cluster-count',
        type: 'symbol',
        source: 'pts',
        filter: ['has', 'point_count'],
        layout: {
          'text-field': ['get', 'point_count_abbreviated'],
          'text-font': ['Noto Sans Regular'],
          'text-size': 12,
        },
        paint: {
          'text-color': '#0F4A37',
        },
      });

      // Stem: small circle offset 6px below — looks like an NL-overheid-pin
      map.addLayer({
        id: 'pin-stem',
        type: 'circle',
        source: 'pts',
        filter: ['!', ['has', 'point_count']],
        paint: {
          'circle-color': ['get', 'color'],
          'circle-radius': 3,
          'circle-translate': [0, 8],
          'circle-stroke-width': 1,
          'circle-stroke-color': '#FFFFFF',
        },
      });
      map.addLayer({
        id: 'pin',
        type: 'circle',
        source: 'pts',
        filter: ['!', ['has', 'point_count']],
        paint: {
          'circle-color': ['get', 'color'],
          'circle-radius': 8,
          'circle-stroke-color': '#FFFFFF',
          'circle-stroke-width': 2,
        },
      });

      // Selectie-marker: aparte source/layers, altijd zichtbaar bovenop alles
      // (ook bovenop clusters). Werkt onafhankelijk van de pins-source, dus
      // ook als de geselecteerde vergunning buiten de huidige filters of
      // buiten de 10k-cap valt.
      map.addSource('selection', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
      map.addLayer({
        id: 'sel-halo',
        type: 'circle',
        source: 'selection',
        paint: {
          'circle-color': '#FFFFFF',
          'circle-radius': 16,
          'circle-stroke-color': '#0F4A37',
          'circle-stroke-width': 2.5,
          'circle-opacity': 0.9,
        },
      });
      map.addLayer({
        id: 'sel-dot',
        type: 'circle',
        source: 'selection',
        paint: {
          'circle-color': ['get', 'color'],
          'circle-radius': 7,
          'circle-stroke-color': '#FFFFFF',
          'circle-stroke-width': 2,
        },
      });

      readyRef.current = true;
      const ev = new CustomEvent('mapready');
      containerRef.current.dispatchEvent(ev);

      // Click handlers
      map.on('click', 'pin', (e) => {
        const f = e.features[0];
        onSelectRef.current(f.properties.id);
      });
      map.on('click', 'clusters', (e) => {
        const f = e.features[0];
        const clusterId = f.properties.cluster_id;
        const src = map.getSource('pts');
        src.getClusterExpansionZoom(clusterId, (err, zoom) => {
          if (err) return;
          map.easeTo({ center: f.geometry.coordinates, zoom: zoom + 0.2, duration: 500 });
        });
      });
      map.on('mouseenter', 'pin', () => map.getCanvas().style.cursor = 'pointer');
      map.on('mouseleave', 'pin', () => map.getCanvas().style.cursor = '');
      map.on('mouseenter', 'clusters', () => map.getCanvas().style.cursor = 'pointer');
      map.on('mouseleave', 'clusters', () => map.getCanvas().style.cursor = '');

      // Viewport-change emit
      const emit = () => {
        const c = map.getCenter();
        onViewportChange({ lat: c.lat, lon: c.lng, zoom: map.getZoom(), bounds: map.getBounds() });
      };
      map.on('moveend', emit);
      map.on('zoomend', emit);
      setTimeout(emit, 50);
      // Force a canvas resize after first layout settles — MapLibre's
      // internal trackResize occasionally misses the post-mount reflow,
      // which leaves the GL canvas smaller than the container.
      map.resize();
      setTimeout(() => map.resize(), 0);
    });

    mapRef.current = map;

    // Belt-and-suspenders ResizeObserver
    const ro = new ResizeObserver(() => {
      if (mapRef.current) mapRef.current.resize();
    });
    ro.observe(containerRef.current);

    return () => { ro.disconnect(); map.remove(); mapRef.current = null; };
  // eslint-disable-next-line
  }, []);

  // --- update source when pins or selection change ------------------------
  useEffect(() => {
    const map = mapRef.current;
    const apply = () => {
      const src = map.getSource('pts');
      if (!src) return;
      src.setData(buildGeoJSON(pins));
    };
    if (!map || !readyRef.current) {
      if (containerRef.current) {
        containerRef.current.addEventListener('mapready', apply, { once: true });
      }
      return;
    }
    apply();
  }, [pins, selectedId]);

  // --- selection-marker source update ------------------------------------
  // Prefer selectedRec (authoritative coords uit /vergunningen/{id}) zodat
  // de marker ook werkt bij vergunningen die buiten de huidige filters of
  // buiten de 10k pins-cap vallen. Fallback op pins-lookup voor instant
  // feedback terwijl de detail-fetch nog loopt.
  useEffect(() => {
    const map = mapRef.current;
    const apply = () => {
      const src = map.getSource('selection');
      if (!src) return;
      if (!selectedId) {
        src.setData({ type: 'FeatureCollection', features: [] });
        return;
      }
      let lat, lon, tb;
      if (selectedRec && typeof selectedRec.lat === 'number') {
        lat = selectedRec.lat; lon = selectedRec.lon; tb = selectedRec.type_besluit;
      } else {
        const pin = pins.find((p) => p.id === selectedId);
        if (pin) { lat = pin.lat; lon = pin.lon; tb = pin.tb; }
      }
      if (lat == null || lon == null) {
        src.setData({ type: 'FeatureCollection', features: [] });
        return;
      }
      src.setData({
        type: 'FeatureCollection',
        features: [{
          type: 'Feature',
          properties: { color: TB_COLOR[tb] || '#0F4A37' },
          geometry: { type: 'Point', coordinates: [lon, lat] },
        }],
      });
    };
    if (!map || !readyRef.current) {
      if (containerRef.current) containerRef.current.addEventListener('mapready', apply, { once: true });
      return;
    }
    apply();
  }, [selectedId, selectedRec, pins]);

  // --- ease to selected pin (één keer per selectie) ----------------------
  // Pannen + inzoomen tot voorbij clusterMaxZoom (13) zodat de marker
  // gegarandeerd uit een cluster breekt en zichtbaar wordt. easedForIdRef
  // voorkomt dat we opnieuw pannen als pins/selectedRec later binnenkomen.
  const easedForIdRef = useRef(null);
  useEffect(() => {
    const map = mapRef.current;
    const apply = () => {
      if (!selectedId) { easedForIdRef.current = null; return; }
      if (easedForIdRef.current === selectedId) return;
      let lat, lon;
      if (selectedRec && typeof selectedRec.lat === 'number') {
        lat = selectedRec.lat; lon = selectedRec.lon;
      } else {
        const pin = pins.find((p) => p.id === selectedId);
        if (pin) { lat = pin.lat; lon = pin.lon; }
      }
      if (lat == null) return; // wacht op selectedRec
      easedForIdRef.current = selectedId;
      map.easeTo({ center: [lon, lat], zoom: Math.max(map.getZoom(), 14), duration: 600 });
    };
    if (!map || !readyRef.current) {
      if (containerRef.current) containerRef.current.addEventListener('mapready', apply, { once: true });
      return;
    }
    apply();
  }, [selectedId, selectedRec, pins]);

  // --- fitTrigger: external request to fit to all visible pins ----------
  useEffect(() => {
    if (!fitTrigger) return;
    const map = mapRef.current;
    if (!map || !readyRef.current || !pins.length) return;
    const first = [pins[0].lon, pins[0].lat];
    const bounds = pins.reduce(
      (b, p) => b.extend([p.lon, p.lat]),
      new maplibregl.LngLatBounds(first, first)
    );
    map.fitBounds(bounds, { padding: 60, duration: 600, maxZoom: 13 });
  }, [fitTrigger]);

  return (
    <div className="relative w-full h-full">
      <div ref={containerRef} className="absolute inset-0" aria-label="Kaart van Nederland met vergunningkennisgevingen" />
      {truncated && totalMatching > cap && (
        <div
          role="status"
          className="absolute top-3 left-1/2 -translate-x-1/2 z-10 bg-white/95 backdrop-blur-sm border border-amber-300 text-amber-900 text-[12px] px-3 py-1.5 rounded-md shadow-sm flex items-center gap-2 max-w-[480px]"
        >
          <svg viewBox="0 0 16 16" className="w-3.5 h-3.5 shrink-0 text-amber-600" aria-hidden>
            <path d="M8 2 L14 13 L2 13 Z" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinejoin="round" />
            <path d="M8 6.5 L8 9.5 M8 11 L8 11.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
          </svg>
          <span>
            <span className="tabular-nums font-medium">{fmt.number(cap)}</span>
            <span> van </span>
            <span className="tabular-nums font-medium">{fmt.number(totalMatching)}</span>
            <span> vergunningen getoond — verfijn filters of zoom in.</span>
          </span>
        </div>
      )}
      <MapLegend />
    </div>
  );
}

function MapLegend() {
  const items = [
    { label: 'Verleend', color: '#0F4A37' },
    { label: 'Aanvraag', color: '#8C7A5B' },
    { label: 'Ontwerp',  color: '#B58A2F' },
    { label: 'Geweigerd', color: '#A0522D' },
    { label: 'Ingetrokken', color: '#9CA3AF' },
  ];
  return (
    <div className="absolute left-3 bottom-3 bg-white/95 backdrop-blur-sm border border-slate-200 rounded px-3 py-2 text-[11px] text-slate-700 shadow-sm">
      <div className="font-medium text-slate-900 mb-1.5 uppercase tracking-wider text-[10px]">Type besluit</div>
      <div className="flex flex-col gap-1">
        {items.map((it) => (
          <div key={it.label} className="flex items-center gap-2">
            <span
              className="inline-block w-2.5 h-2.5 rounded-full ring-1 ring-white"
              style={{ background: it.color, boxShadow: '0 0 0 1px ' + it.color + '55' }}
            ></span>
            <span>{it.label}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { MapView });
