// map.jsx — MapLibre wrapper that uses the per-style URL from MAP_STYLES.
// Supports 3D pitch/bearing on classic3d and reuses the same map instance
// when the user pans/zooms (only re-creates on style change).

const { useEffect, useRef, useState } = React;

// Reference width for the map's geographic extent. Every context (editor at any
// size, checkout preview, export) anchors to this so they all show the SAME area.
const REF_WIDTH = 400;

function MapPreview({ styleId, center, zoom, is3D, pitch, bearing, onMoveEnd, onMap, interactive = true }) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const zAdjRef = useRef(0);
  const [ready, setReady] = useState(false);

  const styleDef = (window.MAP_STYLES || []).find(s => s.id === styleId) || window.MAP_STYLES[0];
  const styleUrl = styleDef.styleUrl;

  // Init (or re-init on style change).
  useEffect(() => {
    if (!containerRef.current) return;
    if (typeof maplibregl === 'undefined') return;

    if (mapRef.current) {
      try { mapRef.current.remove(); } catch (e) {}
      mapRef.current = null;
    }
    setReady(false);

    let cancelled = false;
    let map = null;

    async function build() {
      // For 3D styles: the MapTiler style enables 3D terrain from the start.
      // MapLibre crashes ("Cannot read properties of undefined (reading 'wrap')")
      // when terrain is active on an interactive map before its elevation tiles
      // have loaded. Fix: load WITHOUT terrain, then enable it on 'load' via
      // setTerrain — this keeps the real 3D relief (mountains) but avoids the
      // race-condition crash.
      let styleSpec = styleUrl;
      let terrainCfg = null;
      if (is3D) {
        try {
          const resp = await fetch(styleUrl);
          const json = await resp.json();
          terrainCfg = json.terrain || null; // apply after load
          delete json.terrain;
          styleSpec = json;
        } catch (e) {
          console.warn('No se pudo preparar el estilo 3D, usando el original', e);
        }
      }

      if (cancelled || !containerRef.current) return;

      // Show the same geographic span regardless of pixel width: a narrower
      // container uses a proportionally lower zoom.
      const zAdj = Math.log2((containerRef.current.clientWidth || REF_WIDTH) / REF_WIDTH);
      zAdjRef.current = zAdj;

      map = new maplibregl.Map({
        container: containerRef.current,
        style: styleSpec,
        center: center,
        zoom: zoom + zAdj,
        pitch: is3D ? (pitch ?? 55) : 0,
        bearing: is3D ? (bearing ?? 20) : 0,
        attributionControl: false,
        interactive: interactive,
        maxZoom: 20,
        minZoom: 1,
        maxPitch: 85,
        // Only the server-side render (Puppeteer, interactive=false) needs the
        // WebGL buffer preserved so it can capture the canvas. Keeping it on for
        // the interactive editor makes zoom/pan sluggish and leaves the map
        // looking pixelated after zooming — so enable it ONLY when non-interactive.
        preserveDrawingBuffer: !interactive,
      });
      if (!is3D) {
        map.dragRotate.disable();
        map.touchZoomRotate.disableRotation();
      }
      // The map is usable (pan / easeTo / unproject) as soon as it's created —
      // don't gate on 'load', which can be flaky with software WebGL and would
      // otherwise stop the pin/coords sync from working.
      if (onMap) onMap(map);
      setReady(true);
      map.on('load', () => {
        // Enable 3D terrain now that the style is loaded (avoids the crash).
        if (terrainCfg) {
          try { map.setTerrain(terrainCfg); }
          catch (e) { console.warn('setTerrain falló', e); }
        }
        // Readiness signals for server-side (Puppeteer) rendering.
        window.__MAP_LOADED__ = true;
        window.__MAP_LOADED_AT__ = Date.now();
      });
      // Signal for server-side (Puppeteer) rendering: increments once the map
      // has fully rendered and there are no more tiles pending. Harmless in the
      // normal app.
      map.on('idle', () => { window.__MAP_IDLE__ = (window.__MAP_IDLE__ || 0) + 1; });
      map.on('moveend', () => {
        if (onMoveEnd) {
          const c = map.getCenter();
          onMoveEnd({ center: [c.lng, c.lat], zoom: map.getZoom() - zAdjRef.current });
        }
      });
      mapRef.current = map;
    }

    build();

    return () => {
      cancelled = true;
      try { if (map) map.remove(); } catch (e) {}
      mapRef.current = null;
    };
  }, [styleUrl]);

  // External center/zoom changes (e.g. template picked).
  useEffect(() => {
    const m = mapRef.current;
    if (!m || !ready) return;
    const c = m.getCenter();
    const dx = Math.abs(c.lng - center[0]);
    const dy = Math.abs(c.lat - center[1]);
    const targetZoom = zoom + zAdjRef.current;
    const dz = Math.abs(m.getZoom() - targetZoom);
    if (dx > 0.0005 || dy > 0.0005 || dz > 0.05) {
      m.jumpTo({ center, zoom: targetZoom });
    }
  }, [center[0], center[1], zoom, ready]);

  // 3D orientation
  useEffect(() => {
    const m = mapRef.current;
    if (!m || !ready) return;
    m.jumpTo({
      pitch: is3D ? (pitch ?? 55) : 0,
      bearing: is3D ? (bearing ?? 20) : 0,
    });
  }, [is3D, pitch, bearing, ready]);

  return <div ref={containerRef} style={{ width: '100%', height: '100%' }} />;
}

// Static cheap thumbnail for the style picker (no MapLibre).
function MapThumb({ style }) {
  const sc = style.thumbStroke || '#222';
  return (
    <div className="style-tile-thumb" style={{ background: style.thumbBg, position: 'relative' }}>
      <svg viewBox="0 0 100 130" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>
        <g stroke={sc} strokeWidth="0.6" fill="none" opacity="0.7">
          <path d="M 0 22 Q 40 26 100 18" />
          <path d="M 0 50 Q 50 56 100 46" />
          <path d="M 0 78 Q 45 82 100 72" />
          <path d="M 0 100 Q 50 104 100 92" />
          <path d="M 22 0 Q 24 60 28 130" />
          <path d="M 52 0 Q 48 60 50 130" />
          <path d="M 78 0 Q 82 60 80 130" />
        </g>
        <circle cx="50" cy="60" r="3" fill="#c9694e" />
        <circle cx="50" cy="60" r="1.2" fill="white" />
      </svg>
    </div>
  );
}

Object.assign(window, { MapPreview, MapThumb });
