// Interactive 3D globe — orthographic projection via d3-geo
// Drag to rotate; auto-rotates when idle; click a country to see its data.

// Map country names from our dataset → topojson numeric ISO IDs.
const COUNTRY_ISO = {
  ARGENTINA: '032', BRAZIL: '076', CHINA: '156', 'EL SALVADOR': '222',
  'HONG KONG': '344', INDIA: '356', INDONESIA: '360', ITALY: '380',
  JAPAN: '392', KENYA: '404', LEBANON: '422', MEXICO: '484',
  NIGERIA: '566', PHILIPPINES: '608', POLAND: '616', RUSSIA: '643',
  'SAUDI ARABIA': '682', 'SOUTH AFRICA': '710', 'SOUTH KOREA': '410',
  SWITZERLAND: '756', TURKEY: '792', UAE: '784', UKRAINE: '804',
  US: '840', VENEZUELA: '862',
};

// Approx centroids for marker fallback (long, lat) — used for visibility,
// especially for small countries (Hong Kong, El Salvador, Lebanon, UAE).
const COUNTRY_CENTROID = {
  ARGENTINA: [-64, -34], BRAZIL: [-53, -10], CHINA: [104, 35], 'EL SALVADOR': [-88.9, 13.8],
  'HONG KONG': [114.2, 22.3], INDIA: [78, 21], INDONESIA: [118, -2], ITALY: [12.5, 42],
  JAPAN: [138, 36], KENYA: [37.9, 0.2], LEBANON: [35.9, 33.85], MEXICO: [-102, 23.6],
  NIGERIA: [8, 9.1], PHILIPPINES: [122, 12.9], POLAND: [19, 51.9], RUSSIA: [97, 61],
  'SAUDI ARABIA': [45, 23.9], 'SOUTH AFRICA': [25, -29], 'SOUTH KOREA': [127.8, 36.5],
  SWITZERLAND: [8, 46.8], TURKEY: [35, 39], UAE: [54, 24], UKRAINE: [31, 49],
  US: [-99, 39.5], VENEZUELA: [-66, 8],
};

function GlobePage({ data, embedded }) {
  const rankRef = React.useRef(null);
  const { countries, perCountry } = data.summary;
  const [metric, setMetric] = React.useState('awareness');
  const [hovered, setHovered] = React.useState(null);
  const [selected, setSelected] = React.useState(null);
  const [tip, setTip] = React.useState(null);
  const [topo, setTopo] = React.useState(null);
  const [d3, setD3] = React.useState(null);
  const [topoLib, setTopoLib] = React.useState(null);
  const [rotation, setRotation] = React.useState([0, -15, 0]);
  const autoRotate = React.useRef(true);
  const draggingRef = React.useRef(false);
  const lastInteractRef = React.useRef(performance.now());

  const metrics = MAP_METRICS;

  // Lookup map: ISO code → country in our data
  const isoToCountry = React.useMemo(() => {
    const m = {};
    for (const c of countries) if (COUNTRY_ISO[c]) m[COUNTRY_ISO[c]] = c;
    return m;
  }, [countries]);

  const valFor = (countryName) => {
    if (!countryName) return null;
    const row = perCountry[countries.indexOf(countryName)];
    if (!row) return null;
    return row[metric];
  };

  // load d3-geo + topojson + world atlas
  React.useEffect(() => {
    function loadScript(src) {
      return new Promise((res, rej) => {
        if (document.querySelector(`script[src="${src}"]`)) { res(); return; }
        const s = document.createElement('script');
        s.src = src; s.async = true;
        s.onload = res; s.onerror = rej;
        document.head.appendChild(s);
      });
    }
    (async () => {
      await loadScript('https://unpkg.com/d3@7.9.0/dist/d3.min.js');
      await loadScript('https://unpkg.com/topojson-client@3.1.0/dist/topojson-client.min.js');
      setD3(window.d3);
      setTopoLib(window.topojson);
      const r = await fetch('https://unpkg.com/world-atlas@2.0.2/countries-110m.json');
      const j = await r.json();
      setTopo(j);
    })();
  }, []);

  // Auto-rotate loop
  React.useEffect(() => {
    let raf;
    const tick = () => {
      const idle = performance.now() - lastInteractRef.current > 1500;
      if (autoRotate.current && idle && !draggingRef.current && !selected) {
        setRotation(([lon, lat, roll]) => [lon + 0.15, lat, roll]);
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [selected]);

  // Drag to rotate
  const stageRef = React.useRef(null);
  const globeCardRef = React.useRef(null);
  const dragStartRef = React.useRef(null); // { x, y, rot }
  React.useEffect(() => {
    const el = stageRef.current;
    if (!el) return;
    const onDown = (e) => {
      draggingRef.current = true;
      lastInteractRef.current = performance.now();
      autoRotate.current = false;
      const pt = e.touches ? e.touches[0] : e;
      dragStartRef.current = { x: pt.clientX, y: pt.clientY, rot: null };
      // capture current rotation lazily on first move via setRotation callback
      setRotation((r) => { dragStartRef.current.rot = r; return r; });
    };
    const onMove = (e) => {
      if (!draggingRef.current || !dragStartRef.current?.rot) return;
      const pt = e.touches ? e.touches[0] : e;
      const dx = pt.clientX - dragStartRef.current.x;
      const dy = pt.clientY - dragStartRef.current.y;
      const w = el.clientWidth || 600;
      const k = 220 / w; // sensitivity: ~220deg per full-width drag
      const startRot = dragStartRef.current.rot;
      setRotation([
        startRot[0] + dx * k,
        Math.max(-85, Math.min(85, startRot[1] - dy * k)),
        startRot[2],
      ]);
      lastInteractRef.current = performance.now();
      if (e.cancelable) e.preventDefault();
    };
    const onUp = () => {
      draggingRef.current = false;
      dragStartRef.current = null;
    };
    el.addEventListener('mousedown', onDown);
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
    el.addEventListener('touchstart', onDown, { passive: true });
    window.addEventListener('touchmove', onMove, { passive: false });
    window.addEventListener('touchend', onUp);
    return () => {
      el.removeEventListener('mousedown', onDown);
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
      el.removeEventListener('touchstart', onDown);
      window.removeEventListener('touchmove', onMove);
      window.removeEventListener('touchend', onUp);
    };
  }, []);

  // When a country is selected, smoothly rotate to its centroid
  React.useEffect(() => {
    if (!selected || !COUNTRY_CENTROID[selected]) return;
    const [lon, lat] = COUNTRY_CENTROID[selected];
    let raf;
    const start = rotation;
    const end = [-lon, -lat, 0];
    const t0 = performance.now();
    const dur = 900;
    const tick = (t) => {
      const p = Math.min(1, (t - t0) / dur);
      const ease = 1 - Math.pow(1 - p, 3);
      const lerpAngle = (a, b) => {
        let diff = ((b - a + 540) % 360) - 180;
        return a + diff * ease;
      };
      setRotation([
        lerpAngle(start[0], end[0]),
        start[1] + (end[1] - start[1]) * ease,
        0,
      ]);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [selected]);

  // Color ramp — use brand color, t=0 is surface-2, t=1 is brand at full saturation
  const validVals = perCountry.map(r => r[metric]).filter(v => typeof v === 'number');
  const maxV = Math.max(...validVals, 0.01);

  function colorFor(countryName) {
    const v = valFor(countryName);
    if (v == null) return null;
    const t = Math.min(1, v / maxV);
    return `color-mix(in srgb, var(--brand) ${Math.round(t * 90)}%, var(--surface-2))`;
  }

  // Render the globe svg
  const W = 720, H = 720;

  let geo = null, sphere = null, graticule = null, path = null, projection = null;
  if (d3 && topo) {
    projection = d3.geoOrthographic()
      .scale(W * 0.46)
      .translate([W/2, H/2])
      .rotate(rotation)
      .clipAngle(90);
    path = d3.geoPath(projection);
    geo = topoLib.feature(topo, topo.objects.countries);
    sphere = { type: 'Sphere' };
    graticule = d3.geoGraticule10();
  }

  const onCountryClick = (countryName) => {
    if (!countryName) return;
    autoRotate.current = false;
    setSelected(countryName === selected ? null : countryName);
  };

  const showTip = (e, countryName) => {
    if (!countryName) { setTip(null); return; }
    const rect = stageRef.current.getBoundingClientRect();
    const v = valFor(countryName);
    setTip({
      x: e.clientX - rect.left,
      y: e.clientY - rect.top,
      country: countryName,
      value: v,
    });
  };

  return (
    <section className="section" style={{ paddingTop: embedded ? 8 : 56 }}>
      <div className="container">
        {!embedded && <div className="eyebrow sans" style={{ marginBottom: 8 }}>Interactive Globe · 25 countries</div>}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', alignItems: 'end', gap: 32, marginBottom: 28 }}>
          <h1 style={{ maxWidth: '22ch' }}>{metrics.find(m => m.k === metric).l}<span style={{ display: 'block', fontFamily: 'JetBrains Mono, monospace', fontSize: 11, fontWeight: 400, textTransform: 'uppercase', letterSpacing: '.12em', color: 'var(--muted)', marginTop: 10 }}>{metrics.find(m => m.k === metric).base}</span></h1>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, maxWidth: 520, justifyContent: 'flex-end' }}>
            {metrics.map(m =>
              <button key={m.k} onClick={() => { setMetric(m.k); autoRotate.current = true; setSelected(null); }}
                style={{
                  background: m.k === metric ? 'var(--ink)' : 'transparent',
                  color: m.k === metric ? 'var(--paper)' : 'var(--ink-2)',
                  border: '1px solid var(--rule)', padding: '6px 10px',
                  fontFamily: 'JetBrains Mono, monospace', fontSize: 10,
                  textTransform: 'uppercase', letterSpacing: '.12em', cursor: 'pointer',
                }}>{m.l}</button>
            )}
            <ExportButton targetRef={globeCardRef} filename="cbai-globe.png" style={{ alignSelf: 'center' }}/>
          </div>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1.4fr .6fr', gap: 32, alignItems: 'start' }}>
          {/* Globe stage */}
          <div className="chart-card" style={{ padding: 24 }} ref={globeCardRef}>
            <div className="globe-stage" ref={stageRef}>
              {!d3 || !topo ?
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', color: 'var(--muted)', fontFamily: 'Inter Tight, sans-serif' }}>
                  Loading globe…
                </div>
                :
                <svg viewBox={`0 0 ${W} ${H}`} onMouseMove={e => {
                  if (hovered) showTip(e, hovered);
                }}>
                  {/* Sphere */}
                  <path className="sphere" d={path(sphere)} />
                  {/* Graticule */}
                  <path className="graticule" d={path(graticule)} />
                  {/* Countries */}
                  {geo.features.map((feat, fi) => {
                    const iso = String(feat.id).padStart(3, '0');
                    const countryName = isoToCountry[iso];
                    const inStudy = !!countryName;
                    const fill = inStudy ? colorFor(countryName) : null;
                    const isHovered = hovered === countryName;
                    const isSelected = selected === countryName;
                    return (
                      <path key={feat.id || fi}
                        className={"country" + (inStudy ? " in-study" : "") + (isHovered ? " hovered" : "") + (isSelected ? " selected" : "")}
                        d={path(feat)}
                        style={inStudy && fill ? { fill } : {}}
                        onMouseEnter={inStudy ? (e) => { setHovered(countryName); showTip(e, countryName); autoRotate.current = false; } : null}
                        onMouseLeave={inStudy ? () => { setHovered(null); setTip(null); autoRotate.current = true; } : null}
                        onClick={inStudy ? () => onCountryClick(countryName) : null}
                      />
                    );
                  })}
                  {/* Country markers — small dots for visibility, especially for small countries */}
                  {Object.entries(COUNTRY_CENTROID).map(([name, ll]) => {
                    if (!countries.includes(name)) return null;
                    const proj = projection(ll);
                    if (!proj) return null;
                    // Check if point is on visible side
                    const lonRad = (ll[0] * Math.PI) / 180;
                    const latRad = (ll[1] * Math.PI) / 180;
                    const rotLon = (-rotation[0] * Math.PI) / 180;
                    const rotLat = (-rotation[1] * Math.PI) / 180;
                    const dotProd = Math.cos(latRad) * Math.cos(lonRad - rotLon) * Math.cos(rotLat) + Math.sin(latRad) * Math.sin(rotLat);
                    if (dotProd < 0) return null;
                    const v = valFor(name);
                    const isHK = name === 'HONG KONG';
                    return (
                      <g key={name}>
                        <circle cx={proj[0]} cy={proj[1]} r={isHK ? 5 : 3}
                          fill={v == null ? 'transparent' : 'var(--fg)'}
                          stroke={v == null ? 'var(--muted)' : 'var(--paper)'}
                          strokeWidth={v == null ? 1.2 : 1.4}
                          strokeDasharray={v == null ? '2 2' : 'none'}
                          style={{ pointerEvents: 'all', cursor: 'pointer' }}
                          onMouseEnter={(e) => { setHovered(name); showTip(e, name); autoRotate.current = false; }}
                          onMouseLeave={() => { setHovered(null); setTip(null); autoRotate.current = true; }}
                          onClick={() => onCountryClick(name)}
                        />
                      </g>
                    );
                  })}
                </svg>
              }
              {tip &&
                <div className="globe-tip" style={{ left: tip.x, top: tip.y - 12 }}>
                  <div style={{ fontWeight: 600 }}>{titleCase(tip.country)}</div>
                  <div className="tv">
                    {metrics.find(m => m.k === metric).l}
                    {' — '}
                    {tip.value == null ? '— (no data)' : fmtPct(tip.value, 0)}
                  </div>
                </div>
              }
            </div>

            {/* Legend / colorbar */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginTop: 22, justifyContent: 'center', fontFamily: 'JetBrains Mono, monospace', fontSize: 10, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.12em' }}>
              <span>0%</span>
              <div style={{ width: 220, height: 10, background: `linear-gradient(to right, var(--surface-2), var(--brand))`, border: '1px solid var(--rule)' }} />
              <span>{Math.round(maxV * 100)}%</span>
            </div>
            <p style={{ fontFamily: 'Inter Tight, sans-serif', fontSize: 12, color: 'var(--muted)', textAlign: 'center', marginTop: 14 }}>
              Drag to rotate · click a country to focus · the globe auto-rotates when idle
            </p>
          </div>

          {/* Side panel — selected country detail or instructions */}
          <div className="chart-card" style={{ padding: '28px 26px', alignSelf: 'start' }}>
            {selected ?
              <SelectedCountryPanel
                country={selected}
                data={data}
                metric={metric}
                onClose={() => { setSelected(null); autoRotate.current = true; }}
              />
              :
              <DefaultPanel countries={countries} perCountry={perCountry} metric={metric}
                metricLabel={metrics.find(m => m.k === metric).l}
                onSelect={(c) => { autoRotate.current = false; setSelected(c); }} />
            }
          </div>
        </div>

        <div style={{ marginTop: 48 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 16, marginBottom: 14 }}>
            <div className="eyebrow sans">Ranking · {metrics.find(m => m.k === metric).l}</div>
            <ExportButton targetRef={rankRef} filename="cbai-ranking.png"/>
          </div>
          <div className="chart-card" style={{ padding: 16 }} ref={rankRef}>
            {[...perCountry].sort((a, b) => (b[metric] || 0) - (a[metric] || 0)).map((r, i) =>
              <div key={r.name} className="bar-row">
                <div className="lbl sans"><span style={{ display: 'inline-block', width: 24, color: 'var(--muted)', fontFamily: 'JetBrains Mono, monospace', fontSize: 12 }}>{i + 1}.</span>{titleCase(r.name)}</div>
                <div className="bar-track"><div className="bar-fill" style={{ width: ((r[metric] || 0) / maxV * 100) + '%' }}/></div>
                <div className="val mono">{fmtPct(r[metric], 0)}</div>
              </div>
            )}
          </div>
        </div>
      </div>
    </section>
  );
}

function DefaultPanel({ countries, perCountry, metric, metricLabel, onSelect }) {
  const rows = countries.map((c, i) => ({ name: c, v: perCountry[i][metric] }))
    .filter(r => typeof r.v === 'number')
    .sort((a, b) => b.v - a.v);
  const top = rows.slice(0, 5);
  const bottom = rows.slice(-5).reverse();
  return (
    <>
      <div className="eyebrow sans" style={{ marginBottom: 8 }}>Ranked by current metric</div>
      <h3 style={{ fontFamily: 'Source Serif 4, serif', fontSize: 18, fontWeight: 500, marginBottom: 16, textTransform: 'none', letterSpacing: '-0.01em' }}>
        {metricLabel}
      </h3>
      <div className="eyebrow sans" style={{ marginBottom: 8, color: 'var(--brand)' }}>Top 5</div>
      <RankedList rows={top} onSelect={onSelect} />
      <div className="eyebrow sans" style={{ marginBottom: 8, marginTop: 22 }}>Bottom 5</div>
      <RankedList rows={bottom} onSelect={onSelect} muted />
      <p style={{ fontFamily: 'Inter Tight, sans-serif', fontSize: 12, color: 'var(--muted)', marginTop: 20, lineHeight: 1.4 }}>
        Click any country in the list — or on the globe — to focus the camera and see its full demographic breakdown.
      </p>
    </>
  );
}

function RankedList({ rows, onSelect, muted }) {
  return (
    <div style={{ display: 'grid', gap: 4 }}>
      {rows.map((r, i) =>
        <button key={r.name} onClick={() => onSelect(r.name)} style={{
          background: 'transparent', border: 0, cursor: 'pointer', textAlign: 'left',
          padding: '6px 0', display: 'grid', gridTemplateColumns: '24px 1fr 60px', gap: 8, alignItems: 'baseline',
          color: 'inherit',
        }}>
          <span className="mono" style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10, color: 'var(--muted)' }}>{(i + 1).toString().padStart(2, '0')}</span>
          <span style={{ fontFamily: 'Source Serif 4, serif', fontSize: 14, color: muted ? 'var(--ink-2)' : 'var(--fg)' }}>{titleCase(r.name)}</span>
          <span className="mono" style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 12, textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{fmtPct(r.v, 0)}</span>
        </button>
      )}
    </div>
  );
}

function SelectedCountryPanel({ country, data, metric, onClose }) {
  const { countries, perCountry } = data.summary;
  const row = perCountry[countries.indexOf(country)];
  const all = [
    { k: 'awareness', l: 'Bitcoin Awareness' },
    { k: 'ownEver', l: 'Have Ever Owned' },
    { k: 'ownCurrent', l: 'Currently Own' },
    { k: 'financialFreedom', l: 'Increases Financial Freedom' },
    { k: 'protectsPrivacy', l: 'Protects Privacy' },
    { k: 'confusing', l: 'Is Confusing' },
    { k: 'proneToFraud', l: 'Prone to Fraud' },
    { k: 'knows21M', l: 'Knows 21M Supply Cap' },
  ];
  return (
    <>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 14 }}>
        <div>
          <div className="eyebrow sans" style={{ marginBottom: 6 }}>Selected country</div>
          <h2 style={{ fontFamily: 'Source Serif 4, serif', fontSize: 26, marginBottom: 0, fontWeight: 500, letterSpacing: '-0.01em' }}>{titleCase(country)}</h2>
        </div>
        <button onClick={onClose} style={{
          background: 'transparent', border: '1px solid var(--rule)', color: 'var(--ink-2)',
          padding: '4px 10px', fontFamily: 'JetBrains Mono, monospace', fontSize: 10,
          textTransform: 'uppercase', letterSpacing: '.12em', cursor: 'pointer',
        }}>Close ×</button>
      </div>
      <div className="eyebrow sans" style={{ marginBottom: 10 }}>n = {fmtNum(row.n)} respondents</div>

      <div style={{ display: 'grid', gap: 8 }}>
        {all.map(m => {
          const v = row[m.k];
          const isCurrent = m.k === metric;
          return (
            <div key={m.k} style={{
              display: 'grid', gridTemplateColumns: '1fr 56px', alignItems: 'center', gap: 8,
              padding: '8px 10px', background: isCurrent ? 'var(--surface-2)' : 'transparent',
              borderLeft: isCurrent ? '2px solid var(--brand)' : '2px solid transparent',
            }}>
              <div style={{ fontFamily: 'Inter Tight, sans-serif', fontSize: 13, color: isCurrent ? 'var(--fg)' : 'var(--ink-2)' }}>{m.l}</div>
              <div className="mono" style={{
                fontFamily: 'JetBrains Mono, monospace', fontSize: 13, textAlign: 'right',
                fontVariantNumeric: 'tabular-nums',
                color: v == null ? 'var(--muted)' : (isCurrent ? 'var(--brand)' : 'var(--fg)'),
                fontWeight: isCurrent ? 600 : 400,
              }}>{v == null ? '—' : fmtPct(v, 0)}</div>
            </div>
          );
        })}
      </div>

      {(country === 'CHINA' || country === 'HONG KONG') &&
        <p style={{ fontFamily: 'Inter Tight, sans-serif', fontSize: 12, color: 'var(--muted)', marginTop: 16, padding: '10px 12px', background: 'var(--surface-2)', borderLeft: '2px solid var(--rule)', lineHeight: 1.45 }}>
          <strong>Note:</strong> Current bitcoin-ownership responses were not collected in {titleCase(country)} due to regulatory constraints. "Ever owned" reflects past ownership only.
        </p>
      }
    </>
  );
}

Object.assign(window, { GlobePage, COUNTRY_ISO, COUNTRY_CENTROID });
