// hero-visual.jsx — generative 3D network mesh for the hero (exported to window)
// Canvas-based rotating point sphere with proximity links. Reads --accent
// from CSS so it follows the Tweaks accent, and pauses when data-motion=off.
const { useRef: hvUseRef, useEffect: hvUseEffect } = React;

function HeroVisual() {
  const canvasRef = hvUseRef(null);
  const wrapRef = hvUseRef(null);

  hvUseEffect(() => {
    const canvas = canvasRef.current;
    const wrap = wrapRef.current;
    if (!canvas || !wrap) return;
    const ctx = canvas.getContext("2d");
    let raf, W, H, dpr;
    let mouseX = 0, mouseY = 0, tMouseX = 0, tMouseY = 0;

    // build points on a fibonacci sphere
    const N = 150;
    const R = 1;
    const pts = [];
    for (let i = 0; i < N; i++) {
      const y = 1 - (i / (N - 1)) * 2;
      const rad = Math.sqrt(1 - y * y);
      const theta = Math.PI * (3 - Math.sqrt(5)) * i;
      pts.push({ x: Math.cos(theta) * rad, y, z: Math.sin(theta) * rad });
    }
    // precompute neighbor links (short edges only)
    const links = [];
    for (let i = 0; i < N; i++) {
      for (let j = i + 1; j < N; j++) {
        const dx = pts[i].x - pts[j].x, dy = pts[i].y - pts[j].y, dz = pts[i].z - pts[j].z;
        const d = Math.sqrt(dx * dx + dy * dy + dz * dz);
        if (d < 0.34) links.push([i, j]);
      }
    }

    let accent = "#5b8def";
    const readAccent = () => {
      const v = getComputedStyle(document.documentElement).getPropertyValue("--accent").trim();
      if (v) accent = v;
    };
    readAccent();

    const parseToRGBA = (col, a) => {
      // accent is oklch(...) — let canvas resolve it via a temp element
      const probe = document.createElement("span");
      probe.style.color = col; document.body.appendChild(probe);
      const rgb = getComputedStyle(probe).color; probe.remove();
      const m = rgb.match(/\d+(\.\d+)?/g);
      if (!m) return `rgba(120,160,240,${a})`;
      return `rgba(${m[0]},${m[1]},${m[2]},${a})`;
    };
    let accentRGBA = (a) => parseToRGBA(accent, a);

    const resize = () => {
      dpr = Math.min(2, window.devicePixelRatio || 1);
      const rect = wrap.getBoundingClientRect();
      W = rect.width; H = rect.height;
      canvas.width = W * dpr; canvas.height = H * dpr;
      canvas.style.width = W + "px"; canvas.style.height = H + "px";
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(wrap);

    const onMove = (e) => {
      const rect = wrap.getBoundingClientRect();
      tMouseX = ((e.clientX - rect.left) / rect.width - 0.5) * 2;
      tMouseY = ((e.clientY - rect.top) / rect.height - 0.5) * 2;
    };
    window.addEventListener("mousemove", onMove);

    let ry = 0, rx = 0.3, frame = 0;
    const proj = pts.map(() => ({ x: 0, y: 0, z: 0, s: 0 }));

    const draw = () => {
      const motionOff = document.documentElement.getAttribute("data-motion") === "off";
      if (frame % 40 === 0) { readAccent(); accentRGBA = (a) => parseToRGBA(accent, a); }
      frame++;

      mouseX += (tMouseX - mouseX) * 0.04;
      mouseY += (tMouseY - mouseY) * 0.04;
      if (!motionOff) ry += 0.0026;
      const yaw = ry + mouseX * 0.5;
      const pitch = rx + mouseY * 0.35;

      const cx = W / 2, cy = H / 2;
      const scale = Math.min(W, H) * 0.42;
      const cosY = Math.cos(yaw), sinY = Math.sin(yaw);
      const cosX = Math.cos(pitch), sinX = Math.sin(pitch);

      for (let i = 0; i < N; i++) {
        let { x, y, z } = pts[i];
        let x1 = x * cosY - z * sinY;
        let z1 = x * sinY + z * cosY;
        let y1 = y * cosX - z1 * sinX;
        let z2 = y * sinX + z1 * cosX;
        const persp = 2.4 / (2.4 + z2);
        proj[i].x = cx + x1 * scale * persp;
        proj[i].y = cy + y1 * scale * persp;
        proj[i].z = z2;
        proj[i].s = persp;
      }

      ctx.clearRect(0, 0, W, H);

      // links
      ctx.lineWidth = 1;
      for (let k = 0; k < links.length; k++) {
        const a = proj[links[k][0]], b = proj[links[k][1]];
        const depth = (a.z + b.z) / 2; // -1..1
        const alpha = Math.max(0.05, 0.26 + depth * 0.24);
        ctx.strokeStyle = accentRGBA(alpha);
        ctx.beginPath();
        ctx.moveTo(a.x, a.y);
        ctx.lineTo(b.x, b.y);
        ctx.stroke();
      }
      // nodes
      for (let i = 0; i < N; i++) {
        const p = proj[i];
        const r = 1.5 * p.s + (p.z > 0.55 ? 1.2 : 0);
        const alpha = Math.max(0.28, 0.55 + p.z * 0.4);
        ctx.fillStyle = accentRGBA(alpha);
        ctx.beginPath();
        ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
        ctx.fill();
        if (p.z > 0.65) {
          ctx.fillStyle = accentRGBA(0.16);
          ctx.beginPath();
          ctx.arc(p.x, p.y, r + 3.5, 0, Math.PI * 2);
          ctx.fill();
        }
      }
      raf = requestAnimationFrame(draw);
    };
    draw();

    return () => {
      cancelAnimationFrame(raf);
      ro.disconnect();
      window.removeEventListener("mousemove", onMove);
    };
  }, []);

  return (
    <div className="hero__visual" ref={wrapRef} aria-hidden="true">
      <canvas ref={canvasRef}></canvas>
      <div className="hero__visual-glow"></div>
    </div>
  );
}

Object.assign(window, { HeroVisual });
