// Real-world choropleth map using Natural Earth TopoJSON.
// Loads via world-atlas CDN, projects with d3-geo (natural earth projection).
// Country ISO-A3 codes join to our fit-score dataset.

function WorldMap({ countries, selected, onSelect, onHover, hoverIso }) {
  const [features, setFeatures] = React.useState(null);
  const [zoom, setZoom] = React.useState(1);
  const [pan, setPan] = React.useState({ x: 0, y: 0 });
  const svgRef = React.useRef(null);
  const dragRef = React.useRef(null);

  // ISO numeric → ISO alpha-3 (subset used by our country list)
  const ISO_N_TO_A3 = {
    "004":"AFG","032":"ARG","036":"AUS","040":"AUT","056":"BEL","076":"BRA","100":"BGR","124":"CAN",
    "152":"CHL","156":"CHN","170":"COL","188":"CRI","191":"HRV","196":"CYP","203":"CZE","208":"DNK",
    "214":"DOM","218":"ECU","233":"EST","246":"FIN","250":"FRA","276":"DEU","288":"GHA","300":"GRC",
    "320":"GTM","348":"HUN","352":"ISL","356":"IND","360":"IDN","364":"IRN","368":"IRQ","372":"IRL",
    "376":"ISR","380":"ITA","384":"CIV","388":"JAM","392":"JPN","398":"KAZ","404":"KEN","410":"KOR",
    "428":"LVA","440":"LTU","458":"MYS","484":"MEX","504":"MAR","528":"NLD","554":"NZL","566":"NGA",
    "578":"NOR","586":"PAK","604":"PER","608":"PHL","616":"POL","620":"PRT","642":"ROU","643":"RUS",
    "682":"SAU","686":"SEN","688":"SRB","703":"SVK","705":"SVN","710":"ZAF","724":"ESP","752":"SWE",
    "756":"CHE","764":"THA","788":"TUN","792":"TUR","804":"UKR","818":"EGY","826":"GBR","840":"USA",
    "858":"URY","862":"VEN","704":"VNM",
  };

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      // Load topojson-client + world-atlas 110m
      if (!window.topojson) {
        await new Promise((ok, ko) => {
          const s = document.createElement("script");
          s.src = (window.__resources && window.__resources.topojsonClient) || "https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js";
          s.onload = ok; s.onerror = ko; document.head.appendChild(s);
        });
      }
      const res = await fetch((window.__resources && window.__resources.worldAtlas) || "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json");
      const topo = await res.json();
      if (cancelled) return;
      const fc = window.topojson.feature(topo, topo.objects.countries);
      setFeatures(fc.features);
    })();
    return () => { cancelled = true; };
  }, []);

  const byIso = React.useMemo(
    () => Object.fromEntries(countries.map(c => [c.iso, c])),
    [countries]
  );

  // Natural Earth projection — hand-rolled so we avoid loading d3.
  // Winkel Tripel-ish feel via simple equirectangular with tuned aspect.
  // Good-enough for a choropleth prototype.
  const WIDTH = 1000, HEIGHT = 520;
  const project = React.useCallback(([lon, lat]) => {
    // Natural Earth I approximation (Savric et al. polynomial)
    const x = lon * Math.PI / 180;
    const y = lat * Math.PI / 180;
    const l2 = y * y, l4 = l2 * l2, l6 = l4 * l2;
    const px = x * (0.8707 - 0.131979 * l2 + l4 * (-0.013791 + l6 * (0.003971 * l2 - 0.001529 * l4)));
    const py = y * (1.007226 + l2 * (0.015085 + l4 * (-0.044475 + 0.028874 * l2 - 0.005916 * l4)));
    // scale into viewBox
    const scale = 170;
    const cx = WIDTH / 2 + px * scale;
    const cy = HEIGHT / 2 - py * scale;
    return [cx, cy];
  }, []);

  const pathFor = React.useCallback((geom) => {
    if (!geom) return "";
    const rings = geom.type === "Polygon" ? [geom.coordinates] : geom.coordinates;
    let d = "";
    for (const poly of rings) {
      for (const ring of poly) {
        ring.forEach((pt, i) => {
          const [x, y] = project(pt);
          d += (i === 0 ? " M" : " L") + x.toFixed(1) + " " + y.toFixed(1);
        });
        d += " Z";
      }
    }
    return d;
  }, [project]);

  // Pan handlers
  const onMouseDown = (e) => {
    if (e.button !== 0) return;
    dragRef.current = { x: e.clientX, y: e.clientY, px: pan.x, py: pan.y, moved: false };
  };
  const onMouseMove = (e) => {
    if (!dragRef.current) return;
    const dx = e.clientX - dragRef.current.x;
    const dy = e.clientY - dragRef.current.y;
    if (Math.abs(dx) + Math.abs(dy) > 3) dragRef.current.moved = true;
    setPan({ x: dragRef.current.px + dx, y: dragRef.current.py + dy });
  };
  const onMouseUp = () => { dragRef.current = null; };
  const nudgeZoom = (f) => setZoom(z => Math.max(0.8, Math.min(6, z * f)));
  const reset = () => { setZoom(1); setPan({ x: 0, y: 0 }); };

  const onWheel = (e) => {
    e.preventDefault();
    nudgeZoom(e.deltaY < 0 ? 1.12 : 0.9);
  };

  return (
    <div style={{ position: "relative", width: "100%", height: "100%", overflow: "hidden", background: "var(--map-ocean)" }}
      onMouseMove={onMouseMove} onMouseUp={onMouseUp} onMouseLeave={onMouseUp}>
      <svg ref={svgRef}
        viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
        preserveAspectRatio="xMidYMid meet"
        onMouseDown={onMouseDown}
        onWheel={onWheel}
        style={{
          width: "100%", height: "100%", display: "block",
          cursor: dragRef.current ? "grabbing" : "grab",
          userSelect: "none",
        }}>
        <g transform={`translate(${pan.x} ${pan.y}) scale(${zoom})`} style={{ transformOrigin: "center" }}>
          {/* Ocean graticule */}
          <rect width={WIDTH} height={HEIGHT} fill="var(--map-ocean)"/>
          {!features && (
            <text x={WIDTH/2} y={HEIGHT/2} textAnchor="middle"
              fontFamily="var(--mono)" fontSize="12" fill="var(--ink-muted)">
              Loading world geometry…
            </text>
          )}
          {features && features.map((f, i) => {
            const iso = ISO_N_TO_A3[String(f.id).padStart(3, "0")];
            const country = iso ? byIso[iso] : null;
            const hasFit = !!country;
            const fill = hasFit ? fitColor(country.fit) : "var(--map-land)";
            const opacity = hasFit ? 0.35 + (country.fit / 100) * 0.55 : 1;
            const isSel = iso && iso === selected;
            const isHov = iso && iso === hoverIso;
            return (
              <path key={i}
                d={pathFor(f.geometry)}
                className={"geo-path " + (!hasFit ? "no-fit" : "") + (isSel ? " selected" : "")}
                style={{
                  fill, fillOpacity: isHov ? Math.min(1, opacity + 0.25) : opacity,
                }}
                onClick={(e) => {
                  if (dragRef.current?.moved) return;
                  if (!hasFit) return;
                  e.stopPropagation();
                  onSelect(iso);
                }}
                onMouseEnter={() => hasFit && onHover(iso)}
                onMouseLeave={() => onHover(null)}/>
            );
          })}

          {/* ISO labels on selected / hovered only, so we don't pollute */}
          {features && [selected, hoverIso].filter(Boolean).map(iso => {
            const country = byIso[iso];
            if (!country) return null;
            const f = features.find(x => ISO_N_TO_A3[String(x.id).padStart(3,"0")] === iso);
            if (!f) return null;
            // centroid approx
            const coords = [];
            const walk = (arr) => {
              if (typeof arr[0] === "number") coords.push(arr);
              else arr.forEach(walk);
            };
            walk(f.geometry.coordinates);
            const cx = coords.reduce((s, c) => s + c[0], 0) / coords.length;
            const cy = coords.reduce((s, c) => s + c[1], 0) / coords.length;
            const [px, py] = project([cx, cy]);
            return (
              <g key={iso} style={{ pointerEvents: "none" }}>
                <text x={px} y={py} textAnchor="middle" dominantBaseline="middle"
                  fontFamily="var(--mono)" fontSize={10 / zoom} fontWeight="700"
                  fill="var(--ink)" style={{ paintOrder: "stroke" }}
                  stroke="var(--panel)" strokeWidth={3 / zoom} strokeLinejoin="round">
                  {iso}
                </text>
              </g>
            );
          })}
        </g>
      </svg>

      {/* Zoom controls */}
      <div style={{
        position: "absolute", right: 16, bottom: 16,
        display: "flex", flexDirection: "column", gap: 4,
      }}>
        <button className="btn sm ghost" onClick={() => nudgeZoom(1.25)} style={{ background: "var(--panel)" }}>+</button>
        <button className="btn sm ghost" onClick={() => nudgeZoom(0.8)}  style={{ background: "var(--panel)" }}>−</button>
        <button className="btn sm ghost" onClick={reset}                style={{ background: "var(--panel)" }} title="Reset">⌂</button>
      </div>

      {/* Legend */}
      <div style={{
        position: "absolute", left: 16, bottom: 16,
        display: "flex", alignItems: "center", gap: 10,
        background: "var(--panel)",
        border: "1px solid var(--rule)",
        padding: "8px 12px",
        fontFamily: "var(--mono)",
        fontSize: 10,
        letterSpacing: "0.06em",
        textTransform: "uppercase",
        color: "var(--ink-muted)",
      }}>
        <span>Fit score</span>
        <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
          <span style={{ width: 10, height: 10, background: "var(--bad)", opacity: .6 }}/>Poor
        </span>
        <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
          <span style={{ width: 10, height: 10, background: "var(--mid)", opacity: .75 }}/>Mid
        </span>
        <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
          <span style={{ width: 10, height: 10, background: "var(--good)", opacity: .9 }}/>Strong
        </span>
      </div>

      <div style={{
        position: "absolute", right: 16, top: 16,
        fontFamily: "var(--mono)", fontSize: 10,
        color: "var(--ink-muted)", letterSpacing: "0.08em", textTransform: "uppercase",
        background: "var(--panel)", padding: "4px 8px", border: "1px solid var(--rule)",
      }}>
        Natural Earth · {countries.length} regions scored
      </div>
    </div>
  );
}

Object.assign(window, { WorldMap });
