// hero.jsx — animated constellation hero
// Lightweight 2D canvas: drifting nodes with proximity links + cursor reactivity.

function HeroCanvas({ accent = '#3a6fa7', ink = '#0f1417', density = 90 }) {
  const canvasRef = React.useRef(null);
  const mouseRef = React.useRef({ x: -9999, y: -9999, active: false });
  const rafRef = React.useRef(0);

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

    const resize = () => {
      const r = cvs.getBoundingClientRect();
      W = r.width; H = r.height;
      cvs.width = Math.floor(W * dpr);
      cvs.height = Math.floor(H * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };

    const seed = () => {
      nodes.length = 0;
      const count = Math.max(30, Math.floor((W * H) / (16000 - density * 100)));
      for (let i = 0; i < count; i++) {
        nodes.push({
          x: Math.random() * W,
          y: Math.random() * H,
          vx: (Math.random() - 0.5) * 0.18,
          vy: (Math.random() - 0.5) * 0.18,
          r: 1 + Math.random() * 1.4,
        });
      }
    };

    const hexToRgb = (hex) => {
      const h = hex.replace('#', '');
      const x = h.length === 3 ? h.split('').map(c => c + c).join('') : h;
      const n = parseInt(x, 16);
      return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
    };
    const [ar, ag, ab] = hexToRgb(accent);
    const [ir, ig, ib] = hexToRgb(ink);

    const draw = () => {
      ctx.clearRect(0, 0, W, H);
      const mx = mouseRef.current.x;
      const my = mouseRef.current.y;
      // links
      for (let i = 0; i < nodes.length; i++) {
        const a = nodes[i];
        for (let j = i + 1; j < nodes.length; j++) {
          const b = nodes[j];
          const dx = a.x - b.x, dy = a.y - b.y;
          const d2 = dx * dx + dy * dy;
          const maxD = 130;
          if (d2 < maxD * maxD) {
            const d = Math.sqrt(d2);
            const alpha = (1 - d / maxD) * 0.18;
            ctx.strokeStyle = `rgba(${ir},${ig},${ib},${alpha})`;
            ctx.lineWidth = 0.6;
            ctx.beginPath();
            ctx.moveTo(a.x, a.y);
            ctx.lineTo(b.x, b.y);
            ctx.stroke();
          }
        }
      }
      // nodes + mouse interaction
      for (const n of nodes) {
        n.x += n.vx; n.y += n.vy;
        if (n.x < 0 || n.x > W) n.vx *= -1;
        if (n.y < 0 || n.y > H) n.vy *= -1;
        const dx = n.x - mx, dy = n.y - my;
        const d2 = dx * dx + dy * dy;
        let glow = 0;
        if (d2 < 160 * 160) {
          glow = (1 - Math.sqrt(d2) / 160);
          // gentle repulsion
          const force = glow * 0.6;
          n.vx += (dx / Math.sqrt(d2 || 1)) * force * 0.02;
          n.vy += (dy / Math.sqrt(d2 || 1)) * force * 0.02;
          // line to cursor
          ctx.strokeStyle = `rgba(${ar},${ag},${ab},${glow * 0.55})`;
          ctx.lineWidth = 0.8;
          ctx.beginPath();
          ctx.moveTo(n.x, n.y);
          ctx.lineTo(mx, my);
          ctx.stroke();
        }
        // damping
        n.vx *= 0.99; n.vy *= 0.99;
        ctx.fillStyle = glow > 0
          ? `rgba(${ar},${ag},${ab},${0.35 + glow * 0.5})`
          : `rgba(${ir},${ig},${ib},0.38)`;
        ctx.beginPath();
        ctx.arc(n.x, n.y, n.r + glow * 1.2, 0, Math.PI * 2);
        ctx.fill();
      }
      rafRef.current = requestAnimationFrame(draw);
    };

    const onMove = (e) => {
      const r = cvs.getBoundingClientRect();
      mouseRef.current.x = e.clientX - r.left;
      mouseRef.current.y = e.clientY - r.top;
      mouseRef.current.active = true;
    };
    const onLeave = () => {
      mouseRef.current.x = -9999;
      mouseRef.current.y = -9999;
      mouseRef.current.active = false;
    };

    resize(); seed();
    draw();
    const onResize = () => { resize(); seed(); };
    window.addEventListener('resize', onResize);
    cvs.addEventListener('mousemove', onMove);
    cvs.addEventListener('mouseleave', onLeave);
    return () => {
      cancelAnimationFrame(rafRef.current);
      window.removeEventListener('resize', onResize);
      cvs.removeEventListener('mousemove', onMove);
      cvs.removeEventListener('mouseleave', onLeave);
    };
  }, [accent, ink, density]);

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

Object.assign(window, { HeroCanvas });
