/* mapa-real.jsx — mapa OpenStreetMap (Leaflet) del MODELO REAL: una burbuja por barrio geolocalizado
   y fallback por macro-zona. Tamaño = cantidad de rutas · color = estado de cobertura. Las rutas sin
   macro-zona clasificada no se dibujan (se informan en la leyenda). window.CR.MapaReal */
(function () {
  window.CR = window.CR || {};
  var React = window.React;
  var h = React.createElement;

  // centroides de referencia (aprox.) de cada macro-zona de la operatoria
  var ZC = {
    CABA: [-34.6037, -58.3816],
    NORTE: [-34.4708, -58.5133],   // San Isidro
    OESTE: [-34.6534, -58.6198],   // Morón
    SUR: [-34.7606, -58.3970],     // Lomas de Zamora
    'LA PLATA': [-34.9221, -57.9544], // INDEC/Georef; misma referencia aproximada que ingesta-xlsx
    ROSARIO: [-32.9442, -60.6505]
  };
  function money(n) { return '$' + Number(n || 0).toLocaleString('es-AR'); }
  function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
    return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
  }); }

  function tiles(theme) {
    var dark = theme === 'dark';
    return window.L.tileLayer(
      'https://{s}.basemaps.cartocdn.com/' + (dark ? 'dark_all' : 'light_all') + '/{z}/{x}/{y}{r}.png',
      { maxZoom: 19, subdomains: 'abcd', attribution: '© OpenStreetMap · © CARTO' });
  }

  function tieneBarrio(r) {
    return r.barrioKey && r.barrioLat != null && r.barrioLng != null &&
      Number.isFinite(Number(r.barrioLat)) && Number.isFinite(Number(r.barrioLng));
  }

  // El backend entrega barrioKey y sus coordenadas ya resueltos. Si faltan, conserva el fallback v0.15.
  function porBarrio(rutas) {
    var m = {};
    (rutas || []).forEach(function (r) {
      var z = r.macroZona; if (!z || !ZC[z]) return;
      var conBarrio = tieneBarrio(r);
      var key = conBarrio ? 'b:' + r.barrioKey : 'z:' + z;
      var g = m[key] || (m[key] = {
        zona: conBarrio ? r.barrioKey : z,
        tipo: conBarrio ? 'barrio' : 'macro',
        coord: conBarrio ? [Number(r.barrioLat), Number(r.barrioLng)] : ZC[z],
        rutas: 0, asignadas: 0, sinCubrir: 0, backup: 0, costo: 0, km: 0
      });
      g.rutas++;
      if (r.estado === 'sin_asignar') g.sinCubrir++;
      else if (r.estado === 'manual') g.backup++;
      else if (r.patente) g.asignadas++;
      g.costo += r.tarifa || 0;
      g.km += r.km || 0;
    });
    return Object.keys(m).map(function (k) { return m[k]; });
  }

  function MapaReal(props) {
    var mapEl = React.useRef(null);
    var mapRef = React.useRef(null);
    var layerRef = React.useRef(null);
    var tileRef = React.useRef(null);

    function curTheme() { return document.documentElement.getAttribute('data-theme') || 'dark'; }

    function draw() {
      var L = window.L, map = mapRef.current, layer = layerRef.current;
      if (!L || !map || !layer || !map._loaded || !mapEl.current) return;
      try { drawInner(L, map, layer); } catch (e) { /* re-dibujo durante zoom/transición */ }
    }
    // El draw DIFERIDO (el timeout del setup, que corre una sola vez al montar) tiene que usar las
    // props del ÚLTIMO render, no las del primero: si dispara después de que llegaron datos nuevos
    // (p.ej. el upgrade del snapshot al arrancar), la closure vieja repinta la foto anterior encima
    // y el mapa queda mostrando macro-zonas aunque los barrios ya estén cargados.
    var drawRef = React.useRef(draw);
    drawRef.current = draw;
    function drawInner(L, map, layer) {
      layer.clearLayers();
      var grupos = porBarrio(props.rutas);
      if (!grupos.length) return;
      var maxR = Math.max.apply(null, grupos.map(function (g) { return g.rutas; }).concat([1]));
      var pts = [];

      grupos.forEach(function (g) {
        var c = g.coord; if (!c) return;
        pts.push(c);
        var D = g.tipo === 'barrio'
          ? Math.round(28 + Math.sqrt(g.rutas / maxR) * 28) // 28..56 px
          : Math.round(44 + Math.sqrt(g.rutas / maxR) * 40); // fallback v0.15: 44..84 px
        var estado = g.sinCubrir ? 'bad' : (g.backup ? 'warn' : 'ok');
        var html =
          '<div class="cr-zona cr-zona--' + estado + '" style="width:' + D + 'px;height:' + D + 'px;font-size:' + (D > 62 ? 17 : 14) + 'px">' +
            '<b>' + g.rutas + '</b>' +
          '</div>' +
          '<div class="cr-zona-lbl">' + esc(g.zona) + '</div>';
        // esc() también acá: Leaflet inyecta el tooltip por innerHTML igual que el label (l.94)
        var tip = esc(g.zona) + ' — ' + g.rutas + ' ruta' + (g.rutas > 1 ? 's' : '') +
          ' · ' + g.asignadas + ' asignada' + (g.asignadas === 1 ? '' : 's') +
          (g.sinCubrir ? ' · ⚠ ' + g.sinCubrir + ' sin cubrir' : '') +
          (g.backup ? ' · ' + g.backup + ' BackUp manual' : '') +
          (window.CR.canSeeCosts ? ' · ' + money(g.costo) : '') + ' · ' + Math.round(g.km) + ' km';
        L.marker(c, { icon: L.divIcon({ className: '', iconSize: [D, D + 22], iconAnchor: [D / 2, D / 2], html: html }) })
          .addTo(layer).bindTooltip(tip, { direction: 'top' });
      });

      // invalidateSize ANTES de encuadrar: si el contenedor todavía mide 0, fitBounds cae a zoom 0
      try { map.invalidateSize(false); } catch (e) {}
      var size = map.getSize();
      if (!size || size.x < 40 || size.y < 40) return;   // sin tamaño útil: reintenta en el próximo draw
      if (pts.length === 1) map.setView(pts[0], 10);
      else if (pts.length > 1) { try { map.fitBounds(L.latLngBounds(pts), { padding: [30, 30], maxZoom: 11 }); } catch (e) {} }
    }

    React.useEffect(function () {
      var L = window.L; if (!L) return;
      var map = L.map(mapEl.current, { zoomControl: true, attributionControl: true, scrollWheelZoom: true, wheelPxPerZoomLevel: 90 })
        .setView([-34.2, -59.2], 7);
      mapRef.current = map;
      tileRef.current = tiles(curTheme()); tileRef.current.addTo(map);
      layerRef.current = L.layerGroup().addTo(map);
      var t0 = setTimeout(function () { if (!mapRef.current) return; try { map.invalidateSize(); } catch (e) {} drawRef.current(); }, 150);
      var obs = new MutationObserver(function () {
        if (!mapRef.current) return;
        try { if (tileRef.current) map.removeLayer(tileRef.current); tileRef.current = tiles(curTheme()); tileRef.current.addTo(map); } catch (e) {}
      });
      obs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
      return function () { clearTimeout(t0); obs.disconnect(); mapRef.current = null; try { map.remove(); } catch (e) {} };
    }, []);

    var sig = (props.rutas || []).map(function (r) {
      return (r.idRuta || r.id) + ':' + r.macroZona + ':' + (r.barrioKey || '') + ':' +
        (r.barrioLat == null ? '' : r.barrioLat) + ':' + (r.barrioLng == null ? '' : r.barrioLng) + ':' +
        r.estado + ':' + (r.patente || '');
    }).join('|');
    React.useEffect(function () { draw(); }, [sig]);

    if (!window.L) {
      return h('div', { className: 'mapa-wrap' }, h('div', { className: 'empty' },
        h('h3', null, 'Mapa no disponible'), h('p', { className: 'muted' }, 'No se pudo cargar Leaflet (¿sin internet?).')));
    }

    var sinZona = (props.rutas || []).filter(function (r) { return !r.macroZona || !ZC[r.macroZona]; }).length;
    var sinBarrio = (props.rutas || []).filter(function (r) {
      return r.macroZona && ZC[r.macroZona] && !tieneBarrio(r);
    }).length;
    return h('div', { className: 'mapa-wrap' },
      h('div', { ref: mapEl, className: 'mapa-leaf' }),
      h('div', { className: 'mapa-legend' },
        h('span', null, h('i', { className: 'lg-dot', style: { background: 'var(--ok)' } }), 'Todo cubierto'),
        h('span', null, h('i', { className: 'lg-dot', style: { background: 'var(--warn)' } }), 'BackUp manual pendiente'),
        h('span', null, h('i', { className: 'lg-dot', style: { background: 'var(--danger)' } }), 'Rutas sin cubrir'),
        sinBarrio ? h('span', { className: 'muted' }, sinBarrio + ' ruta' + (sinBarrio > 1 ? 's' : '') + ' sin barrio geolocalizado') : null,
        sinZona ? h('span', { className: 'muted' }, sinZona + ' ruta' + (sinZona > 1 ? 's' : '') + ' sin macro-zona (no se dibuja' + (sinZona > 1 ? 'n' : '') + ')') : null,
        h('span', { className: 'muted' }, 'Barrios geolocalizados · fallback por macro-zona · © OpenStreetMap · CARTO')));
  }

  window.CR.MapaReal = MapaReal;
})();
