// Streams — horizontal rivers of data packets that deflect around the cursor
// Each packet is a glyph or number; they drift sideways at different speeds/depths.

const GLYPHS = '0123456789ABCDEF↑↓→←◦·•+=';

const Streams = () => {
  const canvasRef = useRef(null);
  const t = useTweaks();
  const motion = t.motion !== false;
  const intensity = t.intensity ?? 1;

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    let w, h, dpr;
    let packets = [];
    let raf;

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

      // ~8 horizontal lanes of packets, offset vertically
      const lanes = 8;
      packets = [];
      for (let i = 0; i < lanes; i++) {
        const y = (i + 0.5) * (h / lanes) + (Math.random() - 0.5) * 40;
        const dir = i % 2 === 0 ? 1 : -1;
        const speed = (0.4 + Math.random() * 0.8) * dir;
        const density = 6 + Math.floor(Math.random() * 4);
        for (let j = 0; j < density; j++) {
          packets.push({
            x: Math.random() * w,
            y,
            by: y,
            vy: 0,
            speed,
            alpha: 0.12 + Math.random() * 0.3,
            glyph: GLYPHS[Math.floor(Math.random() * GLYPHS.length)],
            size: 9 + Math.random() * 3,
            accent: Math.random() < 0.12,
            nextSwap: Math.random() * 2000,
          });
        }
      }
    };

    const draw = (ts) => {
      ctx.clearRect(0, 0, w, h);
      const m = window.__mouse__ || { x: w / 2, y: h / 2 };
      const hue = getComputedStyle(document.documentElement).getPropertyValue('--accent-h') || 190;

      for (const p of packets) {
        // horizontal drift
        if (motion) p.x += p.speed * intensity;
        if (p.x < -20) p.x = w + 20;
        if (p.x > w + 20) p.x = -20;

        // cursor deflection — push the packet vertically away
        const dx = p.x - m.x;
        const dy = p.y - m.y;
        const d2 = dx * dx + dy * dy;
        const reach = 160;
        if (d2 < reach * reach && d2 > 1) {
          const d = Math.sqrt(d2);
          const force = (1 - d / reach) * 30 * intensity;
          p.vy += (dy / d) * force * 0.04;
        }

        // return toward base y
        p.vy += (p.by - p.y) * 0.012;
        p.vy *= 0.9;
        p.y += p.vy;

        // occasional glyph swap for lively feel
        if (motion && ts > p.nextSwap) {
          p.glyph = GLYPHS[Math.floor(Math.random() * GLYPHS.length)];
          p.nextSwap = ts + 400 + Math.random() * 2200;
        }

        ctx.font = `${p.size}px "JetBrains Mono", monospace`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        if (p.accent) {
          ctx.fillStyle = `oklch(0.84 0.1 ${hue} / ${p.alpha * 1.6})`;
          ctx.shadowColor = `oklch(0.84 0.1 ${hue})`;
          ctx.shadowBlur = 6;
        } else {
          ctx.fillStyle = `rgba(237, 235, 228, ${p.alpha})`;
          ctx.shadowBlur = 0;
        }
        ctx.fillText(p.glyph, p.x, p.y);
      }
      ctx.shadowBlur = 0;
      raf = requestAnimationFrame(draw);
    };

    resize();
    draw(0);
    window.addEventListener('resize', resize);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('resize', resize);
    };
  }, [motion, intensity]);

  return <canvas className="streams" ref={canvasRef} aria-hidden="true" />;
};

window.Streams = Streams;
