/* global React */
// ============================================================
// SPEI Global — asymmetric two-column section.
//
// LEFT  (55%) : badge, headline, paragraph, 2×2 stats grid.
// RIGHT (45%) : large interactive 3D globe with a real
//               orthographic projection of the world.
//               Continents are loaded from world-atlas
//               (TopoJSON) and rendered via d3-geo.
//               Connection nodes and transfer arcs are
//               positioned by [lon, lat] and projected each
//               frame, so they ride along with the rotation.
// ============================================================

const { useEffect, useRef, useState } = React;

// ------------------------------------------------------------
// Stats data
// ------------------------------------------------------------
const SPEI_STATS = [
{
  label: "Países conectados",
  value: "70+",
  Icon: (p) =>
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}>
        <circle cx="12" cy="12" r="9" />
        <path d="M3 12h18" />
        <path d="M12 3a14 14 0 0 1 0 18 14 14 0 0 1 0-18" />
      </svg>

},
{
  label: "Velocidad de envío",
  value: "Mismo día",
  Icon: (p) =>
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}>
        <circle cx="12" cy="12" r="9" />
        <path d="M12 7v5l3.5 2" />
      </svg>

},
{
  label: "Comisiones",
  value: "$0",
  Icon: (p) =>
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}>
        <circle cx="12" cy="12" r="9" />
        <path d="M15 8.5C14.5 7.5 13.3 7 12 7c-2 0-3.2 1.1-3.2 2.4 0 1.4 1.2 2 3.2 2.4 2 0.4 3.2 1 3.2 2.4 0 1.3-1.2 2.4-3.2 2.4-1.3 0-2.5-0.5-3-1.5" />
        <path d="M12 5.5v13" />
      </svg>

},
{
  label: "Clientes activos",
  value: "+100",
  Icon: (p) =>
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}>
        <path d="M4 21V8l8-5 8 5v13" />
        <path d="M9 21v-6h6v6" />
        <path d="M8 11h.01M12 11h.01M16 11h.01M8 15h.01M16 15h.01" />
      </svg>

}];


// ------------------------------------------------------------
// Connection nodes — real cities, defined as [lon, lat].
// Spread across the requested regions: Norteamérica, Europa,
// Asia, Sudamérica, plus a couple extra hubs for visual rhythm.
// ------------------------------------------------------------
const SPEI_NODES = [
{ id: "mex", name: "Ciudad de México", coord: [-99.13, 19.43] },
{ id: "nyc", name: "Nueva York", coord: [-74.01, 40.71] },
{ id: "sao", name: "São Paulo", coord: [-46.63, -23.55] },
{ id: "lon", name: "Londres", coord: [-0.13, 51.51] },
{ id: "mad", name: "Madrid", coord: [-3.70, 40.42] },
{ id: "dub", name: "Dubái", coord: [55.27, 25.20] },
{ id: "sin", name: "Singapur", coord: [103.82, 1.35] },
{ id: "hkg", name: "Hong Kong", coord: [114.17, 22.32] },
{ id: "tyo", name: "Tokio", coord: [139.69, 35.69] },
{ id: "syd", name: "Sídney", coord: [151.21, -33.87] },
{ id: "jnb", name: "Johannesburgo", coord: [28.04, -26.20] }];


// ------------------------------------------------------------
// Transfer arcs — pairs of node ids, each with a stagger.
// dur = seconds for one full draw-in / draw-out cycle.
// ------------------------------------------------------------
const SPEI_ARCS = [
{ from: "mex", to: "lon", delay: 0.0, dur: 5.4 },
{ from: "nyc", to: "tyo", delay: 0.9, dur: 6.0 },
{ from: "mad", to: "sao", delay: 1.8, dur: 5.0 },
{ from: "lon", to: "sin", delay: 2.6, dur: 5.6 },
{ from: "hkg", to: "syd", delay: 3.4, dur: 4.8 },
{ from: "mex", to: "hkg", delay: 4.2, dur: 5.8 },
{ from: "dub", to: "jnb", delay: 5.0, dur: 4.6 },
{ from: "nyc", to: "mad", delay: 5.8, dur: 5.2 }];


// ------------------------------------------------------------
// Lazy-load the world-atlas TopoJSON. d3 + topojson-client are
// loaded via <script> in the page <head>, so they're already
// on window by the time this component mounts.
// ------------------------------------------------------------
let _worldPromise = null;
function loadWorld() {
  if (_worldPromise) return _worldPromise;
  _worldPromise = fetch("https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-110m.json").
  then((r) => r.json()).
  then((topo) => {
    const topojson = window.topojson;
    return {
      land: topojson.feature(topo, topo.objects.land),
      countries: topojson.feature(topo, topo.objects.countries),
      borders: topojson.mesh(topo, topo.objects.countries, (a, b) => a !== b)
    };
  });
  return _worldPromise;
}

// ------------------------------------------------------------
// Globe component
// ------------------------------------------------------------
function Globe() {
  // Refs for imperative DOM updates (rAF-driven; we avoid React
  // re-renders during the animation loop for perf).
  const landRef = useRef(null);
  const bordersRef = useRef(null);
  const graticuleRef = useRef(null);
  const sphereRef = useRef(null);
  const nodesGroupRef = useRef(null);
  const arcsGroupRef = useRef(null);
  const cometsGroupRef = useRef(null);

  const [ready, setReady] = useState(false);
  const [failed, setFailed] = useState(false);

  useEffect(() => {
    let mounted = true;
    let rafId = null;
    const startMs = performance.now();

    // ---- viewBox geometry ------------------------------------
    const W = 400,H = 400,R = 186;
    const CX = W / 2,CY = H / 2;

    const d3 = window.d3;
    if (!d3 || !window.topojson) {
      setFailed(true);
      return;
    }

    // Orthographic projection — clipAngle(90) hides the far side.
    const projection = d3.geoOrthographic().
    scale(R).
    translate([CX, CY]).
    clipAngle(90);

    const pathGen = d3.geoPath(projection);
    const graticule = d3.geoGraticule().step([15, 15])();

    // ---- node + arc element factories -----------------------
    function buildNodes() {
      const g = nodesGroupRef.current;
      if (!g) return [];
      g.innerHTML = "";
      return SPEI_NODES.map((_, i) => {
        const ns = "http://www.w3.org/2000/svg";
        const wrap = document.createElementNS(ns, "g");
        wrap.setAttribute("class", "spei-globe-node");

        // Expanding pulse ring
        const pulse = document.createElementNS(ns, "circle");
        pulse.setAttribute("r", "3");
        pulse.setAttribute("fill", "none");
        pulse.setAttribute("stroke", "rgba(140,160,246,0.95)");
        pulse.setAttribute("stroke-width", "1");
        pulse.setAttribute("class", "node-pulse");
        pulse.setAttribute("style", `animation-delay: ${(i * 0.32).toFixed(2)}s;`);

        // Soft halo
        const halo = document.createElementNS(ns, "circle");
        halo.setAttribute("r", "4.5");
        halo.setAttribute("fill", "rgba(140,160,246,0.22)");
        halo.setAttribute("class", "node-halo");
        halo.setAttribute("style", `animation-delay: ${(i * 0.32).toFixed(2)}s;`);

        // Core dot
        const outer = document.createElementNS(ns, "circle");
        outer.setAttribute("r", "2.6");
        outer.setAttribute("fill", "#ffffff");

        const inner = document.createElementNS(ns, "circle");
        inner.setAttribute("r", "1.3");
        inner.setAttribute("fill", "#5A74EC");

        wrap.appendChild(halo);
        wrap.appendChild(pulse);
        wrap.appendChild(outer);
        wrap.appendChild(inner);
        g.appendChild(wrap);
        return wrap;
      });
    }

    function buildArcs() {
      const arcG = arcsGroupRef.current;
      const cometG = cometsGroupRef.current;
      if (!arcG || !cometG) return { arcs: [], comets: [] };
      arcG.innerHTML = "";
      cometG.innerHTML = "";

      const ns = "http://www.w3.org/2000/svg";
      const arcs = SPEI_ARCS.map(() => {
        const p = document.createElementNS(ns, "path");
        p.setAttribute("fill", "none");
        p.setAttribute("stroke", "url(#spei-arc-grad)");
        p.setAttribute("stroke-width", "1.4");
        p.setAttribute("stroke-linecap", "round");
        arcG.appendChild(p);
        return p;
      });
      const comets = SPEI_ARCS.map(() => {
        const wrap = document.createElementNS(ns, "g");
        wrap.setAttribute("class", "spei-globe-comet");
        const glow = document.createElementNS(ns, "circle");
        glow.setAttribute("r", "5");
        glow.setAttribute("fill", "url(#spei-comet-glow)");
        const core = document.createElementNS(ns, "circle");
        core.setAttribute("r", "1.8");
        core.setAttribute("fill", "#ffffff");
        wrap.appendChild(glow);
        wrap.appendChild(core);
        cometG.appendChild(wrap);
        return wrap;
      });
      return { arcs, comets };
    }

    // ---- Boot --------------------------------------------------
    let world = null;
    let nodeEls = [];
    let arcEls = [];
    let cometEls = [];

    // Resolve from-id / to-id to [lon,lat] pairs once.
    const byId = Object.fromEntries(SPEI_NODES.map((n) => [n.id, n.coord]));
    const arcCoords = SPEI_ARCS.map((a) => ({
      from: byId[a.from],
      to: byId[a.to],
      interp: d3.geoInterpolate(byId[a.from], byId[a.to]),
      delay: a.delay,
      dur: a.dur
    }));

    loadWorld().
    then((w) => {
      if (!mounted) return;
      world = w;
      nodeEls = buildNodes();
      const { arcs, comets } = buildArcs();
      arcEls = arcs;
      cometEls = comets;
      setReady(true);
      if (sphereRef.current) sphereRef.current.setAttribute("d", pathGen({ type: "Sphere" }) || "");
      rafId = requestAnimationFrame(tick);
    }).
    catch((err) => {
      if (!mounted) return;
      console.error("World atlas load failed:", err);
      setFailed(true);
    });

    // ---- Animation loop --------------------------------------
    function tick(now) {
      if (!mounted) return;
      const t = (now - startMs) / 1000; // seconds since start
      const lambda = -t * 4 % 360; // ~90s full rotation, west→east
      projection.rotate([lambda, -18, 0]); // 18° axial tilt for "Earth" feel

      // World layers
      if (landRef.current) landRef.current.setAttribute("d", pathGen(world.land) || "");
      if (bordersRef.current) bordersRef.current.setAttribute("d", pathGen(world.borders) || "");
      if (graticuleRef.current) graticuleRef.current.setAttribute("d", pathGen(graticule) || "");

      // Nodes — projected and culled if on the far side
      const center = projection.invert([CX, CY]);
      for (let i = 0; i < SPEI_NODES.length; i++) {
        const el = nodeEls[i];
        if (!el) continue;
        const coord = SPEI_NODES[i].coord;
        const dist = d3.geoDistance(coord, center);
        if (dist < Math.PI / 2 - 0.03) {
          const xy = projection(coord);
          if (xy) {
            el.setAttribute("transform", `translate(${xy[0]} ${xy[1]})`);
            // Fade near the rim for a smoother edge
            const edge = 1 - dist / (Math.PI / 2);
            const alpha = Math.min(1, edge * 4);
            el.setAttribute("opacity", alpha.toFixed(3));
          }
        } else {
          el.setAttribute("opacity", "0");
        }
      }

      // Arcs — each follows its own draw-in / hold / erase cycle.
      // Build the great-circle path as a LineString, then let
      // d3-geoPath clip to the front hemisphere via clipAngle(90).
      for (let i = 0; i < arcCoords.length; i++) {
        const ac = arcCoords[i];
        const arcEl = arcEls[i];
        const cometEl = cometEls[i];
        if (!arcEl || !cometEl) continue;

        const cycle = (t + ac.delay) % ac.dur / ac.dur; // 0..1

        // Phase mapping:
        //   0.00–0.05 fade-in invisible
        //   0.05–0.45 draw-in head from 0 → 1 (with optional tail-from-0 trail)
        //   0.45–0.65 hold full visible
        //   0.65–0.95 erase: tail catches up from 0 → 1
        //   0.95–1.00 fade-out
        let head = 0,tail = 0,opacity = 0;
        if (cycle < 0.05) {
          head = cycle / 0.05 * 0.0;
          opacity = cycle / 0.05;
        } else if (cycle < 0.45) {
          head = (cycle - 0.05) / 0.40;
          opacity = 1;
        } else if (cycle < 0.65) {
          head = 1;
          opacity = 1;
        } else if (cycle < 0.95) {
          head = 1;
          tail = (cycle - 0.65) / 0.30;
          opacity = 1;
        } else {
          head = 1;tail = 1;
          opacity = (1 - cycle) / 0.05;
        }

        // Sample the great-circle from tail → head
        const STEPS = 64;
        const startT = tail;
        const endT = head;
        const coords = [];
        if (endT > startT) {
          const segs = Math.max(2, Math.ceil((endT - startT) * STEPS));
          for (let s = 0; s <= segs; s++) {
            const u = startT + s / segs * (endT - startT);
            coords.push(ac.interp(u));
          }
        }
        const d = coords.length ? pathGen({ type: "LineString", coordinates: coords }) : "";
        arcEl.setAttribute("d", d || "");
        arcEl.setAttribute("opacity", opacity.toFixed(3));

        // Comet rides the LEADING edge during the draw-in phase.
        const cometVisible = cycle > 0.05 && cycle < 0.50;
        if (cometVisible) {
          const headPoint = ac.interp(head);
          const dist = d3.geoDistance(headPoint, center);
          if (dist < Math.PI / 2 - 0.02) {
            const xy = projection(headPoint);
            if (xy) {
              cometEl.setAttribute("transform", `translate(${xy[0]} ${xy[1]})`);
              cometEl.setAttribute("opacity", "1");
            } else {
              cometEl.setAttribute("opacity", "0");
            }
          } else {
            cometEl.setAttribute("opacity", "0");
          }
        } else {
          cometEl.setAttribute("opacity", "0");
        }
      }

      rafId = requestAnimationFrame(tick);
    }

    return () => {
      mounted = false;
      if (rafId) cancelAnimationFrame(rafId);
    };
  }, []);

  return (
    <div className="globe globe-ortho">
      <span className="globe-atmosphere" aria-hidden="true" />

      <svg
        className="globe-svg"
        viewBox="0 0 400 400"
        preserveAspectRatio="xMidYMid meet"
        aria-hidden="true">

        <defs>
          {/* Sphere fill — deep blue with cool highlight upper-left */}
          <radialGradient id="spei-sphere" cx="35%" cy="30%" r="80%">
            <stop offset="0%" stopColor="#1f4694" />
            <stop offset="35%" stopColor="#0d2563" />
            <stop offset="75%" stopColor="#06143e" />
            <stop offset="100%" stopColor="#020722" />
          </radialGradient>
          {/* Glossy shine upper-left */}
          <radialGradient id="spei-shine" cx="30%" cy="22%" r="40%">
            <stop offset="0%" stopColor="rgba(160,195,255,0.55)" />
            <stop offset="55%" stopColor="rgba(160,195,255,0.05)" />
            <stop offset="100%" stopColor="rgba(160,195,255,0)" />
          </radialGradient>
          {/* Rim shadow — darken sphere edges for depth */}
          <radialGradient id="spei-rim" cx="50%" cy="50%" r="50%">
            <stop offset="65%" stopColor="rgba(0,0,0,0)" />
            <stop offset="100%" stopColor="rgba(0,0,0,0.55)" />
          </radialGradient>
          {/* Edge ring — bright blue brand line at perimeter */}
          <radialGradient id="spei-edge" cx="50%" cy="50%" r="50%">
            <stop offset="93%" stopColor="rgba(140,160,246,0)" />
            <stop offset="98.5%" stopColor="rgba(90,116,236,0.85)" />
            <stop offset="100%" stopColor="rgba(140,160,246,0)" />
          </radialGradient>

          {/* Arc stroke — gradient along the line for a "flowing" feel */}
          <linearGradient id="spei-arc-grad" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0%" stopColor="rgba(140,160,246,0)" />
            <stop offset="50%" stopColor="rgba(160,195,255,0.95)" />
            <stop offset="100%" stopColor="rgba(255,255,255,1)" />
          </linearGradient>

          {/* Comet glow */}
          <radialGradient id="spei-comet-glow" cx="50%" cy="50%" r="50%">
            <stop offset="0%" stopColor="rgba(255,255,255,1)" />
            <stop offset="30%" stopColor="rgba(180,210,255,0.95)" />
            <stop offset="100%" stopColor="rgba(90,116,236,0)" />
          </radialGradient>

          <clipPath id="spei-globe-clip">
            <circle cx="200" cy="200" r="186" />
          </clipPath>
        </defs>

        {/* Sphere fill */}
        <circle cx="200" cy="200" r="186" fill="url(#spei-sphere)" />

        {/* World content — clipped to the sphere circle */}
        <g clipPath="url(#spei-globe-clip)">
          {/* Graticule (lat/lon grid) */}
          <path
            ref={graticuleRef}
            fill="none"
            stroke="rgba(140,160,246,0.18)"
            strokeWidth="0.45" />

          {/* Sphere outline (so the ocean has a soft inner ring) */}
          <path
            ref={sphereRef}
            fill="none"
            stroke="rgba(140,160,246,0.25)"
            strokeWidth="0.6" />

          {/* Land fill */}
          <path
            ref={landRef}
            fill="rgba(102,148,235,0.55)"
            stroke="rgba(190,215,255,0.85)"
            strokeWidth="0.5"
            strokeLinejoin="round" />

          {/* Country borders — slightly brighter accent */}
          <path
            ref={bordersRef}
            fill="none"
            stroke="rgba(220,235,255,0.45)"
            strokeWidth="0.35" />


          {/* Rim shadow */}
          <circle cx="200" cy="200" r="186" fill="url(#spei-rim)" />
          {/* Highlight */}
          <circle cx="200" cy="200" r="186" fill="url(#spei-shine)" />

          {/* Arcs + comets ride on top of the sphere but still
               clipped to it so they wrap visually with the planet. */}
          <g ref={arcsGroupRef} />
          <g ref={cometsGroupRef} />
          <g ref={nodesGroupRef} />
        </g>

        {/* Outer electric-blue edge ring (outside clip) */}
        <circle cx="200" cy="200" r="186" fill="url(#spei-edge)" />
        <circle
          cx="200" cy="200" r="186"
          fill="none"
          stroke="rgba(90,116,236,0.55)"
          strokeWidth="0.9" />

      </svg>

      {/* Loading placeholder — fades out once the world data lands */}
      {!ready && !failed &&
      <div className="globe-loading" aria-hidden="true">
          <span className="globe-loading-dot" />
          <span className="globe-loading-dot" />
          <span className="globe-loading-dot" />
        </div>
      }
    </div>);

}

// ------------------------------------------------------------
// Section
// ------------------------------------------------------------
function SpeiGlobalSection() {
  return (
    <section className="spei-section" data-screen-label="SPEI Global">
      {/* Ambient background — soft radial accent + subtle grid */}
      <div className="spei-ambient" aria-hidden="true" />

      <div className="spei-wrap">
        {/* LEFT — copy + stats */}
        <div className="spei-col-left">
          <span className="spei-badge reveal">
            <span className="spei-badge-dot" aria-hidden="true" />
            SPEI Global
          </span>

          <h2 className="spei-headline reveal" style={{ "--reveal-delay": "80ms" }}>
            Tu dinero, a cualquier parte{" "}
            <span className="spei-headline-twist">
              del mundo. Hoy.
            </span>
          </h2>

          <p className="spei-lede reveal" style={{ "--reveal-delay": "160ms" }}>
            Transferencias internacionales a +150 países.
          </p>

          <div className="reveal" style={{ "--reveal-delay": "240ms" }}>
            <SpeiRoutes />
          </div>
        </div>

        {/* RIGHT — globe */}
        <div className="spei-col-right reveal" style={{ "--reveal-delay": "200ms" }}>
          <Globe />
        </div>
      </div>
    </section>);

}

window.SpeiGlobalSection = SpeiGlobalSection;