// Airflow particle field — flow-noise driven; reacts to scroll and pointer.
function AirflowCanvas({ density = 1, hue = "teal", variant = "balanced", paused = false }) {
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(0);
  const pointerRef = React.useRef({ x: -1000, y: -1000, active: false });
  const scrollRef = React.useRef(0);

  React.useEffect(() => {
    const cvs = canvasRef.current;
    if (!cvs) return;
    const ctx = cvs.getContext("2d");
    let W = 0, H = 0, DPR = Math.min(window.devicePixelRatio || 1, 2);

    const resize = () => {
      const rect = cvs.getBoundingClientRect();
      W = rect.width; H = rect.height;
      cvs.width = W * DPR; cvs.height = H * DPR;
      ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(cvs);

    // particles
    const count = Math.floor((W * H) / 9000 * density);
    const ps = new Array(count).fill(0).map(() => ({
      x: Math.random() * W,
      y: Math.random() * H,
      vx: 0, vy: 0,
      life: Math.random() * 220 + 60,
      max: 280,
      w: Math.random() * 1.6 + 0.4,
    }));

    // flow-noise
    const noise = (x, y, t) => {
      const a = Math.sin(x * 0.0018 + t * 0.0006) * Math.cos(y * 0.0021 - t * 0.0004);
      const b = Math.sin((x + y) * 0.0012 + t * 0.0003);
      return (a + b) * Math.PI;
    };

    const paletteByStyle = {
      conservative: ["rgba(74,139,146,0.35)", "rgba(194,217,221,0.45)", "rgba(26,74,82,0.25)"],
      balanced:     ["rgba(92,184,178,0.45)", "rgba(168,218,220,0.55)", "rgba(29,92,99,0.30)"],
      bold:         ["rgba(127,227,220,0.65)", "rgba(191,246,240,0.55)", "rgba(92,184,178,0.40)"],
    };
    const palette = paletteByStyle[variant] || paletteByStyle.balanced;

    const onMove = (e) => {
      const rect = cvs.getBoundingClientRect();
      pointerRef.current = { x: e.clientX - rect.left, y: e.clientY - rect.top, active: true };
    };
    const onLeave = () => { pointerRef.current.active = false; };
    const onScroll = () => { scrollRef.current = window.scrollY; };
    window.addEventListener("pointermove", onMove);
    window.addEventListener("pointerleave", onLeave);
    window.addEventListener("scroll", onScroll, { passive: true });

    let t0 = performance.now();
    const tick = () => {
      const t = performance.now() - t0;
      // gentle trail
      ctx.fillStyle = variant === "bold" ? "rgba(5,30,34,0.10)" : "rgba(240,249,250,0.13)";
      ctx.fillRect(0, 0, W, H);

      const sy = scrollRef.current * 0.05;

      for (let i = 0; i < ps.length; i++) {
        const p = ps[i];
        const ang = noise(p.x, p.y + sy, t);
        const speed = 0.7 + 0.6 * Math.sin((p.x + p.y) * 0.001 + t * 0.001);
        p.vx += Math.cos(ang) * 0.05 * speed;
        p.vy += Math.sin(ang) * 0.05 * speed;

        // pointer repulsion / attraction
        const pt = pointerRef.current;
        if (pt.active) {
          const dx = p.x - pt.x, dy = p.y - pt.y;
          const d2 = dx * dx + dy * dy;
          if (d2 < 22500) {
            const f = 0.6 / Math.max(40, Math.sqrt(d2));
            p.vx += dx * f;
            p.vy += dy * f;
          }
        }

        p.vx *= 0.94; p.vy *= 0.94;
        const px = p.x, py = p.y;
        p.x += p.vx; p.y += p.vy;
        p.life -= 1;

        const alpha = Math.min(1, p.life / 60) * Math.min(1, (p.max - p.life) / 60);
        ctx.strokeStyle = palette[i % palette.length];
        ctx.globalAlpha = alpha;
        ctx.lineWidth = p.w;
        ctx.beginPath();
        ctx.moveTo(px, py);
        ctx.lineTo(p.x, p.y);
        ctx.stroke();

        if (p.life <= 0 || p.x < -10 || p.x > W + 10 || p.y < -10 || p.y > H + 10) {
          p.x = Math.random() * W;
          p.y = Math.random() * H;
          p.vx = 0; p.vy = 0;
          p.life = Math.random() * 220 + 60;
        }
      }
      ctx.globalAlpha = 1;

      if (!paused) rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);

    return () => {
      cancelAnimationFrame(rafRef.current);
      ro.disconnect();
      window.removeEventListener("pointermove", onMove);
      window.removeEventListener("pointerleave", onLeave);
      window.removeEventListener("scroll", onScroll);
    };
  }, [density, variant, paused]);

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

window.AirflowCanvas = AirflowCanvas;
