/* Delivery globe. One dot per delivered programme, not one per country — the
   dots are placed on a deterministic ring around each country's centroid so a
   dense country reads as dense. Real Natural Earth geometry via d3 + topojson;
   hover is desktop-only detail, and the chip row below carries the same numbers
   for touch and reduced-motion. */
const GD = window.SX_DATA;
/* Self-hosted: the map must draw on a network that blocks third-party CDNs. */
const TOPO_URL = "assets/data/countries-110m.json";
const CENTROIDS = {
  Pakistan: [69.3, 30.4],
  "United States": [-98.5, 39.5],
  "United Kingdom": [-1.8, 53.2],
  Canada: [-106, 56],
  Japan: [138.2, 36.2],
  "United Arab Emirates": [54.3, 24.3],
  "Saudi Arabia": [45.1, 24.1],
  Ukraine: [31.2, 49.0],
};
const TIER_R = { flagship: 5.5, deployment: 4, delivery: 3 };
/* One hue per weight tier: accent for flagship (the thing to look at), field green
   for a live deployment, amber for a delivered-and-handed-over build. */
const TIER_C = { flagship: "var(--sx-accent)", deployment: "var(--sx-field)", delivery: "var(--sx-amber)" };
/* Natural Earth 110m spells some names differently from the project record. */
const GEO_ALIAS = { "United States of America": "United States", "USA": "United States", "United States": "United States", "United Kingdom": "United Kingdom", "England": "United Kingdom" };
const MAX_DOTS = 4;

/* Offsets are derived from the projection scale, never fixed px: at a 434px world a
   22px ring equals 15° of longitude and would land a Lahore programme in Laos. */
function ringOffset(i, base) {
  if (i === 0) return [0, 0];
  const a = ((i - 1) / (MAX_DOTS - 1)) * Math.PI * 1.4 - 0.5;
  return [Math.cos(a) * base, Math.sin(a) * base * 0.72];
}

const FLAG_BY_COUNTRY = Object.fromEntries((GD.countries || []).map((c) => [c.name, c.flag]));
const flagFor = (name) => FLAG_BY_COUNTRY[name] || "";
const SHORT_NAME = { "United Arab Emirates": "UAE", "United States": "United States" };

function DeliveryGlobe({ mobile, height }) {
  const wrapRef = React.useRef(null);
  const svgRef = React.useRef(null);
  const [geo, setGeo] = React.useState(null);
  const [width, setWidth] = React.useState(mobile ? 340 : 1000);
  const [hover, setHover] = React.useState(null);
  const [active, setActive] = React.useState(null);

  React.useEffect(() => {
    const el = wrapRef.current;
    if (!el) return;
    const ro = new ResizeObserver(() => setWidth(el.clientWidth || 1000));
    ro.observe(el);
    setWidth(el.clientWidth || 1000);
    return () => ro.disconnect();
  }, []);

  React.useEffect(() => {
    const d3 = window.d3, topojson = window.topojson;
    if (!d3 || !topojson) return;
    let dead = false;
    d3.json(TOPO_URL).then((topo) => {
      if (dead) return;
      setGeo(topojson.feature(topo, topo.objects.countries).features);
    }).catch(() => {});
    return () => { dead = true; };
  }, []);

  const located = GD.projects.filter((p) => p.country);
  /* Natural Earth is width-bound at this aspect, so the canvas height follows the
     measured width — a passed height only acts as a ceiling. */
  const h = Math.round(Math.min(height || 560, Math.max(mobile ? 170 : 240, width / 2.05)));
  const byCountry = {};
  located.forEach((p) => { (byCountry[p.country] = byCountry[p.country] || []).push(p); });

  const d3 = window.d3;
  const projection = d3 && d3.geoNaturalEarth1().fitSize([width, h], { type: "Sphere" });
  const path = d3 && d3.geoPath(projection);
  const delivered = new Set(Object.keys(byCountry));

  const dots = [];
  const overflow = [];
  const dotBase = Math.max(4.5, width * 0.0085);
  if (projection) {
    Object.entries(byCountry).forEach(([country, list]) => {
      const c = CENTROIDS[country];
      if (!c) return;
      const [cx, cy] = projection(c);
      const rank = { flagship: 0, deployment: 1, delivery: 2 };
      const byRank = list.slice().sort((a, b) => rank[a.tier] - rank[b.tier]);
      /* Take the heaviest first, but keep one of each tier present so every legend
         entry resolves to a dot the reader can actually find. */
      const order = [];
      ["flagship", "deployment", "delivery"].forEach((t) => { const f = byRank.find((p) => p.tier === t); if (f) order.push(f); });
      byRank.forEach((p) => { if (!order.includes(p)) order.push(p); });
      order.slice(0, MAX_DOTS).forEach((p, i) => {
        const [dx, dy] = ringOffset(i, dotBase);
        dots.push({ p, x: cx + dx, y: cy + dy, r: TIER_R[p.tier], c: TIER_C[p.tier] || "var(--sx-accent)" });
      });
      if (order.length > MAX_DOTS) overflow.push({ country, n: order.length - MAX_DOTS, x: cx + dotBase * 1.6, y: cy - dotBase * 1.4 });
    });
  }

  return (
    <div ref={wrapRef} style={{ position: "relative" }}>
      <svg ref={svgRef} viewBox={`0 0 ${width} ${h}`} width="100%" height={h} role="img"
        aria-label={`${located.length} delivered programmes plotted across ${delivered.size} countries`}
        onPointerLeave={() => setHover(null)}>
        {path && (
          <>
            <path d={path({ type: "Sphere" })} fill="var(--surface-card)" stroke="var(--border-default)" />
            {(geo || []).map((f, i) => {
              const on = delivered.has(GEO_ALIAS[f.properties.name] || f.properties.name);
              return <path key={i} d={path(f)} fill={on ? "color-mix(in oklab, var(--sx-accent) 22%, var(--surface-raised))" : "var(--surface-raised)"} stroke="var(--border-default)" strokeWidth="0.5" />;
            })}
            {d3.range(-60, 90, 30).map((lat) => (
              <path key={lat} d={path(d3.geoGraticule().step([0, 30])())} fill="none" stroke="var(--border-default)" strokeWidth="0.4" opacity="0.5" />
            )).slice(0, 1)}
            {dots.map((d, i) => {
              const dim = hover && hover.p.slug !== d.p.slug;
              return (
                <g key={d.p.slug + i}>
                  <circle cx={d.x} cy={d.y} r={d.r + 5} fill="transparent" style={{ cursor: "pointer" }}
                    onPointerEnter={() => setHover(d)} onClick={() => setActive(d.p)} />
                  <circle cx={d.x} cy={d.y} r={d.r} fill={d.c} stroke="var(--bg-page)" strokeWidth="1" opacity={dim ? 0.3 : 0.95} style={{ pointerEvents: "none", transition: "opacity 150ms linear" }} />
                  {hover && hover.p.slug === d.p.slug && (
                    <circle cx={d.x} cy={d.y} r={d.r + 6} fill="none" stroke={d.c} strokeWidth="1" opacity="0.7" style={{ pointerEvents: "none" }} />
                  )}
                </g>
              );
            })}
            {overflow.map((o) => (
              <g key={o.country}>
                <rect x={o.x} y={o.y - 8} width={o.n > 9 ? 26 : 20} height={15} rx={4} fill="var(--surface-overlay)" stroke="var(--sx-accent-line)" />
                <text x={o.x + (o.n > 9 ? 13 : 10)} y={o.y + 3} textAnchor="middle" fill="var(--text-accent)" style={{ font: "500 10px var(--font-mono)" }}>+{o.n}</text>
              </g>
            ))}
          </>
        )}
      </svg>

      {hover && !mobile && (
        <div style={{ position: "absolute", left: Math.min(Math.max(hover.x + 14, 8), width - 260), top: Math.max(hover.y - 10, 8), width: 240, padding: "12px 14px", background: "var(--surface-overlay)", border: "1px solid var(--border-strong)", borderRadius: "var(--r-card)", boxShadow: "var(--shadow-overlay)", pointerEvents: "none" }}>
          <p className="sx-micro" style={{ display: "flex", alignItems: "center", gap: 8, color: TIER_C[hover.p.tier] || "var(--text-accent)" }}>
            {flagFor(hover.p.country) && <img src={flagFor(hover.p.country)} alt="" width="18" height="13" style={{ display: "block", borderRadius: 2, objectFit: "cover" }} />}
            <span style={{ whiteSpace: "nowrap" }}>{hover.p.tier} · {hover.p.country.toLowerCase()}</span>
          </p>
          <p className="sx-small" style={{ marginTop: 8, color: "var(--text-strong)" }}>{hover.p.client}</p>
          <p className="sx-caption" style={{ marginTop: 6, color: "var(--text-body)", textTransform: "none" }}>{hover.p.title}</p>
          <p className="sx-caption" style={{ marginTop: 8, color: "var(--text-quiet)", textTransform: "none" }}>{hover.p.sector.toLowerCase()} · {hover.p.year || hover.p.era}</p>
        </div>
      )}

      <ul style={{ listStyle: "none", margin: "22px 0 0", padding: 0, display: "grid", gap: 1, gridTemplateColumns: mobile ? "repeat(2, minmax(0,1fr))" : "repeat(4, minmax(0,1fr))", background: "var(--border-default)", border: "1px solid var(--border-default)", borderRadius: "var(--r-card)", overflow: "hidden" }}>
        {GD.countries.map((c) => (
          <li key={c.name} style={{ background: "var(--bg-page)", padding: mobile ? "12px 10px" : "14px 16px", display: "grid", gridTemplateRows: "auto 1fr auto", justifyItems: "center", gap: 7, textAlign: "center" }}>
            {c.flag && <img src={c.flag} alt="" width="22" height="16" style={{ display: "block", borderRadius: 2, objectFit: "cover" }} />}
            <span className="sx-caption" style={{ color: "var(--text-body)", textTransform: "none", lineHeight: 1.25, alignSelf: "start" }}>{SHORT_NAME[c.name] || c.name}</span>
            <span className="sx-num" style={{ color: "var(--text-strong)", fontSize: mobile ? 15 : 17, lineHeight: 1 }}>{c.total}</span>
          </li>
        ))}
      </ul>
      <div style={{ marginTop: 14, display: "flex", gap: 18, flexWrap: "wrap", alignItems: "center" }}>
        {[["flagship", 5.5, TIER_C.flagship], ["deployment", 4, TIER_C.deployment], ["delivery", 3, TIER_C.delivery]].map(([label, r, col]) => (
          <span key={label} className="sx-caption" style={{ display: "inline-flex", alignItems: "center", gap: 7, color: "var(--text-quiet)" }}>
            <svg width="14" height="14" aria-hidden><circle cx="7" cy="7" r={r} fill={col} /></svg>{label}
          </span>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { DeliveryGlobe });
