// BrazilMapCanvas — scientific constellation visualization of Brazil.
//
// Draws a stylized Brazil outline as faint dashed constellation lines,
// glowing city nodes with pulse rings, and animated kit-delivery pulses
// that travel from the base city (Goiânia) toward other nodes.

function BrazilMapCanvas() {
  const ref = React.useRef(null);

  React.useEffect(() => {
    const canvas = ref.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    const dpr = Math.min(window.devicePixelRatio || 1, 1.5);
    let w = 0, h = 0;
    let raf = null;
    let paused = document.hidden;
    let offscreen = false;
    // 30fps cap — this canvas has no interaction requirement
    const FPS_CAP = 1000 / 30;
    let lastFrame = 0;

    // ── Simplified Brazil polygon (normalized [0,1] coordinates) ──────
    // Points traced from a simplified Brazil outline, normalized to fit [0,1]×[0,1].
    // x=0 is leftmost (west), x=1 rightmost (east); y=0 is top (north), y=1 bottom (south).
    const BRAZIL_POLY = [
      [0.39,0.00],[0.45,0.01],[0.50,0.03],[0.57,0.02],[0.63,0.04],
      [0.68,0.07],[0.72,0.06],[0.78,0.10],[0.83,0.09],[0.89,0.13],
      [0.93,0.17],[0.96,0.22],[0.98,0.27],[1.00,0.34],[0.97,0.39],
      [0.93,0.44],[0.91,0.50],[0.94,0.55],[0.96,0.61],[0.94,0.67],
      [0.90,0.72],[0.85,0.76],[0.80,0.79],[0.75,0.82],[0.70,0.86],
      [0.65,0.89],[0.60,0.92],[0.55,0.95],[0.50,0.97],[0.45,0.99],
      [0.40,1.00],[0.35,0.99],[0.30,0.96],[0.27,0.91],[0.25,0.86],
      [0.22,0.81],[0.20,0.75],[0.18,0.69],[0.15,0.64],[0.10,0.60],
      [0.05,0.57],[0.02,0.52],[0.00,0.46],[0.02,0.40],[0.05,0.35],
      [0.08,0.30],[0.06,0.25],[0.08,0.19],[0.11,0.14],[0.16,0.10],
      [0.21,0.07],[0.27,0.04],[0.33,0.02],[0.39,0.00],
    ];

    // ── City nodes (normalized to Brazil's bounding box) ──────────────
    // Goiânia is roughly at 16°S, 49°W — which maps to about [0.57, 0.53] in our poly space
    const CITIES = [
      { name: "Goiânia",            nx: 0.55, ny: 0.52, primary: true,  r: 5 },
      { name: "Aparecida de Goiânia",nx: 0.56, ny: 0.54, primary: false, r: 3.5 },
      { name: "Anápolis",           nx: 0.57, ny: 0.49, primary: false, r: 3.5 },
      // Expansion nodes (future reach) — faint
      { name: "",  nx: 0.35, ny: 0.22, primary: false, r: 2, faint: true },
      { name: "",  nx: 0.72, ny: 0.35, primary: false, r: 2, faint: true },
      { name: "",  nx: 0.48, ny: 0.72, primary: false, r: 2, faint: true },
      { name: "",  nx: 0.25, ny: 0.55, primary: false, r: 2, faint: true },
    ];

    // ── Kit pulses state ───────────────────────────────────────────────
    const pulses = [];
    let nextPulseTime = 2000;
    const PULSE_DURATION = 2800; // ms

    function spawnPulse() {
      // Target: one of the non-faint cities (not Goiânia itself) or a faint node
      const targets = CITIES.filter((_, i) => i !== 0);
      const target = targets[Math.floor(Math.random() * targets.length)];
      pulses.push({
        t: 0,
        dur: PULSE_DURATION,
        tx: target.nx,
        ty: target.ny,
      });
    }

    // ── Timing ────────────────────────────────────────────────────────
    let last = performance.now();

    function resize() {
      const rect = canvas.getBoundingClientRect();
      w = rect.width; h = rect.height;
      canvas.width  = w * dpr;
      canvas.height = h * dpr;
      ctx.scale(dpr, dpr);
    }
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(canvas);

    function toScreen(nx, ny) {
      // Margin so poly doesn't touch edges
      const mx = w * 0.06, my = h * 0.06;
      return [mx + nx * (w - mx * 2), my + ny * (h - my * 2)];
    }

    function checkPaused() {
      const should = document.hidden || offscreen;
      if (!paused && should) paused = true;
      else if (paused && !should) { paused = false; if (!raf) tick(); }
    }
    document.addEventListener("visibilitychange", checkPaused);

    // Pause when scrolled off-screen — saves CPU when user hasn't reached Impact section
    const visObs = new IntersectionObserver(
      ([e]) => { offscreen = !e.isIntersecting; checkPaused(); },
      { threshold: 0.01 }
    );
    visObs.observe(canvas);

    function drawMap(alpha) {
      // Draw dashed constellation outline
      ctx.save();
      ctx.setLineDash([2.5, 7]);
      ctx.strokeStyle = `rgba(0,229,255,${alpha * 0.22})`;
      ctx.lineWidth = 0.7;
      ctx.beginPath();
      const [sx, sy] = toScreen(BRAZIL_POLY[0][0], BRAZIL_POLY[0][1]);
      ctx.moveTo(sx, sy);
      for (let i = 1; i < BRAZIL_POLY.length; i++) {
        const [px, py] = toScreen(BRAZIL_POLY[i][0], BRAZIL_POLY[i][1]);
        ctx.lineTo(px, py);
      }
      ctx.closePath();
      ctx.stroke();
      ctx.setLineDash([]);
      ctx.restore();
    }

    function drawStars(t) {
      // Faint star field inside Brazil outline region
      const seed = [0.2,0.5,0.3,0.7,0.15,0.6,0.4,0.8,0.25,0.9,0.45,0.65,0.35,0.55,0.75];
      for (let i = 0; i < seed.length; i += 2) {
        const [sx, sy] = toScreen(seed[i] * 0.8 + 0.1, seed[i+1 < seed.length ? i+1 : 0] * 0.8 + 0.1);
        const twinkle = 0.03 + 0.02 * Math.sin(t * 0.001 + i * 1.3);
        ctx.fillStyle = `rgba(180,200,255,${twinkle})`;
        ctx.beginPath();
        ctx.arc(sx, sy, 0.7, 0, Math.PI * 2);
        ctx.fill();
      }
    }

    function drawCities(t) {
      CITIES.forEach((c, ci) => {
        const [cx, cy] = toScreen(c.nx, c.ny);
        const alpha = c.faint ? 0.28 : 1;

        // Outer glow
        if (!c.faint) {
          const gr = ctx.createRadialGradient(cx, cy, 0, cx, cy, c.primary ? 22 : 14);
          gr.addColorStop(0, "rgba(0,229,255,0.18)");
          gr.addColorStop(1, "rgba(0,229,255,0)");
          ctx.fillStyle = gr;
          ctx.beginPath();
          ctx.arc(cx, cy, c.primary ? 22 : 14, 0, Math.PI * 2);
          ctx.fill();
        }

        // Pulse rings — primary city gets two rings, secondary gets one
        if (c.primary) {
          const p1 = (Math.sin(t * 0.0015) * 0.5 + 0.5);
          const p2 = (Math.sin(t * 0.0015 + Math.PI) * 0.5 + 0.5);
          drawPulseRing(cx, cy, 18 + p1 * 10, 0.25 - p1 * 0.22);
          drawPulseRing(cx, cy, 18 + p2 * 14, 0.18 - p2 * 0.16);
        } else if (!c.faint) {
          const p = (Math.sin(t * 0.0012 + ci) * 0.5 + 0.5);
          drawPulseRing(cx, cy, 12 + p * 7, 0.18 - p * 0.16);
        }

        // Core dot
        ctx.fillStyle = c.faint
          ? "rgba(0,229,255,0.28)"
          : c.primary
            ? "#00E5FF"
            : "rgba(0,229,255,0.7)";
        ctx.shadowColor = "rgba(0,229,255,0.9)";
        ctx.shadowBlur = c.primary ? 14 : c.faint ? 4 : 8;
        ctx.beginPath();
        ctx.arc(cx, cy, c.r, 0, Math.PI * 2);
        ctx.fill();
        ctx.shadowBlur = 0;
      });
    }

    function drawPulseRing(cx, cy, radius, alpha) {
      if (alpha <= 0) return;
      ctx.strokeStyle = `rgba(0,229,255,${alpha})`;
      ctx.lineWidth = 0.8;
      ctx.beginPath();
      ctx.arc(cx, cy, radius, 0, Math.PI * 2);
      ctx.stroke();
    }

    function drawPulses() {
      const [bx, by] = toScreen(CITIES[0].nx, CITIES[0].ny); // Goiânia

      pulses.forEach((p) => {
        const progress = Math.min(p.t / p.dur, 1);
        // Ease in-out
        const eased = progress < 0.5
          ? 2 * progress * progress
          : 1 - Math.pow(-2 * progress + 2, 2) / 2;

        const [tx, ty] = toScreen(p.tx, p.ty);

        // Bezier control point — arc outward
        const cpx = (bx + tx) / 2 + (by - ty) * 0.3;
        const cpy = (by + ty) / 2 + (tx - bx) * 0.2;

        // Current position along bezier
        const t1 = eased;
        const t0 = 1 - t1;
        const px = t0*t0*bx + 2*t0*t1*cpx + t1*t1*tx;
        const py = t0*t0*by + 2*t0*t1*cpy + t1*t1*ty;

        // Alpha: fade in (0→0.3), full (0.3→0.7), fade out (0.7→1)
        let alpha;
        if (progress < 0.25)      alpha = progress / 0.25;
        else if (progress < 0.70) alpha = 1;
        else                      alpha = 1 - (progress - 0.70) / 0.30;
        alpha = Math.max(0, Math.min(1, alpha));

        // Draw trail (last 15% of path already traveled)
        const trailStart = Math.max(0, eased - 0.15);
        const te = trailStart;
        const te0 = 1 - te;
        const trailX = te0*te0*bx + 2*te0*te*cpx + te*te*tx;
        const trailY = te0*te0*by + 2*te0*te*cpy + te*te*ty;

        ctx.strokeStyle = `rgba(255,179,71,${alpha * 0.5})`; // warm amber trail
        ctx.lineWidth = 1;
        ctx.setLineDash([2, 4]);
        ctx.beginPath();
        ctx.moveTo(trailX, trailY);
        ctx.lineTo(px, py);
        ctx.stroke();
        ctx.setLineDash([]);

        // Pulse dot
        const dotGr = ctx.createRadialGradient(px, py, 0, px, py, 7);
        dotGr.addColorStop(0, `rgba(255,200,100,${alpha})`);
        dotGr.addColorStop(0.5, `rgba(255,130,60,${alpha * 0.6})`);
        dotGr.addColorStop(1, "rgba(255,130,60,0)");
        ctx.fillStyle = dotGr;
        ctx.beginPath();
        ctx.arc(px, py, 7, 0, Math.PI * 2);
        ctx.fill();

        // Bright core
        ctx.fillStyle = `rgba(255,230,180,${alpha})`;
        ctx.shadowColor = "rgba(255,180,80,0.9)";
        ctx.shadowBlur = 10;
        ctx.beginPath();
        ctx.arc(px, py, 2.5, 0, Math.PI * 2);
        ctx.fill();
        ctx.shadowBlur = 0;

        // Arrival burst
        if (progress > 0.88) {
          const burst = (progress - 0.88) / 0.12;
          const burstAlpha = (1 - burst) * 0.7;
          ctx.strokeStyle = `rgba(0,229,255,${burstAlpha})`;
          ctx.lineWidth = 1;
          ctx.beginPath();
          ctx.arc(tx, ty, burst * 16, 0, Math.PI * 2);
          ctx.stroke();
        }
      });
    }

    function tick(now) {
      if (paused) { raf = null; return; }
      // 30fps cap — avoids burning GPU on a non-interactive decorative canvas
      if (now - lastFrame < FPS_CAP) { raf = requestAnimationFrame(tick); return; }
      const dt = Math.min(now - last, 50);
      last = now;
      lastFrame = now;

      // Spawn new pulse
      nextPulseTime -= dt;
      if (nextPulseTime <= 0) {
        spawnPulse();
        nextPulseTime = 3200 + Math.random() * 2000;
      }

      // Advance pulses
      for (let i = pulses.length - 1; i >= 0; i--) {
        pulses[i].t += dt;
        if (pulses[i].t >= pulses[i].dur) pulses.splice(i, 1);
      }

      ctx.clearRect(0, 0, w, h);

      drawStars(now);
      drawMap(1);
      drawCities(now);
      drawPulses();

      raf = requestAnimationFrame(tick);
    }
    tick(performance.now());

    return () => {
      if (raf) cancelAnimationFrame(raf);
      ro.disconnect();
      visObs.disconnect();
      document.removeEventListener("visibilitychange", checkPaused);
    };
  }, []);

  return (
    <canvas
      ref={ref}
      style={{ position: "absolute", inset: 0, width: "100%", height: "100%", display: "block" }}
    />
  );
}

window.BrazilMapCanvas = BrazilMapCanvas;
