// aero-radar.jsx — Full-page radar map with MapLibre GL basemap

const REGION_DEFS = {
  south:  { center: [-53.84, -29.69], zoom: 5.2 },
  brazil: { center: [-52,    -14   ], zoom: 3.6 },
  global: { center: [-10,     20   ], zoom: 1.6 },
};

function PageRadar({ lang }) {
  const t = window.WX.T[lang];
  const [layer, setLayer] = useState("composite");
  const [satProduct, setSatProduct] = useState("geocolor");
  const [frame, setFrame] = useState(11);
  const [playing, setPlaying] = useState(false);
  const [region, setRegion] = useState("south");

  // Lifted map state — basemap lives in BigRadarMap, controls in this scope.
  const mapRef = useRef(null);
  const [cursor, setCursor] = useState({ lat: -29.69, lng: -53.84 });
  const [pixelDbz, setPixelDbz] = useState(48);
  const [satTimestamp, setSatTimestamp] = useState(null);

  useEffect(() => {
    if (!playing) return;
    const id = setInterval(() => setFrame(f => (f + 1) % 12), 350);
    return () => clearInterval(id);
  }, [playing]);

  const frameLabel = (f) => f === 11
    ? (lang === "pt" ? "AGORA" : "NOW")
    : `-${(11 - f) * 10}m`;

  // Map ctl helpers
  const zoomIn = () => mapRef.current?.zoomIn();
  const zoomOut = () => mapRef.current?.zoomOut();
  const goHome = () => mapRef.current?.flyTo({ ...REGION_DEFS[region], duration: 900 });
  const resetNorth = () => mapRef.current?.rotateTo(0, { duration: 400 });

  return (
    <div style={{ background: AERO.paper }}>
      {/* Top breadcrumb + title */}
      <Container style={{ paddingTop: 36 }}>
        <div className="aero-eyebrow">
          <a href="#/home" className="aero-link" style={{ color: AERO.inkSoft }}>{lang === "pt" ? "Início" : "Home"}</a>
          {" / "}<span style={{ color: AERO.ink }}>Radar</span>
        </div>
        <h1 className="aero-serif" style={{
          fontStyle: 'italic', fontWeight: 400,
          fontSize: 'clamp(56px, 6vw, 88px)',
          lineHeight: 0.95, letterSpacing: -2,
          color: AERO.ink, margin: '12px 0 6px',
        }}>
          {lang === "pt" ? "Radar composto" : "Composite radar"}
        </h1>
        <div className="aero-mono" style={{
          fontSize: 11, color: AERO.inkSoft, letterSpacing: 0.5,
        }}>
          {lang === "pt"
            ? "32 estações radar · banda S · resolução 1 km · varredura a cada 10 min · mapa base MapLibre"
            : "32 radar stations · S-band · 1 km resolution · 10-min sweep · MapLibre basemap"}
        </div>
      </Container>

      {/* Main interactive surface */}
      <Container style={{ marginTop: 36 }}>
        <div style={{
          display: 'grid', gridTemplateColumns: '260px 1fr', gap: 24,
          minHeight: 720,
        }}>
          {/* SIDEBAR */}
          <aside style={{
            background: '#fff', border: `0.5px solid ${AERO.hair}`,
            borderRadius: 6, padding: 24,
            display: 'flex', flexDirection: 'column', gap: 26,
            alignSelf: 'start', position: 'sticky', top: 130,
          }}>
            <div>
              <div className="aero-eyebrow" style={{ marginBottom: 12 }}>
                {lang === "pt" ? "Camada" : "Layer"}
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                {[
                  ["composite", lang === "pt" ? "Composto radar+sat" : "Radar + satellite"],
                  ["reflectivity", lang === "pt" ? "Refletividade" : "Reflectivity"],
                  ["satellite", lang === "pt" ? "Satélite (GOES-16)" : "Satellite (GOES-16)"],
                  ["lightning", lang === "pt" ? "Descargas atmosféricas" : "Lightning"],
                  ["wind", lang === "pt" ? "Vento 10 m" : "Wind 10 m"],
                ].map(([k, label]) => (
                  <button key={k} onClick={() => setLayer(k)} style={{
                    textAlign: 'left', padding: '8px 12px', borderRadius: 4,
                    background: layer === k ? AERO.ink : 'transparent',
                    color: layer === k ? AERO.paper : AERO.ink,
                    border: 'none', cursor: 'pointer',
                    fontFamily: 'inherit', fontSize: 13,
                  }}>{label}</button>
                ))}
              </div>
            </div>

            <div>
              <div className="aero-eyebrow" style={{ marginBottom: 12 }}>
                {lang === "pt" ? "Região" : "Region"}
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                {[
                  ["south", lang === "pt" ? "Sul do Brasil" : "Southern Brazil"],
                  ["brazil", "Brasil"],
                  ["global", lang === "pt" ? "Global" : "Global"],
                ].map(([k, label]) => (
                  <button key={k} onClick={() => setRegion(k)} style={{
                    textAlign: 'left', padding: '8px 12px', borderRadius: 4,
                    background: region === k ? AERO.paperDeep : 'transparent',
                    color: AERO.ink, border: 'none', cursor: 'pointer',
                    fontFamily: 'inherit', fontSize: 13,
                    fontWeight: region === k ? 500 : 400,
                  }}>{label}</button>
                ))}
              </div>
            </div>

            <div>
              <div className="aero-eyebrow" style={{ marginBottom: 12 }}>
                {lang === "pt" ? "Sobreposições" : "Overlays"}
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8, fontSize: 13 }}>
                <Toggle label={lang === "pt" ? "Rótulos de cidades" : "City labels"} checked />
                <Toggle label={lang === "pt" ? "Estações radar" : "Radar stations"} checked />
                <Toggle label={lang === "pt" ? "Anéis de alcance" : "Range rings"} />
                <Toggle label={lang === "pt" ? "Rotas de voo" : "Flight routes"} />
              </div>
            </div>

            <div style={{ borderTop: `0.5px solid ${AERO.hair}`, paddingTop: 18 }}>
              <div className="aero-eyebrow">{t.reflectivity} (dBZ)</div>
              <div style={{ display: 'flex', height: 8, borderRadius: 2,
                overflow: 'hidden', marginTop: 8 }}>
                {["#3a6f4b", "#5a9c5e", "#c5a23a", "#c97c3a", "#a8463a", "#7a2a6a"].map((c, i) =>
                  <div key={i} style={{ flex: 1, background: c }} />
                )}
              </div>
              <div className="aero-mono" style={{
                display: 'flex', justifyContent: 'space-between', marginTop: 4,
                fontSize: 9, color: AERO.inkSoft,
              }}>
                <span>5</span><span>20</span><span>30</span><span>40</span><span>50</span><span>60+</span>
              </div>
            </div>

            <div className="aero-mono" style={{
              fontSize: 10, color: AERO.inkSoft, letterSpacing: 0.5, lineHeight: 1.6,
              borderTop: `0.5px solid ${AERO.hair}`, paddingTop: 18,
            }}>
              {lang === "pt" ? "Última varredura" : "Last sweep"}: 08:30 GMT-3
              <br />{lang === "pt" ? "Próxima" : "Next"}: 08:40 GMT-3
              <br />{lang === "pt" ? "Latência" : "Latency"}: 230 ms
            </div>
          </aside>

          {/* MAP SURFACE */}
          <div style={{
            background: '#0a131c', border: `0.5px solid ${AERO.hair}`,
            borderRadius: 6, overflow: 'hidden', position: 'relative',
            minHeight: 720,
          }}>
            <BigRadarMap layer={layer} frame={frame} region={region} lang={lang}
              satProduct={satProduct}
              mapRef={mapRef} setCursor={setCursor} setPixelDbz={setPixelDbz}
              setSatTimestamp={setSatTimestamp} />

            {/* Satellite product picker (only when satellite layer is active) */}
            {layer === "satellite" && (
              <div style={{
                position: 'absolute', top: 64, left: 18, zIndex: 5,
                background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(8px)',
                border: '0.5px solid rgba(255,255,255,0.18)', borderRadius: 6,
                padding: '8px 10px', display: 'flex', flexDirection: 'column', gap: 6,
              }}>
                <div className="aero-mono" style={{
                  fontSize: 9, letterSpacing: 1.2, color: 'rgba(255,255,255,0.6)',
                  textTransform: 'uppercase', marginBottom: 2,
                }}>GOES-16 · {lang === "pt" ? "Produto" : "Product"}</div>
                {[
                  ["geocolor", "GeoColor"],
                  ["13",       lang === "pt" ? "IR · Banda 13" : "IR · Band 13"],
                  ["08",       lang === "pt" ? "Vapor d'água · 08" : "Water vapor · 08"],
                ].map(([k, label]) => (
                  <button key={k} onClick={() => setSatProduct(k)} style={{
                    padding: '4px 10px', borderRadius: 4,
                    background: satProduct === k ? 'rgba(255,255,255,0.18)' : 'transparent',
                    color: satProduct === k ? '#fff' : 'rgba(255,255,255,0.65)',
                    border: 'none', cursor: 'pointer', textAlign: 'left',
                    fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5,
                    letterSpacing: 0.5,
                  }}>{label}</button>
                ))}
                {satTimestamp && (
                  <div className="aero-mono" style={{
                    fontSize: 9, letterSpacing: 0.5, color: 'rgba(255,255,255,0.45)',
                    marginTop: 4, paddingTop: 6, borderTop: '0.5px solid rgba(255,255,255,0.12)',
                  }}>
                    {lang === "pt" ? "Última" : "Latest"}: {satTimestamp}
                  </div>
                )}
              </div>
            )}

            {/* Top overlays */}
            <div style={{
              position: 'absolute', top: 18, left: 18, right: 18,
              display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start',
              pointerEvents: 'none', zIndex: 5,
            }}>
              <div style={{ pointerEvents: 'auto' }}>
                <div className="aero-mono" style={{
                  display: 'inline-flex', alignItems: 'center', gap: 8,
                  background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(8px)',
                  padding: '8px 12px', borderRadius: 999,
                  fontSize: 10.5, letterSpacing: 1.4, color: '#fff',
                  border: '0.5px solid rgba(255,255,255,0.18)',
                }}>
                  <span style={{ width: 7, height: 7, borderRadius: 4, background: AERO.danger }} />
                  {layer === "satellite"
                    ? `GOES-16 · ${lang === "pt" ? "LOOP 3H" : "3H LOOP"}`
                    : (playing ? (lang === "pt" ? "REPRODUZINDO" : "PLAYING") : frameLabel(frame).toUpperCase())}
                </div>
              </div>
              {layer !== "satellite" && (
                <div className="aero-mono" style={{
                  background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(8px)',
                  padding: '8px 14px', borderRadius: 6, color: '#fff',
                  fontSize: 10.5, letterSpacing: 0.5, lineHeight: 1.55,
                  border: '0.5px solid rgba(255,255,255,0.18)',
                  pointerEvents: 'auto',
                }}>
                  {lang === "pt" ? "Cursor" : "Cursor"}: {cursor.lat.toFixed(2)}°, {cursor.lng.toFixed(2)}°
                  <br />{lang === "pt" ? "Pixel" : "Pixel"}: {pixelDbz} dBZ
                </div>
              )}
            </div>

            {/* Zoom + compass — only relevant for the Mercator basemap */}
            {layer !== "satellite" && (
              <div style={{ position: 'absolute', bottom: 110, right: 18, display: 'flex',
                flexDirection: 'column', gap: 6, pointerEvents: 'auto', zIndex: 5 }}>
                <MapBtn onClick={zoomIn}>＋</MapBtn>
                <MapBtn onClick={zoomOut}>−</MapBtn>
                <MapBtn onClick={goHome}>⌂</MapBtn>
                <MapBtn onClick={resetNorth}>N</MapBtn>
              </div>
            )}

            {/* Bottom timeline scrubber — the GOES GIF carries its own animation */}
            {layer !== "satellite" && (
            <div style={{
              position: 'absolute', bottom: 18, left: 18, right: 18,
              background: 'rgba(0,0,0,0.65)', backdropFilter: 'blur(12px)',
              border: '0.5px solid rgba(255,255,255,0.16)',
              borderRadius: 8, padding: '14px 18px',
              display: 'flex', alignItems: 'center', gap: 18, zIndex: 5,
            }}>
              <button onClick={() => setPlaying(p => !p)} style={{
                width: 34, height: 34, borderRadius: 17,
                border: '0.5px solid rgba(255,255,255,0.3)',
                background: 'rgba(255,255,255,0.08)', color: '#fff',
                cursor: 'pointer', display: 'flex',
                alignItems: 'center', justifyContent: 'center',
              }}>
                {playing
                  ? <svg width="12" height="12" viewBox="0 0 12 12"><rect x="2" y="1" width="3" height="10" fill="#fff"/><rect x="7" y="1" width="3" height="10" fill="#fff"/></svg>
                  : <svg width="12" height="12" viewBox="0 0 12 12"><polygon points="3,1 3,11 11,6" fill="#fff"/></svg>}
              </button>
              <div style={{ flex: 1, position: 'relative', height: 38 }}>
                <div style={{
                  position: 'absolute', left: 0, right: 0, top: 18, height: 2,
                  background: 'rgba(255,255,255,0.16)', borderRadius: 1,
                }} />
                {Array.from({ length: 12 }).map((_, i) => (
                  <button key={i} onClick={() => setFrame(i)} style={{
                    position: 'absolute', left: `${(i / 11) * 100}%`, top: 8,
                    transform: 'translateX(-50%)',
                    background: 'transparent', border: 'none', padding: 0, cursor: 'pointer',
                    color: '#fff',
                  }}>
                    <div style={{
                      width: 14, height: 14, borderRadius: 7, margin: 'auto',
                      background: i === frame ? '#fff' : i < frame ? AERO.warm : 'rgba(255,255,255,0.18)',
                    }} />
                    <div className="aero-mono" style={{
                      fontSize: 9, color: i === frame ? '#fff' : 'rgba(255,255,255,0.45)',
                      marginTop: 4, letterSpacing: 0.5, textAlign: 'center',
                    }}>{frameLabel(i)}</div>
                  </button>
                ))}
              </div>
            </div>
            )}
          </div>
        </div>
      </Container>

      {/* Detected cells table */}
      <Container style={{ marginTop: 56 }}>
        <SectionEyebrow hint={lang === "pt" ? "ordem por intensidade" : "by intensity"}>
          {lang === "pt" ? "Células detectadas" : "Detected cells"}
        </SectionEyebrow>
        <div style={{
          background: '#fff', border: `0.5px solid ${AERO.hair}`, borderRadius: 6,
          overflow: 'hidden',
        }}>
          <CellTable lang={lang} />
        </div>
      </Container>
    </div>
  );
}

function MapBtn({ children, onClick }) {
  return (
    <button onClick={onClick} style={{
      width: 36, height: 36, borderRadius: 18,
      background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(8px)',
      border: '0.5px solid rgba(255,255,255,0.2)', color: '#fff',
      fontFamily: "'JetBrains Mono', monospace", fontSize: 16,
      cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>{children}</button>
  );
}

function Toggle({ label, checked: initial = false }) {
  const [on, setOn] = useState(initial);
  return (
    <label style={{
      display: 'flex', justifyContent: 'space-between', alignItems: 'center',
      padding: '4px 0', cursor: 'pointer', color: AERO.ink,
    }} onClick={() => setOn(!on)}>
      <span>{label}</span>
      <span style={{
        width: 28, height: 16, borderRadius: 8,
        background: on ? AERO.ink : AERO.hair, position: 'relative',
        transition: 'background .15s',
      }}>
        <span style={{
          position: 'absolute', top: 2, left: on ? 14 : 2, width: 12, height: 12,
          borderRadius: 6, background: '#fff', transition: 'left .15s',
        }} />
      </span>
    </label>
  );
}

// ─────────────────────────────────────────────────────────────────────
// BigRadarMap — MapLibre GL basemap + SVG overlay projected from lat/lon.
// All radar/satellite/lightning/wind layers live in the overlay.
// ─────────────────────────────────────────────────────────────────────

// Radar reflectivity cells — defined in geographic coordinates so they
// stay anchored to the ground as the map pans/zooms. Radii in km.
const RADAR_BLOBS = [
  // Strong core just W of Santa Maria
  { lat: -29.55, lon: -54.55, r: 55, fill: '#5a9c5e', op: 0.55 },
  { lat: -29.62, lon: -54.32, r: 38, fill: '#c5a23a', op: 0.7  },
  { lat: -29.68, lon: -54.14, r: 24, fill: '#c97c3a', op: 0.78 },
  { lat: -29.72, lon: -54.02, r: 14, fill: '#a8463a', op: 0.9  },
  { lat: -29.74, lon: -53.96, r: 6,  fill: '#7a2a6a', op: 0.95 },
  // Secondary cell E (over SC/PR border)
  { lat: -26.5,  lon: -50.0,  r: 40, fill: '#5a9c5e', op: 0.45 },
  { lat: -26.7,  lon: -49.8,  r: 22, fill: '#c5a23a', op: 0.55 },
  // Northern Brazil convection
  { lat: -5.0,   lon: -55.0,  r: 90, fill: '#5a9c5e', op: 0.4  },
  { lat: -4.5,   lon: -54.5,  r: 50, fill: '#c5a23a', op: 0.5  },
];

const LIGHTNING = [
  { lat: -29.65, lon: -54.10 }, { lat: -29.72, lon: -54.02 },
  { lat: -29.74, lon: -53.96 }, { lat: -29.55, lon: -54.55 },
  { lat: -29.62, lon: -54.30 }, { lat: -26.7,  lon: -49.8  },
];

const RADAR_SITES = [
  { name: 'SMR', lat: -29.69, lon: -53.84, focus: true },
  { name: 'POA', lat: -30.03, lon: -51.23 },
  { name: 'SAO', lat: -23.55, lon: -46.63 },
  { name: 'RIO', lat: -22.91, lon: -43.17 },
  { name: 'BSB', lat: -15.78, lon: -47.93 },
  { name: 'CWB', lat: -25.43, lon: -49.27 },
];

// GOES-16 imagery served by NOAA NWS ArcGIS ImageServer as
// reprojected-on-the-fly Web Mercator tiles.  This lets us drop the image
// directly on top of the MapLibre basemap with no projection mismatch.
// {bbox-epsg-3857} is replaced by MapLibre with the tile's bbox in 3857.
function goesTileUrl(product, time) {
  // ArcGIS service paths — these are the public NWS endpoints that serve
  // the GOES-East mosaics as time-aware ImageServers.
  const service = {
    geocolor: "GOES_East_GeoColor",
    "13":     "GOES_East_Band13",
    "08":     "GOES_East_Band8",
  }[product] || "GOES_East_GeoColor";
  const base = `https://mapservices.weather.noaa.gov/raster/rest/services/obs/${service}/ImageServer/exportImage`;
  const params = [
    "bbox={bbox-epsg-3857}",
    "bboxSR=3857",
    "imageSR=3857",
    "size=512,512",
    "format=png32",
    "transparent=true",
    "f=image",
  ];
  if (time) params.push("time=" + time);
  return `${base}?${params.join("&")}`;
}

const GOES_PRODUCTS = {
  geocolor: { label: "GeoColor" },
  "13":     { label: "Band 13 IR" },
  "08":     { label: "Band 08 WV" },
};

function BigRadarMap({ layer, frame, region, lang, satProduct, mapRef,
                      setCursor, setPixelDbz, setSatTimestamp }) {
  const containerRef = useRef(null);
  const [, setTick] = useState(0); // bump after map move/zoom to re-project overlay
  const [mapReady, setMapReady] = useState(false);

  // Init MapLibre once.
  useEffect(() => {
    if (!containerRef.current || !window.maplibregl) return;
    const mapStyle = {
      version: 8,
      sources: {
        'carto-dark': {
          type: 'raster',
          tiles: [
            'https://a.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}@2x.png',
            'https://b.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}@2x.png',
            'https://c.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}@2x.png',
            'https://d.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}@2x.png',
          ],
          tileSize: 256,
          attribution: '© OpenStreetMap · © CARTO',
        },
        'carto-labels': {
          type: 'raster',
          tiles: [
            'https://a.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}@2x.png',
            'https://b.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}@2x.png',
            'https://c.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}@2x.png',
            'https://d.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}@2x.png',
          ],
          tileSize: 256,
        },
      },
      layers: [
        { id: 'carto-dark',   type: 'raster', source: 'carto-dark'   },
        { id: 'carto-labels', type: 'raster', source: 'carto-labels' },
      ],
    };

    let map;
    try {
      map = new window.maplibregl.Map({
        container: containerRef.current,
        style: mapStyle,
        center: REGION_DEFS[region].center,
        zoom: REGION_DEFS[region].zoom,
        attributionControl: false,
        dragRotate: false,
      });
    } catch (e) {
      // WebGL unavailable — overlay still renders projection-less.
      console.warn("MapLibre init failed:", e);
      return;
    }
    map.addControl(new window.maplibregl.AttributionControl({ compact: true }), 'top-left');
    map.addControl(new window.maplibregl.ScaleControl({ maxWidth: 80, unit: 'metric' }), 'bottom-left');

    mapRef.current = map;

    const bump = () => setTick(t => (t + 1) % 1000000);
    map.on('move',  bump);
    map.on('zoom',  bump);
    map.on('load',  () => { setMapReady(true); bump(); });
    map.on('mousemove', (e) => {
      setCursor && setCursor({ lat: e.lngLat.lat, lng: e.lngLat.lng });
      // synthesize a dBZ value driven by distance to nearest blob
      let best = 0;
      for (const b of RADAR_BLOBS) {
        const dKm = haversine(e.lngLat.lat, e.lngLat.lng, b.lat, b.lon);
        if (dKm < b.r) best = Math.max(best, Math.round(60 - (dKm / b.r) * 30));
      }
      setPixelDbz && setPixelDbz(best || 0);
    });

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

  // FlyTo on region change.
  useEffect(() => {
    if (!mapRef.current) return;
    mapRef.current.flyTo({ ...REGION_DEFS[region], duration: 900 });
  }, [region]);

  // GOES-16 image is rendered as a plain <img> below the SVG overlay,
  // positioned via map.project() of the SSA corners.  Plate-carrée corners
  // along constant lat/lon edges project to an axis-aligned rectangle in
  // Web Mercator, so a simple left/top/width/height suffices.  Cross-origin
  // <img> works without CORS headers (unlike MapLibre's image source which
  // needs CORS to upload pixels to a WebGL texture).
  const [satTick, setSatTick] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setSatTick(t => t + 1), 5 * 60 * 1000);
    return () => clearInterval(id);
  }, []);
  useEffect(() => {
    const showSat = layer === "satellite";
    if (!showSat) { setSatTimestamp && setSatTimestamp(null); return; }
    const d = new Date();
    setSatTimestamp && setSatTimestamp(
      (lang === "pt" ? "loop " : "loop ") +
      d.toLocaleTimeString(lang === "pt" ? "pt-BR" : "en-US",
        { hour: "2-digit", minute: "2-digit" }) +
      " " + (window.WX.DATA.timezone_abbr || "GMT-3"));
  }, [layer, satProduct, satTick]);

  // Projection helpers (require map to be initialized)
  const map = mapRef.current;
  const project = (lon, lat) => {
    if (!map) return { x: -9999, y: -9999 };
    const p = map.project([lon, lat]);
    return { x: p.x, y: p.y };
  };
  const kmToPx = (lat) => {
    if (!map) return 1;
    const a = map.project([0, lat]);
    const b = map.project([0.01, lat]);
    const pxPerDeg = Math.hypot(b.x - a.x, b.y - a.y) / 0.01;
    const kmPerDeg = 111.32 * Math.cos(lat * Math.PI / 180);
    return pxPerDeg / kmPerDeg;
  };

  const seed = 1 + frame * 0.5;

  // Composite mode shows only radar + base map; satellite mode hides the
  // map and shows the GOES animated GIF in its native geostationary
  // projection (no reprojection artifacts).
  const goesActive = layer === "satellite";
  let goesImg = null;
  if (goesActive) {
    const prod = GOES_PRODUCTS[satProduct] || GOES_PRODUCTS.geocolor;
    // Stable src across re-renders; cache-busted only when satTick or
    // satProduct change, so the animated GIF restarts only on actual refresh.
    const cacheKey = satTick + "-" + satProduct;
    goesImg = (
      <div style={{
        position: "absolute", inset: 0, zIndex: 4,
        background: "#020407",
        display: "flex", alignItems: "center", justifyContent: "center",
        overflow: "hidden",
      }}>
        <img
          key={cacheKey}
          src={prod.gif + "?t=" + cacheKey}
          alt="GOES-16 South America loop"
          draggable={false}
          style={{
            maxWidth: "100%", maxHeight: "100%",
            objectFit: "contain", display: "block",
            pointerEvents: "none",
          }}
        />
      </div>
    );
  }

  return (
    <>
      <div ref={containerRef} style={{ position: 'absolute', inset: 0, zIndex: 1 }} />

      {/* GOES-16 sector image (cross-origin <img>, no CORS needed for display) */}
      {goesImg}

      {/* SVG overlay anchored to lat/lon via map.project.
          Hidden in satellite mode (where we show the native GIF instead). */}
      {!goesActive && <svg style={{
        position: 'absolute', inset: 0, width: '100%', height: '100%',
        pointerEvents: 'none', zIndex: 3,
      }}>
        {/* RADAR REFLECTIVITY / COMPOSITE */}
        {(layer === 'composite' || layer === 'reflectivity') && (
          <g style={{ mixBlendMode: 'screen', filter: 'blur(6px)' }}>
            {RADAR_BLOBS.map((b, i) => {
              const p = project(b.lon, b.lat);
              const r = Math.max(2, b.r * kmToPx(b.lat));
              return <circle key={i}
                cx={p.x + Math.sin(seed + i) * 4}
                cy={p.y + Math.cos(seed + i * 0.7) * 3}
                r={r} fill={b.fill} opacity={b.op} />;
            })}
          </g>
        )}

        {/* SATELLITE — real GOES-16 image is added to the map directly as
            an image source (see useEffect above). No SVG fallback needed. */}

        {/* LIGHTNING */}
        {layer === 'lightning' && (
          <g>
            {LIGHTNING.map((l, i) => {
              const p = project(l.lon, l.lat);
              const visible = (frame + i) % 3 === 0;
              return (
                <g key={i} opacity={visible ? 1 : 0.32}>
                  <circle cx={p.x} cy={p.y} r={24} fill="#fff5a8" opacity={0.28} />
                  <path d={`M ${p.x} ${p.y - 8} L ${p.x - 5} ${p.y + 5} L ${p.x + 3} ${p.y + 5} L ${p.x - 3} ${p.y + 18}`}
                    stroke="#fff5a8" strokeWidth="1.6" fill="none"
                    strokeLinejoin="round" strokeLinecap="round" />
                </g>
              );
            })}
          </g>
        )}

        {/* WIND — barb grid generated in lat/lon */}
        {layer === 'wind' && map && (
          <WindBarbs map={map} project={project} />
        )}

        {/* RANGE RINGS around focused site */}
        {layer === 'reflectivity' && (() => {
          const p = project(-53.84, -29.69);
          const ringKm = [60, 120, 180];
          return ringKm.map(r => (
            <circle key={r} cx={p.x} cy={p.y} r={r * kmToPx(-29.69)}
              fill="none" stroke="#fff" strokeOpacity={0.18}
              strokeDasharray="3 5" />
          ));
        })()}

        {/* RADAR SITES */}
        {RADAR_SITES.map((s, i) => {
          const p = project(s.lon, s.lat);
          return (
            <g key={i}>
              {s.focus && (
                <circle cx={p.x} cy={p.y} r={10}
                  fill="none" stroke="#fff" strokeWidth={1.2} opacity={0.85} />
              )}
              <circle cx={p.x} cy={p.y} r={3.5} fill="#fff" />
              <text x={p.x + 9} y={p.y - 7} fill="#fff" opacity={0.9}
                fontSize="10" fontFamily="'JetBrains Mono', monospace"
                letterSpacing="0.5">
                {s.name}
              </text>
            </g>
          );
        })}
      </svg>}
    </>
  );
}

// Wind layer — generate a regular geographic grid of barbs.
function WindBarbs({ map, project }) {
  const bounds = map.getBounds();
  const lat0 = bounds.getSouth(), lat1 = bounds.getNorth();
  const lon0 = bounds.getWest(),  lon1 = bounds.getEast();
  // 10x6 grid clipped to bounds; dir varies with longitude (decorative).
  const cols = 10, rows = 6;
  const barbs = [];
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      const lat = lat0 + ((r + 0.5) / rows) * (lat1 - lat0);
      const lon = lon0 + ((c + 0.5) / cols) * (lon1 - lon0);
      const p = project(lon, lat);
      const dir = (1.2 * Math.PI) + Math.sin(c * 0.6 + r * 0.4) * 0.35;
      const len = 22;
      const dx = Math.cos(dir) * len, dy = Math.sin(dir) * len;
      const tipX = p.x + dx / 2, tipY = p.y + dy / 2;
      const a = dir;
      barbs.push(
        <g key={`${r}-${c}`}>
          <line x1={p.x - dx/2} y1={p.y - dy/2} x2={tipX} y2={tipY}
            stroke="#bcd9f2" strokeWidth="1.1" strokeOpacity="0.55" />
          <polygon points={`${tipX},${tipY}
            ${tipX - Math.cos(a - 0.4) * 5},${tipY - Math.sin(a - 0.4) * 5}
            ${tipX - Math.cos(a + 0.4) * 5},${tipY - Math.sin(a + 0.4) * 5}`}
            fill="#bcd9f2" opacity="0.65" />
        </g>
      );
    }
  }
  return <g>{barbs}</g>;
}

// Great-circle distance in km
function haversine(lat1, lon1, lat2, lon2) {
  const toRad = (x) => x * Math.PI / 180;
  const R = 6371;
  const dLat = toRad(lat2 - lat1);
  const dLon = toRad(lon2 - lon1);
  const a = Math.sin(dLat / 2) ** 2 +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) ** 2;
  return 2 * R * Math.asin(Math.sqrt(a));
}

// ─────────────────────────────────────────────────────────────────────
// Cells table — unchanged
// ─────────────────────────────────────────────────────────────────────
function CellTable({ lang }) {
  const rows = [
    { id: "C-024", lat: "-29.78", lon: "-54.12", dbz: 52, h: 8.4, mov: "↗ NE 32 km/h", impact: lang === "pt" ? "Severo" : "Severe" },
    { id: "C-025", lat: "-29.42", lon: "-54.38", dbz: 44, h: 6.2, mov: "↗ NE 28 km/h", impact: lang === "pt" ? "Forte" : "Strong" },
    { id: "C-026", lat: "-30.12", lon: "-53.66", dbz: 38, h: 5.0, mov: "→ E 22 km/h", impact: lang === "pt" ? "Moderado" : "Moderate" },
    { id: "C-027", lat: "-28.94", lon: "-55.20", dbz: 32, h: 4.4, mov: "↗ NE 18 km/h", impact: lang === "pt" ? "Fraco" : "Weak" },
    { id: "C-028", lat: "-29.65", lon: "-52.10", dbz: 28, h: 3.8, mov: "→ E 14 km/h", impact: lang === "pt" ? "Fraco" : "Weak" },
  ];
  return (
    <>
      <div style={{
        display: 'grid', gridTemplateColumns: '90px 1.4fr 90px 70px 1.2fr 1fr',
        padding: '14px 24px',
        background: AERO.paperDeep,
        fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
        letterSpacing: 1.2, color: AERO.inkSoft, textTransform: 'uppercase',
      }}>
        <span>ID</span>
        <span>{lang === "pt" ? "Posição" : "Position"}</span>
        <span>dBZ</span>
        <span>Top km</span>
        <span>{lang === "pt" ? "Movimento" : "Movement"}</span>
        <span>{lang === "pt" ? "Impacto" : "Impact"}</span>
      </div>
      {rows.map((r, i) => (
        <div key={r.id} style={{
          display: 'grid', gridTemplateColumns: '90px 1.4fr 90px 70px 1.2fr 1fr',
          padding: '14px 24px',
          borderTop: `0.5px solid ${AERO.hair}`,
          fontFamily: "'JetBrains Mono', monospace", fontSize: 13,
          alignItems: 'center',
        }}>
          <span style={{ color: AERO.inkSoft, letterSpacing: 0.5 }}>{r.id}</span>
          <span style={{ color: AERO.ink }}>{r.lat}, {r.lon}</span>
          <span style={{
            color: r.dbz > 45 ? AERO.danger : r.dbz > 30 ? AERO.warm : AERO.ok,
            fontWeight: 600,
          }}>{r.dbz}</span>
          <span style={{ color: AERO.inkDim }}>{r.h}</span>
          <span style={{ color: AERO.inkDim }}>{r.mov}</span>
          <span style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            color: AERO.ink,
          }}>
            <span style={{
              width: 6, height: 6, borderRadius: 3,
              background: r.dbz > 45 ? AERO.danger : r.dbz > 30 ? AERO.warm : AERO.ok,
            }} />
            {r.impact}
          </span>
        </div>
      ))}
    </>
  );
}

Object.assign(window, { PageRadar });
