// Mapa Leaflet · BR-319 — tiles CARTO + polígonos IBGE reais
const { useEffect, useRef, useState, useMemo } = React;

// Catmull-Rom spline: transforma N pontos em N*segments pontos suaves.
// Usado para os rios — os pontos brutos têm 3-6 vértices e ficam angulares.
function smoothPolyline(pts, segments = 24) {
  if (!pts || pts.length < 3) return pts || [];
  const out = [pts[0]];
  for (let i = 0; i < pts.length - 1; i++) {
    const p0 = pts[Math.max(0, i - 1)];
    const p1 = pts[i];
    const p2 = pts[i + 1];
    const p3 = pts[Math.min(pts.length - 1, i + 2)];
    for (let t = 1; t <= segments; t++) {
      const u = t / segments, u2 = u * u, u3 = u2 * u;
      const lat = 0.5 * (
        (2 * p1[0]) +
        (-p0[0] + p2[0]) * u +
        (2 * p0[0] - 5 * p1[0] + 4 * p2[0] - p3[0]) * u2 +
        (-p0[0] + 3 * p1[0] - 3 * p2[0] + p3[0]) * u3
      );
      const lng = 0.5 * (
        (2 * p1[1]) +
        (-p0[1] + p2[1]) * u +
        (2 * p0[1] - 5 * p1[1] + 4 * p2[1] - p3[1]) * u2 +
        (-p0[1] + 3 * p1[1] - 3 * p2[1] + p3[1]) * u3
      );
      out.push([lat, lng]);
    }
  }
  return out;
}

const TILE_PROVIDERS = {
  dark: {
    url: 'https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png',
    attribution: '© <a href="https://www.openstreetmap.org/copyright">OSM</a> · © <a href="https://carto.com/attributions">CARTO</a>',
    sub: 'abcd', max: 19, dark: true
  },
  satellite: {
    url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
    attribution: 'Tiles © Esri · Source: Esri, Maxar, Earthstar Geographics',
    sub: '', max: 19, dark: true
  },
  light: {
    url: 'https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png',
    attribution: '© <a href="https://www.openstreetmap.org/copyright">OSM</a> · © <a href="https://carto.com/attributions">CARTO</a>',
    sub: 'abcd', max: 19, dark: false
  },
};

function MapaBR319({ data, layers, selectedMun, onSelectMun, year, chainFilter, regionFilter, basemap = 'dark', onExportPNG }) {
  const mapRef = useRef(null);
  const containerRef = useRef(null);
  const tileRef = useRef(null);
  const layersRef = useRef({});
  const fitRef = useRef(false); // se já enquadrou os limites uma vez
  const munisGJRef = useRef(null); // raw geojson once loaded
  const [ready, setReady] = useState(false);

  // ---------- init map ----------
  useEffect(() => {
    if (!containerRef.current || mapRef.current) return;
    const map = L.map(containerRef.current, {
      center: [-5.5, -61.5],
      zoom: 6,
      minZoom: 5, maxZoom: 11,
      zoomControl: false,
      attributionControl: true,
      worldCopyJump: false,
      preferCanvas: true,
      fadeAnimation: true,
    });
    L.control.zoom({ position: 'topright' }).addTo(map);
    L.control.scale({ imperial: false, position: 'bottomleft' }).addTo(map);

    mapRef.current = map;

    // Load munis geojson
    fetch('munis-aae.geojson')
      .then(r => r.json())
      .then(gj => {
        munisGJRef.current = gj;
        setReady(true);
      })
      .catch(e => console.error('geojson load', e));

    return () => { map.remove(); mapRef.current = null; };
  }, []);

  // ---------- tiles ----------
  useEffect(() => {
    if (!mapRef.current) return;
    if (tileRef.current) tileRef.current.remove();
    const cfg = TILE_PROVIDERS[basemap] || TILE_PROVIDERS.dark;
    tileRef.current = L.tileLayer(cfg.url, {
      attribution: cfg.attribution,
      subdomains: cfg.sub, maxZoom: cfg.max
    }).addTo(mapRef.current);
    // bg color matches tile theme
    if (containerRef.current) {
      containerRef.current.style.background = cfg.dark ? '#0b1410' : '#eef3ec';
    }
  }, [basemap]);

  // ---------- helpers ----------
  const dark = TILE_PROVIDERS[basemap]?.dark;

  const productionFor = (m) => {
    if (chainFilter === 'todas') return Number(m.orgs?.total) || 0;
    return Number((m.pevs?.[year] || {})[chainFilter]) || 0;
  };

  // color scale for choropleth
  const valuesMax = useMemo(() => {
    if (!data?.municipios) return 1;
    const vs = data.municipios.map(productionFor);
    return Math.max(1, ...vs);
  }, [data, chainFilter, year]);

  const fillFor = (m) => {
    const v = productionFor(m);
    if (v <= 0) return dark ? '#1a2820' : '#e8efe5';
    const t = Math.sqrt(v) / Math.sqrt(valuesMax); // sqrt scale
    // gradient: green → orange
    const g = [
      [5, 66, 41],     // #054229
      [8, 99, 45],     // #08632D
      [72, 131, 106],  // #48836A
      [205, 93, 28],   // #CD5D1C orange
      [243, 156, 18],  // amber
    ];
    const idx = Math.min(g.length - 2, Math.floor(t * (g.length - 1)));
    const f = (t * (g.length - 1)) - idx;
    const c = g[idx].map((a, i) => Math.round(a + (g[idx + 1][i] - a) * f));
    return `rgb(${c[0]},${c[1]},${c[2]})`;
  };

  // ---------- layers: build/update ----------
  useEffect(() => {
    if (!ready || !mapRef.current) return;
    const map = mapRef.current;

    // Clear previous custom layers
    Object.values(layersRef.current).forEach(l => l && map.removeLayer(l));
    layersRef.current = {};

    // ---- Municípios (polygons) ----
    if (layers.munis && munisGJRef.current) {
      const munisIndex = {};
      data.municipios.forEach(m => munisIndex[m.nome] = m);

      const munisLayer = L.geoJSON(munisGJRef.current, {
        style: (feat) => {
          const m = munisIndex[feat.properties.name];
          const sel = selectedMun === feat.properties.name;
          if (regionFilter !== 'todas' && m && m.regiao !== regionFilter) {
            return { fillOpacity: 0.05, opacity: 0.15, color: '#666', weight: 0.5 };
          }
          return {
            fillColor: m ? fillFor(m) : '#333',
            fillOpacity: sel ? 0.85 : 0.62,
            color: sel ? '#CD5D1C' : (dark ? '#fff' : '#054229'),
            weight: sel ? 2.2 : 0.7,
            opacity: sel ? 1 : (dark ? 0.55 : 0.6),
          };
        },
        onEachFeature: (feat, lyr) => {
          const m = munisIndex[feat.properties.name];
          if (!m) return;
          const v = productionFor(m);
          const cadeia = chainFilter === 'todas' ? null : data.cadeias.find(c => c.id === chainFilter);
          const html = `
            <div style="font-family:Plus Jakarta Sans,sans-serif;min-width:200px">
              <div style="font-size:14px;font-weight:800;color:#054229;margin-bottom:2px">${m.nome}</div>
              <div style="font-size:10px;color:#6b7280;font-family:JetBrains Mono;text-transform:uppercase;letter-spacing:0.06em;margin-bottom:8px">${m.uf} · ${m.regiao}</div>
              <div style="font-size:11px;color:#374151;line-height:1.5">
                <strong>${m.orgs.total}</strong> organizações<br/>
                ${cadeia ? `<strong>${v}</strong> ${cadeia.unidade} · ${cadeia.rotulo} (${year})` : `Produção PEVS ${year}`}
              </div>
              <div style="margin-top:8px;padding-top:6px;border-top:1px solid #e5e7eb;font-size:10px;color:#CD5D1C;font-weight:600">Clique para detalhes →</div>
            </div>`;
          lyr.bindTooltip(html, { sticky: true, direction: 'top', className: 'aae-tip' });
          lyr.on({
            mouseover: (e) => { e.target.setStyle({ weight: 2.4, color: '#CD5D1C' }); },
            mouseout: (e) => { munisLayer.resetStyle(e.target); },
            click: () => onSelectMun(m.nome === selectedMun ? null : m.nome),
          });
        }
      }).addTo(map);
      layersRef.current.munis = munisLayer;

      // Fit on first ready (apenas uma vez — não re-enquadra ao mudar filtros)
      if (!fitRef.current) {
        map.fitBounds(munisLayer.getBounds().pad(0.05));
        fitRef.current = true;
      }
    }

    // ---- BR-319 polyline ----
    if (layers.br319 && data.br319) {
      const buffer = L.polyline(data.br319.flatMap(([lat, lng]) => [[lat + 0.45, lng - 0.45]]).concat(
        [...data.br319].reverse().map(([lat, lng]) => [lat - 0.45, lng + 0.45])
      ), { color: '#054229', weight: 0.5, fill: true, fillColor: '#054229', fillOpacity: 0.04, dashArray: '4 4', opacity: 0.45 });

      const bufferPoly = L.polygon(
        data.br319.map(([lat, lng]) => [lat + 0.45, lng - 0.45]).concat(
          [...data.br319].reverse().map(([lat, lng]) => [lat - 0.45, lng + 0.45])
        ),
        { color: '#CD5D1C', weight: 0.8, fillColor: '#CD5D1C', fillOpacity: 0.03, dashArray: '5 4', opacity: 0.5 }
      ).addTo(map);
      layersRef.current.buffer = bufferPoly;

      const baseLine = L.polyline(data.br319, { color: '#fff', weight: 5, opacity: 0.9 }).addTo(map);
      const innerLine = L.polyline(data.br319, { color: '#CD5D1C', weight: 2.5, opacity: 1 }).addTo(map);
      const dashLine = L.polyline(data.br319, { color: '#054229', weight: 1, dashArray: '5 5', opacity: 0.8 }).addTo(map);
      layersRef.current.br319a = baseLine;
      layersRef.current.br319b = innerLine;
      layersRef.current.br319c = dashLine;

      // BR-319 label as marker
      const midIdx = Math.floor(data.br319.length / 2);
      const lbl = L.marker(data.br319[midIdx], {
        icon: L.divIcon({
          className: 'aae-br-label',
          html: '<div style="background:#CD5D1C;color:#fff;font-family:JetBrains Mono;font-size:10px;font-weight:700;padding:3px 8px;border-radius:2px;letter-spacing:0.1em;box-shadow:0 2px 4px rgba(0,0,0,0.3)">BR-319</div>',
          iconSize: [60, 22], iconAnchor: [30, 11]
        })
      }).addTo(map);
      layersRef.current.br319lbl = lbl;
    }

    // ---- Rios (suavizados + halo cartográfico) ----
    if (layers.rios && data.rios) {
      const rioGroup = L.layerGroup();
      data.rios.forEach(rio => {
        const pts = smoothPolyline(rio.pts, 28);

        // Halo (linha mais larga e clara, simula reflexo da água)
        L.polyline(pts, {
          color: dark ? '#7cc4f5' : '#a8c8df',
          weight: 5.5, opacity: dark ? 0.18 : 0.35,
          lineCap: 'round', lineJoin: 'round',
        }).addTo(rioGroup);

        // Linha principal (azul mais saturado no meio)
        L.polyline(pts, {
          color: dark ? '#3a8bd6' : '#2c5478',
          weight: 2.2, opacity: 0.85,
          lineCap: 'round', lineJoin: 'round',
        }).addTo(rioGroup);

        // Centro mais claro, dá brilho de água
        L.polyline(pts, {
          color: dark ? '#a8d4f5' : '#5b8db8',
          weight: 0.7, opacity: 0.9,
          lineCap: 'round', lineJoin: 'round',
        }).addTo(rioGroup);

        // Label do rio no meio do trecho original (sem suavização)
        const mid = rio.pts[Math.floor(rio.pts.length / 2)];
        L.marker(mid, {
          icon: L.divIcon({
            className: 'aae-rio-label',
            html: `<div style="font-family:'DM Sans';font-style:italic;font-size:11px;font-weight:600;letter-spacing:0.02em;color:${dark ? '#a8d4f5' : '#2c5478'};text-shadow:0 0 6px ${dark ? '#000' : '#fff'},0 0 6px ${dark ? '#000' : '#fff'};white-space:nowrap">${rio.nome}</div>`,
            iconSize: [80, 16], iconAnchor: [40, 8]
          }), interactive: false
        }).addTo(rioGroup);
      });
      rioGroup.addTo(map);
      layersRef.current.rios = rioGroup;
    }

    // ---- TIs ----
    if (layers.tis && data.tis) {
      const tiGroup = L.layerGroup();
      data.tis.forEach(ti => {
        L.circle(ti.c, {
          radius: ti.r * 100000,
          color: '#CD5D1C', weight: 1, opacity: 0.7,
          fillColor: '#CD5D1C', fillOpacity: 0.18,
          dashArray: '3 3'
        }).bindTooltip(`<strong>TI ${ti.nome}</strong>`, { className: 'aae-tip' }).addTo(tiGroup);
      });
      tiGroup.addTo(map);
      layersRef.current.tis = tiGroup;
    }

    // ---- UCs ----
    if (layers.ucs && data.ucs) {
      const ucGroup = L.layerGroup();
      data.ucs.forEach(uc => {
        L.circle(uc.c, {
          radius: uc.r * 100000,
          color: '#08632D', weight: 1, opacity: 0.75,
          fillColor: '#08632D', fillOpacity: 0.12,
          dashArray: '6 4'
        }).bindTooltip(`<strong>UC ${uc.nome}</strong>`, { className: 'aae-tip' }).addTo(ucGroup);
      });
      ucGroup.addTo(map);
      layersRef.current.ucs = ucGroup;
    }

    // ---- Heatmap de densidade organizacional (Leaflet.heat + pontos distribuídos) ----
    if (layers.munis && chainFilter === 'todas' && window.L.heatLayer) {
      // Gera pontos pseudo-aleatórios (seed = nome do município) ao redor da sede,
      // proporcionalmente ao nº de organizações. Cada ponto é uma "organização" sintética.
      const heatPts = [];
      const seedRand = (s) => {
        // hash simples + LCG → determinístico por (município, índice)
        let h = 0;
        for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0;
        return () => {
          h = (h * 1103515245 + 12345) | 0;
          return ((h >>> 16) & 0x7fff) / 0x7fff;
        };
      };

      data.municipios.forEach(m => {
        if (regionFilter !== 'todas' && m.regiao !== regionFilter) return;
        const orgs = Number(m.orgs?.total) || 0;
        if (orgs === 0) return;
        const rng = seedRand(m.nome);
        // Espalha n = orgs pontos em círculo de raio proporcional a sqrt(orgs)
        // Cada ponto representa uma "organização" sintética (densidade visual)
        const spread = 0.04 + Math.sqrt(orgs) * 0.018; // graus (~0.04°≈4km, ~0.2°≈22km)
        for (let i = 0; i < orgs; i++) {
          // Distribuição radial Gaussian-like (mais denso no centro)
          const r = Math.sqrt(rng()) * spread;
          const theta = rng() * Math.PI * 2;
          const lat = m.coords[0] + Math.sin(theta) * r;
          const lng = m.coords[1] + Math.cos(theta) * r * 1.05; // pequena correção de aspecto
          heatPts.push([lat, lng, 0.6]); // intensidade fixa, deixa o heatmap somar
        }
      });

      const heat = window.L.heatLayer(heatPts, {
        radius: 28,
        blur: 38,
        maxZoom: 11,
        minOpacity: 0.35,
        max: 5,
        gradient: {
          0.0: 'rgba(8,99,45,0)',
          0.2: 'rgba(8,99,45,0.55)',
          0.4: 'rgba(72,131,106,0.75)',
          0.6: 'rgba(243,156,18,0.85)',
          0.8: 'rgba(205,93,28,0.92)',
          1.0: 'rgba(180,40,20,0.95)',
        },
      });
      heat.addTo(map);
      layersRef.current.heat = heat;
    }

    // ---- Sedes municipais (marcadores minimalistas estilo cartográfico) ----
    if (layers.munis) {
      const dotGroup = L.layerGroup();
      data.municipios.forEach(m => {
        if (regionFilter !== 'todas' && m.regiao !== regionFilter) return;
        const reg = data.regioes.find(r => r.id === m.regiao);
        const sel = selectedMun === m.nome;
        const v = productionFor(m);
        // Raios bem menores: 2.8 (mín) a ~5 (máx). Selecionado um pouco maior.
        const r = chainFilter === 'todas'
          ? 2.8 + Math.sqrt(m.orgs.total) * 0.28
          : 2.8 + Math.sqrt(Math.max(0, v)) * 0.10;
        const radius = Math.min(5.5, r);
        const baseColor = sel ? '#CD5D1C' : (reg?.cor || '#054229');

        // Sede: ponto sólido + anel fino branco (clássico de cartas militares/IBGE)
        const dot = L.circleMarker(m.coords, {
          radius: sel ? radius + 1.5 : radius,
          color: '#ffffff',
          weight: sel ? 1.6 : 1,
          fillColor: baseColor,
          fillOpacity: 1,
          opacity: 1,
        });
        dot.on('click', () => onSelectMun(m.nome === selectedMun ? null : m.nome));
        dot.bindTooltip(`<strong>${m.nome}</strong>`, {
          direction: 'top', offset: [0, -4], className: 'aae-tip aae-tip-mini'
        });
        dot.addTo(dotGroup);

        // Label permanente do município (não para o selecionado, esse já tem pill flutuante)
        if (!sel) {
          L.marker(m.coords, {
            icon: L.divIcon({
              className: 'aae-muni-label',
              html: `<div class="muni-label-text muni-label-${dark ? 'dark' : 'light'}">${m.nome}</div>`,
              iconSize: [120, 14], iconAnchor: [60, -8]
            }), interactive: false
          }).addTo(dotGroup);
        }

        // Anel laranja externo só no selecionado (chamativo mas discreto)
        if (sel) {
          L.circleMarker(m.coords, {
            radius: radius + 5,
            color: '#CD5D1C',
            weight: 1.3,
            fill: false,
            opacity: 0.85,
            dashArray: '3 3',
            interactive: false,
          }).addTo(dotGroup);
        }

        // Label flutuante para o município selecionado
        if (sel) {
          L.marker(m.coords, {
            icon: L.divIcon({
              className: 'aae-sel-label',
              html: `<div style="background:#CD5D1C;color:#fff;padding:3px 9px;border-radius:99px;font-family:'Plus Jakarta Sans';font-size:11px;font-weight:700;white-space:nowrap;box-shadow:0 3px 10px rgba(0,0,0,0.45),0 0 0 1.5px rgba(255,255,255,0.2);transform:translateY(-18px);letter-spacing:0.01em">${m.nome}</div>`,
              iconSize: [140, 22], iconAnchor: [70, 11]
            }), interactive: false
          }).addTo(dotGroup);
        }
      });
      dotGroup.addTo(map);
      layersRef.current.dots = dotGroup;
    }

  }, [ready, layers, selectedMun, year, chainFilter, regionFilter, basemap, valuesMax]);

  // ---------- expose PNG export ----------
  useEffect(() => {
    if (!onExportPNG) return;
    onExportPNG.current = () => {
      const map = mapRef.current; if (!map) return;
      // simple approach: html2canvas-like via dom-to-image is heavy; fallback: open print dialog
      window.alert('Para exportar o mapa: use o botão de zoom para enquadrar e tire um print da tela. Para PNG completo, exporte os dados via tabela.');
    };
  }, []);

  return (
    <div style={{ position: 'relative', width: '100%', height: '100%' }}>
      <div ref={containerRef} className="leaflet-stage" style={{ width: '100%', height: '100%' }}></div>

      {/* Legenda choropleth */}
      <div className="map-legend">
        <div className="legend-title">
          {chainFilter === 'todas'
            ? 'Densidade institucional'
            : `${data.cadeias.find(c => c.id === chainFilter)?.rotulo || ''} · ${year}`}
        </div>
        <div className="legend-bar">
          <span style={{ background: '#054229' }}></span>
          <span style={{ background: '#08632D' }}></span>
          <span style={{ background: '#48836A' }}></span>
          <span style={{ background: '#CD5D1C' }}></span>
          <span style={{ background: '#F39C12' }}></span>
        </div>
        <div className="legend-scale">
          <span>0</span>
          <span>{chainFilter === 'todas' ? `${valuesMax} orgs` : `${valuesMax} ${data.cadeias.find(c => c.id === chainFilter)?.unidade || ''}`}</span>
        </div>
      </div>

      {/* Crédito */}
      <div className="map-credit">
        Org. Rafael Rabelo, 2026 · DATUM SIRGAS 2000 · EPSG 4674 · Polígonos IBGE/2023
      </div>
    </div>
  );
}

window.MapaBR319 = MapaBR319;
