/* Ambient dot-field canvas — dramatic cursor-responsive background with
   constellation lines, click ripples, and a slow drifting accent glow.
   Performance: dot grid + O(N) cursor scan, line drawing capped to dots
   inside the cursor radius (~150 dots → ~22k pair checks at 30fps). */

const AmbientField = () => {
  const canvasRef = React.useRef(null);
  const mouseRef = React.useRef({ x: -9999, y: -9999, active: false });
  const ripplesRef = React.useRef([]);
  const rafRef = React.useRef(0);

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

    const buildDots = () => {
      dots = [];
      const cols = Math.ceil(W / SPACING) + 2;
      const rows = Math.ceil(H / SPACING) + 2;
      for (let r = 0; r < rows; r++) {
        for (let c = 0; c < cols; c++) {
          dots.push({
            x: c * SPACING,
            y: r * SPACING,
            ph: ((c * 31 + r * 17) % 100) / 100,
          });
        }
      }
    };

    const resize = () => {
      W = window.innerWidth;
      H = window.innerHeight;
      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);
      buildDots();
    };

    resize();
    window.addEventListener("resize", resize);

    let last = 0;
    const loop = (t) => {
      if (t - last < 33) {
        rafRef.current = requestAnimationFrame(loop);
        return;
      }
      last = t;
      const time = t / 1000;
      const isDark = document.documentElement.getAttribute("data-theme") === "dark";
      const baseColor = isDark ? "210, 240, 220" : "20, 22, 18";
      const accentColor = isDark ? "150, 240, 190" : "30, 130, 80";
      const baseAlpha = isDark ? 0.18 : 0.10;
      const breathe = isDark ? 0.06 : 0.03;
      const peakAlpha = isDark ? 0.85 : 0.70;
      const sizeBoost = isDark ? 2.6 : 1.8;
      const lineAlpha = isDark ? 0.55 : 0.35;
      const glowAlpha = isDark ? 0.22 : 0.10;

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

      // ── Drifting accent glow (slow lissajous) ──────────────────────────
      const gx = W * (0.5 + Math.sin(time * 0.07) * 0.35);
      const gy = H * (0.5 + Math.cos(time * 0.05) * 0.30);
      const gr = Math.max(W, H) * 0.55;
      const grad = ctx.createRadialGradient(gx, gy, 0, gx, gy, gr);
      grad.addColorStop(0, `rgba(${accentColor}, ${glowAlpha})`);
      grad.addColorStop(0.55, `rgba(${accentColor}, ${glowAlpha * 0.25})`);
      grad.addColorStop(1, `rgba(${accentColor}, 0)`);
      ctx.fillStyle = grad;
      ctx.fillRect(0, 0, W, H);

      const m = mouseRef.current;
      const radius = 240;
      const radiusSq = radius * radius;

      // Collect "hot" dots near the cursor for connection lines.
      const hot = [];
      const hotMax = 80;

      // ── Dots ────────────────────────────────────────────────────────────
      for (let i = 0; i < dots.length; i++) {
        const d = dots[i];
        // Subtle per-dot drift so the grid doesn't feel rigid
        const wob = Math.sin(time * 0.4 + d.ph * 6.28);
        const dx0 = wob * 1.4;
        const dy0 = Math.cos(time * 0.35 + d.ph * 6.28) * 1.4;
        const px = d.x + dx0;
        const py = d.y + dy0;

        const dx = px - m.x;
        const dy = py - m.y;
        const distSq = dx * dx + dy * dy;
        let alpha = baseAlpha + Math.sin(time * 0.6 + d.ph * 6.28) * breathe;
        let size = 1;
        let color = baseColor;
        let f = 0;
        if (m.active && distSq < radiusSq) {
          f = 1 - distSq / radiusSq;
          alpha = baseAlpha + f * peakAlpha;
          size = 1 + f * sizeBoost;
          color = accentColor;
          if (hot.length < hotMax) hot.push({ x: px, y: py, f });
        }

        // Click-ripple boost — additive over the base dot
        let rippleA = 0;
        let rippleSize = 0;
        const ripples = ripplesRef.current;
        for (let j = 0; j < ripples.length; j++) {
          const rp = ripples[j];
          const age = time - rp.t;
          if (age < 0 || age > 1.4) continue;
          const rippleR = age * 700;
          const ringW = 80;
          const rdx = px - rp.x;
          const rdy = py - rp.y;
          const dist = Math.sqrt(rdx * rdx + rdy * rdy);
          const band = Math.max(0, 1 - Math.abs(dist - rippleR) / ringW);
          const fade = Math.max(0, 1 - age / 1.4);
          const k = band * fade;
          if (k > rippleA) {
            rippleA = k;
            rippleSize = k * 2.4;
          }
        }
        if (rippleA > 0) {
          alpha = Math.min(0.95, alpha + rippleA * 0.7);
          size += rippleSize;
          color = accentColor;
        }

        ctx.fillStyle = `rgba(${color}, ${alpha})`;
        ctx.beginPath();
        ctx.arc(px, py, size, 0, Math.PI * 2);
        ctx.fill();
      }

      // ── Constellation lines ────────────────────────────────────────────
      if (m.active && hot.length > 1) {
        ctx.lineWidth = 0.9;
        // Hot-to-hot
        const linkRadius = 60;
        const linkRadiusSq = linkRadius * linkRadius;
        for (let i = 0; i < hot.length; i++) {
          const a = hot[i];
          for (let j = i + 1; j < hot.length; j++) {
            const b = hot[j];
            const dx = a.x - b.x;
            const dy = a.y - b.y;
            const d2 = dx * dx + dy * dy;
            if (d2 > linkRadiusSq) continue;
            const k = 1 - d2 / linkRadiusSq;
            const la = lineAlpha * k * Math.min(a.f, b.f);
            ctx.strokeStyle = `rgba(${accentColor}, ${la})`;
            ctx.beginPath();
            ctx.moveTo(a.x, a.y);
            ctx.lineTo(b.x, b.y);
            ctx.stroke();
          }
        }
        // Hot-to-cursor (closest 12)
        hot.sort((a, b) => b.f - a.f);
        const cursorLinks = Math.min(hot.length, 12);
        for (let i = 0; i < cursorLinks; i++) {
          const a = hot[i];
          const la = lineAlpha * a.f * 0.9;
          ctx.strokeStyle = `rgba(${accentColor}, ${la})`;
          ctx.beginPath();
          ctx.moveTo(a.x, a.y);
          ctx.lineTo(m.x, m.y);
          ctx.stroke();
        }
        // Cursor halo
        const halo = ctx.createRadialGradient(m.x, m.y, 0, m.x, m.y, radius);
        halo.addColorStop(0, `rgba(${accentColor}, ${isDark ? 0.18 : 0.10})`);
        halo.addColorStop(1, `rgba(${accentColor}, 0)`);
        ctx.fillStyle = halo;
        ctx.beginPath();
        ctx.arc(m.x, m.y, radius, 0, Math.PI * 2);
        ctx.fill();
      }

      // ── Click ripples — expanding rings ────────────────────────────────
      const ripples = ripplesRef.current;
      for (let j = ripples.length - 1; j >= 0; j--) {
        const rp = ripples[j];
        const age = time - rp.t;
        if (age > 1.4) { ripples.splice(j, 1); continue; }
        const rRad = age * 700;
        const fade = Math.max(0, 1 - age / 1.4);
        ctx.strokeStyle = `rgba(${accentColor}, ${fade * (isDark ? 0.55 : 0.35)})`;
        ctx.lineWidth = 1.4 * fade;
        ctx.beginPath();
        ctx.arc(rp.x, rp.y, rRad, 0, Math.PI * 2);
        ctx.stroke();
      }

      rafRef.current = requestAnimationFrame(loop);
    };
    rafRef.current = requestAnimationFrame(loop);

    let mouseTimer = 0;
    const onMove = (e) => {
      mouseRef.current.x = e.clientX;
      mouseRef.current.y = e.clientY;
      mouseRef.current.active = true;
      clearTimeout(mouseTimer);
      mouseTimer = setTimeout(() => { mouseRef.current.active = false; }, 1500);
    };
    const onLeave = () => { mouseRef.current.active = false; };
    const onClick = (e) => {
      ripplesRef.current.push({ x: e.clientX, y: e.clientY, t: performance.now() / 1000 });
      if (ripplesRef.current.length > 6) ripplesRef.current.shift();
    };
    window.addEventListener("mousemove", onMove, { passive: true });
    window.addEventListener("mouseleave", onLeave);
    window.addEventListener("click", onClick, { passive: true });

    return () => {
      cancelAnimationFrame(rafRef.current);
      window.removeEventListener("resize", resize);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseleave", onLeave);
      window.removeEventListener("click", onClick);
    };
  }, []);

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

window.AmbientField = AmbientField;
