// Three hero animation variants. Each renders into a <canvas> filling .hero-canvas.
// Variants: constellation, signal, aperture
const { useEffect, useRef } = React;

function useDPR(canvas) {
  useEffect(() => {
    if (!canvas.current) return;
    const c = canvas.current;
    const resize = () => {
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      const r = c.getBoundingClientRect();
      c.width = r.width * dpr;
      c.height = r.height * dpr;
      const ctx = c.getContext("2d");
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(c);
    return () => ro.disconnect();
  }, []);
}

function getAccent() {
  const v = getComputedStyle(document.documentElement).getPropertyValue("--accent").trim();
  return v || "oklch(0.86 0.14 195)";
}

// ============== CONSTELLATION ==============
function HeroConstellation() {
  const ref = useRef(null);
  useDPR(ref);
  useEffect(() => {
    const c = ref.current; if (!c) return;
    const ctx = c.getContext("2d");
    let running = true;
    const N = 70;
    const nodes = Array.from({length: N}, () => ({
      x: Math.random(), y: Math.random(),
      vx: (Math.random()-0.5)*0.00012,
      vy: (Math.random()-0.5)*0.00012,
      r: Math.random()*1.4 + 0.5,
      phase: Math.random()*Math.PI*2,
    }));
    let mouse = {x: 0.5, y: 0.5, active: false};
    const onMove = (e) => {
      const r = c.getBoundingClientRect();
      mouse.x = (e.clientX - r.left) / r.width;
      mouse.y = (e.clientY - r.top) / r.height;
      mouse.active = true;
    };
    const onLeave = () => { mouse.active = false; };
    window.addEventListener("mousemove", onMove);
    c.parentElement.addEventListener("mouseleave", onLeave);

    let t0 = performance.now();
    function frame(now) {
      if (!running) return;
      const dt = now - t0; t0 = now;
      const r = c.getBoundingClientRect();
      const W = r.width, H = r.height;
      ctx.clearRect(0, 0, W, H);

      // subtle grid
      ctx.strokeStyle = "oklch(0.28 0.013 250 / 0.4)";
      ctx.lineWidth = 1;
      const gx = 80;
      for (let x = 0; x < W; x += gx) {
        ctx.beginPath(); ctx.moveTo(x+0.5, 0); ctx.lineTo(x+0.5, H); ctx.stroke();
      }
      for (let y = 0; y < H; y += gx) {
        ctx.beginPath(); ctx.moveTo(0, y+0.5); ctx.lineTo(W, y+0.5); ctx.stroke();
      }

      const accent = getAccent();
      const ax = mouse.x * W, ay = mouse.y * H;

      // update
      for (const n of nodes) {
        n.x += n.vx * dt; n.y += n.vy * dt;
        if (n.x < 0 || n.x > 1) n.vx *= -1;
        if (n.y < 0 || n.y > 1) n.vy *= -1;
        n.x = Math.max(0, Math.min(1, n.x));
        n.y = Math.max(0, Math.min(1, n.y));
      }

      // links
      for (let i = 0; i < N; i++) {
        for (let j = i+1; j < N; j++) {
          const dx = (nodes[i].x - nodes[j].x) * W;
          const dy = (nodes[i].y - nodes[j].y) * H;
          const d = Math.hypot(dx, dy);
          if (d < 160) {
            const a = (1 - d/160) * 0.35;
            ctx.strokeStyle = `oklch(0.86 0.14 195 / ${a})`;
            // use accent var via filter trick: just compute alpha and use accent's hue via CSS color-mix wouldn't work in canvas, so we hardcode accent color string
            ctx.strokeStyle = withAlpha(accent, a);
            ctx.lineWidth = 1;
            ctx.beginPath(); ctx.moveTo(nodes[i].x*W, nodes[i].y*H); ctx.lineTo(nodes[j].x*W, nodes[j].y*H); ctx.stroke();
          }
        }
      }

      // mouse links
      if (mouse.active) {
        for (const n of nodes) {
          const dx = n.x*W - ax, dy = n.y*H - ay;
          const d = Math.hypot(dx, dy);
          if (d < 220) {
            ctx.strokeStyle = withAlpha(accent, (1 - d/220) * 0.7);
            ctx.lineWidth = 1;
            ctx.beginPath(); ctx.moveTo(ax, ay); ctx.lineTo(n.x*W, n.y*H); ctx.stroke();
          }
        }
      }

      // nodes
      for (const n of nodes) {
        n.phase += 0.01;
        const r2 = n.r + Math.sin(n.phase) * 0.4;
        ctx.fillStyle = withAlpha(accent, 0.95);
        ctx.beginPath(); ctx.arc(n.x*W, n.y*H, r2, 0, Math.PI*2); ctx.fill();
        // halo
        ctx.fillStyle = withAlpha(accent, 0.12);
        ctx.beginPath(); ctx.arc(n.x*W, n.y*H, r2*4, 0, Math.PI*2); ctx.fill();
      }

      // vignette top-right diagonal sheen
      const grad = ctx.createLinearGradient(0, 0, W, H);
      grad.addColorStop(0, "transparent");
      grad.addColorStop(1, "oklch(0.16 0.014 250 / 0.6)");
      ctx.fillStyle = grad;
      ctx.fillRect(0, 0, W, H);

      requestAnimationFrame(frame);
    }
    requestAnimationFrame(frame);
    return () => {
      running = false;
      window.removeEventListener("mousemove", onMove);
    };
  }, []);
  return <canvas ref={ref} aria-hidden="true" />;
}

// ============== SIGNAL FIELD ==============
function HeroSignal() {
  const ref = useRef(null);
  useDPR(ref);
  useEffect(() => {
    const c = ref.current; if (!c) return;
    const ctx = c.getContext("2d");
    let running = true;
    let t = 0;
    function frame() {
      if (!running) return;
      const r = c.getBoundingClientRect();
      const W = r.width, H = r.height;
      ctx.fillStyle = "oklch(0.16 0.014 250 / 0.18)";
      ctx.fillRect(0, 0, W, H);

      const accent = getAccent();
      const cols = 60;
      const rows = 32;
      const cw = W / cols;
      const rh = H / rows;
      t += 0.02;

      for (let i = 0; i < cols; i++) {
        for (let j = 0; j < rows; j++) {
          const x = i * cw + cw/2;
          const y = j * rh + rh/2;
          const d = Math.hypot(i - cols/2, j - rows/2);
          const wave = Math.sin(d * 0.3 - t * 1.4) * 0.5 + 0.5;
          const wave2 = Math.sin(i * 0.15 + j * 0.22 - t * 0.8) * 0.5 + 0.5;
          const v = wave * wave2;
          if (v > 0.55) {
            const intensity = (v - 0.55) / 0.45;
            ctx.fillStyle = withAlpha(accent, intensity * 0.9);
            const sz = Math.max(0.5, 1 + intensity * 1.6);
            ctx.fillRect(x - sz/2, y - sz/2, sz, sz);
          } else {
            ctx.fillStyle = withAlpha("oklch(0.45 0.012 250)", 0.4);
            ctx.fillRect(x - 0.5, y - 0.5, 1, 1);
          }
        }
      }

      // scan line
      const scanY = ((Math.sin(t*0.4) * 0.5 + 0.5)) * H;
      const grad = ctx.createLinearGradient(0, scanY - 60, 0, scanY + 60);
      grad.addColorStop(0, "transparent");
      grad.addColorStop(0.5, withAlpha(accent, 0.2));
      grad.addColorStop(1, "transparent");
      ctx.fillStyle = grad;
      ctx.fillRect(0, scanY - 60, W, 120);

      requestAnimationFrame(frame);
    }
    requestAnimationFrame(frame);
    return () => { running = false; };
  }, []);
  return <canvas ref={ref} aria-hidden="true" />;
}

// ============== APERTURE ==============
function HeroAperture() {
  const ref = useRef(null);
  useDPR(ref);
  useEffect(() => {
    const c = ref.current; if (!c) return;
    const ctx = c.getContext("2d");
    let running = true;
    let t = 0;
    function frame() {
      if (!running) return;
      const r = c.getBoundingClientRect();
      const W = r.width, H = r.height;
      ctx.clearRect(0, 0, W, H);

      const accent = getAccent();
      const cx = W * 0.72, cy = H * 0.5;
      const maxR = Math.hypot(W, H) * 0.9;

      // grid
      ctx.strokeStyle = "oklch(0.28 0.013 250 / 0.35)";
      ctx.lineWidth = 1;
      for (let x = 0; x < W; x += 80) {
        ctx.beginPath(); ctx.moveTo(x+0.5, 0); ctx.lineTo(x+0.5, H); ctx.stroke();
      }
      for (let y = 0; y < H; y += 80) {
        ctx.beginPath(); ctx.moveTo(0, y+0.5); ctx.lineTo(W, y+0.5); ctx.stroke();
      }

      t += 0.008;

      // concentric rings expanding outward
      const ringCount = 18;
      for (let i = 0; i < ringCount; i++) {
        const phase = (i / ringCount + t) % 1;
        const radius = phase * maxR;
        const alpha = (1 - phase) * 0.35;
        ctx.strokeStyle = withAlpha(accent, alpha);
        ctx.lineWidth = 1 + (1 - phase) * 1.2;
        ctx.beginPath();
        ctx.arc(cx, cy, radius, 0, Math.PI*2);
        ctx.stroke();
      }

      // crosshair ticks
      ctx.strokeStyle = withAlpha(accent, 0.7);
      ctx.lineWidth = 1;
      const tick = 12;
      ctx.beginPath();
      ctx.moveTo(cx - tick, cy); ctx.lineTo(cx + tick, cy);
      ctx.moveTo(cx, cy - tick); ctx.lineTo(cx, cy + tick);
      ctx.stroke();

      // tick marks at angles
      for (let k = 0; k < 24; k++) {
        const ang = (k / 24) * Math.PI * 2 + t * 0.4;
        const r1 = 80, r2 = 88;
        ctx.strokeStyle = withAlpha(accent, 0.5);
        ctx.beginPath();
        ctx.moveTo(cx + Math.cos(ang) * r1, cy + Math.sin(ang) * r1);
        ctx.lineTo(cx + Math.cos(ang) * r2, cy + Math.sin(ang) * r2);
        ctx.stroke();
      }
      // outer rotating arc
      ctx.strokeStyle = withAlpha(accent, 0.8);
      ctx.lineWidth = 2;
      ctx.beginPath();
      ctx.arc(cx, cy, 110, t*0.6, t*0.6 + Math.PI*0.6);
      ctx.stroke();
      ctx.beginPath();
      ctx.arc(cx, cy, 110, t*0.6 + Math.PI, t*0.6 + Math.PI + Math.PI*0.4);
      ctx.stroke();

      // central glow
      const glow = ctx.createRadialGradient(cx, cy, 0, cx, cy, 200);
      glow.addColorStop(0, withAlpha(accent, 0.18));
      glow.addColorStop(1, "transparent");
      ctx.fillStyle = glow;
      ctx.fillRect(0, 0, W, H);

      // left fade for text legibility
      const lg = ctx.createLinearGradient(0, 0, W*0.6, 0);
      lg.addColorStop(0, "oklch(0.16 0.014 250 / 0.85)");
      lg.addColorStop(1, "transparent");
      ctx.fillStyle = lg;
      ctx.fillRect(0, 0, W, H);

      requestAnimationFrame(frame);
    }
    requestAnimationFrame(frame);
    return () => { running = false; };
  }, []);
  return <canvas ref={ref} aria-hidden="true" />;
}

// helper: parse oklch() and append alpha (oklch supports / alpha syntax).
function withAlpha(color, a) {
  // works for "oklch(L C H)" -> "oklch(L C H / a)"
  const trimmed = color.trim();
  if (trimmed.startsWith("oklch(")) {
    const inner = trimmed.slice(6, -1);
    return `oklch(${inner} / ${a})`;
  }
  return trimmed;
}

window.HeroConstellation = HeroConstellation;
window.HeroSignal = HeroSignal;
window.HeroAperture = HeroAperture;
