/* ============================================================
   $GCA particle layer.

   - NeonRain: low-cost canvas overlay for the hero. Streaks of
     neon rain falling diagonally + ambient floating embers.
     Sized once per resize, uses requestAnimationFrame, idles
     when offscreen via IntersectionObserver.

   - CoinBurst: page-level singleton that listens for the global
     "gca:claim" event and spawns a coin-confetti burst from
     the originating element (passed in event.detail.x/.y) or
     from the screen center. Auto-cleans particles when done.

   - SatRadar: thin overlay we mount inside the turf-stage to
     give the diorama a slow sat-radar sweep.
   ============================================================ */

const {
  useEffect: useE_p,
  useRef:    useR_p,
} = React;

/* ─────────────────────────────────────────────────────────────
   NeonRain — full-bleed canvas. drop into the hero.
   ───────────────────────────────────────────────────────────── */
function NeonRain({ density = 110, palette = ["#ff3aa3", "#46e5e5", "#ffd23a"] }) {
  const ref = useR_p(null);
  const wrap = useR_p(null);

  useE_p(() => {
    const cv = ref.current;
    const wr = wrap.current;
    if (!cv || !wr) return;
    const ctx = cv.getContext("2d");
    let running = true;
    let drops = [];
    let embers = [];
    let raf = 0;

    const sizeCanvas = () => {
      const r = wr.getBoundingClientRect();
      const dpr = Math.min(2, window.devicePixelRatio || 1);
      cv.width = Math.max(1, r.width) * dpr;
      cv.height = Math.max(1, r.height) * dpr;
      cv.style.width = r.width + "px";
      cv.style.height = r.height + "px";
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      seed(r.width, r.height);
    };
    const seed = (w, h) => {
      drops = Array.from({ length: density }, () => ({
        x: Math.random() * w,
        y: Math.random() * h,
        len: 40 + Math.random() * 90,
        vy: 240 + Math.random() * 320,
        vx: -110 - Math.random() * 80,
        col: palette[Math.floor(Math.random() * palette.length)],
        op:  0.25 + Math.random() * 0.45,
      }));
      embers = Array.from({ length: 24 }, () => ({
        x: Math.random() * w,
        y: Math.random() * h,
        r: 1 + Math.random() * 2.4,
        vx: -8 - Math.random() * 14,
        vy: -4 - Math.random() * 10,
        col: palette[Math.floor(Math.random() * palette.length)],
        ph: Math.random() * Math.PI * 2,
      }));
    };

    sizeCanvas();
    const ro = new ResizeObserver(() => sizeCanvas());
    ro.observe(wr);

    // visibility — pause when offscreen
    let visible = true;
    const io = new IntersectionObserver(([e]) => { visible = e.isIntersecting; }, { threshold: 0.01 });
    io.observe(wr);

    let last = performance.now();
    const draw = (now) => {
      raf = requestAnimationFrame(draw);
      if (!running || !visible) { last = now; return; }
      const dt = Math.min(0.06, (now - last) / 1000);
      last = now;
      const r = wr.getBoundingClientRect();
      const w = r.width, h = r.height;

      // soft motion-blur instead of full clear → trails for free
      ctx.fillStyle = "rgba(8,3,15,0.22)";
      ctx.fillRect(0, 0, w, h);

      // additive on top
      ctx.globalCompositeOperation = "lighter";

      // rain
      for (let i = 0; i < drops.length; i++) {
        const d = drops[i];
        const x2 = d.x + (d.vx / d.vy) * d.len;
        const y2 = d.y + d.len;
        const grad = ctx.createLinearGradient(d.x, d.y, x2, y2);
        grad.addColorStop(0, "rgba(255,255,255,0)");
        grad.addColorStop(0.4, d.col + "60");
        grad.addColorStop(1, d.col + "ff");
        ctx.strokeStyle = grad;
        ctx.globalAlpha = d.op;
        ctx.lineWidth = 1.3;
        ctx.beginPath();
        ctx.moveTo(d.x, d.y); ctx.lineTo(x2, y2);
        ctx.stroke();
        d.x += d.vx * dt;
        d.y += d.vy * dt;
        if (d.y > h + 20 || d.x < -40) {
          d.x = Math.random() * (w + 200);
          d.y = -20 - Math.random() * 80;
        }
      }

      // embers
      for (let i = 0; i < embers.length; i++) {
        const e = embers[i];
        e.ph += dt * 1.4;
        const r0 = e.r + Math.sin(e.ph) * 0.6;
        const g = ctx.createRadialGradient(e.x, e.y, 0, e.x, e.y, r0 * 6);
        g.addColorStop(0, e.col + "ff");
        g.addColorStop(0.4, e.col + "30");
        g.addColorStop(1, e.col + "00");
        ctx.globalAlpha = 0.55;
        ctx.fillStyle = g;
        ctx.beginPath();
        ctx.arc(e.x, e.y, r0 * 6, 0, Math.PI * 2);
        ctx.fill();
        e.x += e.vx * dt;
        e.y += e.vy * dt;
        if (e.y < -20 || e.x < -40) {
          e.x = Math.random() * w + 40;
          e.y = h + 20;
        }
      }
      ctx.globalAlpha = 1;
      ctx.globalCompositeOperation = "source-over";
    };
    raf = requestAnimationFrame(draw);

    return () => {
      running = false;
      cancelAnimationFrame(raf);
      ro.disconnect(); io.disconnect();
    };
  }, [density, palette.join(",")]);

  return (
    <div ref={wrap} className="gx-rain">
      <canvas ref={ref} />
    </div>
  );
}
window.NeonRain = NeonRain;

/* ─────────────────────────────────────────────────────────────
   CoinBurst — page-level singleton.
   Listens for window.dispatchEvent(new CustomEvent("gca:claim", {detail:{x,y}}))
   ───────────────────────────────────────────────────────────── */
function CoinBurst() {
  const cv = useR_p(null);

  useE_p(() => {
    const canvas = cv.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    let raf = 0;
    let coins = [];

    const resize = () => {
      const dpr = Math.min(2, window.devicePixelRatio || 1);
      canvas.width = window.innerWidth * dpr;
      canvas.height = window.innerHeight * dpr;
      canvas.style.width = window.innerWidth + "px";
      canvas.style.height = window.innerHeight + "px";
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    window.addEventListener("resize", resize);

    const spawn = (x, y, count = 36) => {
      for (let i = 0; i < count; i++) {
        const a = (i / count) * Math.PI * 2 + Math.random() * 0.4;
        const sp = 320 + Math.random() * 360;
        coins.push({
          x, y,
          vx: Math.cos(a) * sp * (0.5 + Math.random() * 0.6),
          vy: Math.sin(a) * sp * (0.5 + Math.random() * 0.6) - 220,
          rot: Math.random() * Math.PI,
          vr: (Math.random() - 0.5) * 12,
          life: 1.8 + Math.random() * 0.6,
          age: 0,
          col: ["#ffd23a", "#46e5e5", "#ff3aa3"][Math.floor(Math.random() * 3)],
          size: 6 + Math.random() * 8,
          kind: Math.random() < 0.78 ? "coin" : "spark",
        });
      }
    };

    const onClaim = (e) => {
      let x, y;
      if (e.detail && Number.isFinite(e.detail.x) && Number.isFinite(e.detail.y)) {
        x = e.detail.x; y = e.detail.y;
      } else {
        x = window.innerWidth / 2;
        y = window.innerHeight * 0.45;
      }
      spawn(x, y, 60);

      // brief flash banner
      const banner = document.createElement("div");
      banner.className = "gx-claim-banner";
      banner.innerHTML = `<span class="gx-claim-star">★</span> turf claimed${e.detail?.c ? " · " + e.detail.c : ""}`;
      document.body.appendChild(banner);
      setTimeout(() => banner.classList.add("out"), 1100);
      setTimeout(() => banner.remove(), 1900);
    };
    window.addEventListener("gca:claim", onClaim);

    let last = performance.now();
    const draw = (now) => {
      const dt = Math.min(0.05, (now - last) / 1000);
      last = now;
      ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
      const g = 720; // gravity
      for (let i = coins.length - 1; i >= 0; i--) {
        const c = coins[i];
        c.age += dt;
        if (c.age >= c.life) { coins.splice(i, 1); continue; }
        c.vy += g * dt;
        c.x += c.vx * dt;
        c.y += c.vy * dt;
        c.rot += c.vr * dt;
        const a = 1 - c.age / c.life;
        ctx.save();
        ctx.translate(c.x, c.y);
        ctx.rotate(c.rot);
        ctx.globalAlpha = a;
        if (c.kind === "coin") {
          // coin disc with letter
          const r0 = c.size;
          const sx = Math.abs(Math.cos(c.rot * 1.7));
          const grad = ctx.createLinearGradient(-r0, -r0, r0, r0);
          grad.addColorStop(0, "#fff4c2");
          grad.addColorStop(0.5, c.col);
          grad.addColorStop(1, "#7a5a10");
          ctx.fillStyle = grad;
          ctx.beginPath();
          ctx.ellipse(0, 0, r0 * sx + 0.5, r0, 0, 0, Math.PI * 2);
          ctx.fill();
          ctx.strokeStyle = "#0a0014";
          ctx.lineWidth = 0.8;
          ctx.stroke();
          // letter
          ctx.fillStyle = "#0a0014";
          ctx.textAlign = "center"; ctx.textBaseline = "middle";
          ctx.font = "bold " + Math.round(r0) + "px JetBrains Mono, monospace";
          if (sx > 0.4) ctx.fillText("$", 0, 0.5);
        } else {
          // bright neon spark
          const r0 = c.size * 0.5;
          const grad = ctx.createRadialGradient(0, 0, 0, 0, 0, r0 * 3);
          grad.addColorStop(0, c.col);
          grad.addColorStop(1, c.col + "00");
          ctx.fillStyle = grad;
          ctx.fillRect(-r0 * 3, -r0 * 3, r0 * 6, r0 * 6);
        }
        ctx.restore();
      }
      raf = requestAnimationFrame(draw);
    };
    raf = requestAnimationFrame(draw);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("gca:claim", onClaim);
      window.removeEventListener("resize", resize);
    };
  }, []);

  return <canvas ref={cv} className="gx-coinburst" aria-hidden="true" />;
}
window.CoinBurst = CoinBurst;

/* ─────────────────────────────────────────────────────────────
   SatRadar — slow conic sweep inside the turf stage.
   Pure CSS via conic-gradient + animation; the component is
   just a styled <div> with a sweep + axis lines.
   ───────────────────────────────────────────────────────────── */
function SatRadar() {
  return (
    <div className="gx-radar" aria-hidden="true">
      <div className="gx-radar-sweep" />
      <div className="gx-radar-rings">
        {[0.25, 0.5, 0.75, 1].map((r, i) => (
          <span key={i} style={{ "--r": r }} />
        ))}
      </div>
      <div className="gx-radar-cross">
        <span className="vx" /><span className="hx" />
      </div>
    </div>
  );
}
window.SatRadar = SatRadar;
