// NAXUS — Floating particle constellation background
// Canvas-based: dots float, lines connect nearby pairs.
// Sits fixed behind all content, respects bold/conservative modes.

const { useEffect, useRef } = React;

function ParticleField() {
  const canvasRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    let raf = 0;
    let particles = [];
    let mouse = { x: -9999, y: -9999, active: false };
    let dpr = Math.min(window.devicePixelRatio || 1, 2);

    function resize() {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      canvas.width = window.innerWidth * dpr;
      canvas.height = window.innerHeight * dpr;
      canvas.style.width = window.innerWidth + "px";
      canvas.style.height = window.innerHeight + "px";
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      seedParticles();
    }

    function seedParticles() {
      const area = window.innerWidth * window.innerHeight;
      // ~1 particle per 14000px² — gives ~140 on a 1920×1080 viewport
      const count = Math.min(180, Math.max(40, Math.round(area / 14000)));
      particles = [];
      for (let i = 0; i < count; i++) {
        particles.push({
          x: Math.random() * window.innerWidth,
          y: Math.random() * window.innerHeight,
          vx: (Math.random() - 0.5) * 0.25,
          vy: (Math.random() - 0.5) * 0.25,
          r: 0.8 + Math.random() * 1.6,
          // hue bias: cyan or magenta, with a few violet ones
          tone: Math.random() < 0.55 ? "cyan" : (Math.random() < 0.8 ? "magenta" : "violet"),
          twinkle: Math.random() * Math.PI * 2,
        });
      }
    }

    const COLORS = {
      cyan:    { r: 34,  g: 230, b: 255 },
      magenta: { r: 255, g: 94,  b: 201 },
      violet:  { r: 138, g: 107, b: 255 },
    };

    function step(now) {
      const W = window.innerWidth;
      const H = window.innerHeight;
      const isBold = document.documentElement.getAttribute("data-mode") !== "conservative";
      const opMul = isBold ? 1 : 0.55;
      const linkDist = isBold ? 130 : 110;
      const linkDistSq = linkDist * linkDist;

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

      // Update positions
      for (const p of particles) {
        p.x += p.vx;
        p.y += p.vy;
        p.twinkle += 0.02;

        // Wrap around edges
        if (p.x < -10) p.x = W + 10;
        if (p.x > W + 10) p.x = -10;
        if (p.y < -10) p.y = H + 10;
        if (p.y > H + 10) p.y = -10;

        // Mouse repulsion (gentle)
        if (mouse.active) {
          const dx = p.x - mouse.x;
          const dy = p.y - mouse.y;
          const d2 = dx * dx + dy * dy;
          if (d2 < 18000) {
            const f = (1 - d2 / 18000) * 0.6;
            const d = Math.sqrt(d2) || 1;
            p.vx += (dx / d) * f * 0.15;
            p.vy += (dy / d) * f * 0.15;
          }
        }

        // Velocity damping so particles don't accelerate forever
        p.vx *= 0.985;
        p.vy *= 0.985;
        // Keep some minimum drift
        if (Math.abs(p.vx) < 0.05) p.vx += (Math.random() - 0.5) * 0.04;
        if (Math.abs(p.vy) < 0.05) p.vy += (Math.random() - 0.5) * 0.04;
      }

      // Connection lines (constellation)
      ctx.lineWidth = 0.6;
      for (let i = 0; i < particles.length; i++) {
        const a = particles[i];
        for (let j = i + 1; j < particles.length; j++) {
          const b = particles[j];
          const dx = a.x - b.x;
          const dy = a.y - b.y;
          const d2 = dx * dx + dy * dy;
          if (d2 < linkDistSq) {
            const alpha = (1 - d2 / linkDistSq) * 0.28 * opMul;
            // Mix the two endpoint colors
            const ca = COLORS[a.tone];
            const cb = COLORS[b.tone];
            const r = (ca.r + cb.r) / 2 | 0;
            const g = (ca.g + cb.g) / 2 | 0;
            const bl = (ca.b + cb.b) / 2 | 0;
            ctx.strokeStyle = `rgba(${r},${g},${bl},${alpha})`;
            ctx.beginPath();
            ctx.moveTo(a.x, a.y);
            ctx.lineTo(b.x, b.y);
            ctx.stroke();
          }
        }
      }

      // Mouse-to-particle lines
      if (mouse.active) {
        for (const p of particles) {
          const dx = p.x - mouse.x;
          const dy = p.y - mouse.y;
          const d2 = dx * dx + dy * dy;
          if (d2 < 22000) {
            const alpha = (1 - d2 / 22000) * 0.5 * opMul;
            ctx.strokeStyle = `rgba(34, 230, 255, ${alpha})`;
            ctx.lineWidth = 0.8;
            ctx.beginPath();
            ctx.moveTo(p.x, p.y);
            ctx.lineTo(mouse.x, mouse.y);
            ctx.stroke();
          }
        }
      }

      // Draw particles
      for (const p of particles) {
        const c = COLORS[p.tone];
        const tw = 0.6 + 0.4 * Math.sin(p.twinkle);
        const a = 0.85 * tw * opMul;

        // Glow halo
        const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.r * 6);
        grad.addColorStop(0, `rgba(${c.r},${c.g},${c.b},${a})`);
        grad.addColorStop(1, `rgba(${c.r},${c.g},${c.b},0)`);
        ctx.fillStyle = grad;
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.r * 6, 0, Math.PI * 2);
        ctx.fill();

        // Solid core
        ctx.fillStyle = `rgba(${c.r},${c.g},${c.b},${Math.min(1, a + 0.2)})`;
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
        ctx.fill();
      }

      raf = requestAnimationFrame(step);
    }

    function onMove(e) {
      mouse.x = e.clientX;
      mouse.y = e.clientY;
      mouse.active = true;
    }
    function onLeave() {
      mouse.active = false;
    }
    function onTouch(e) {
      if (e.touches && e.touches[0]) {
        mouse.x = e.touches[0].clientX;
        mouse.y = e.touches[0].clientY;
        mouse.active = true;
      }
    }

    resize();
    raf = requestAnimationFrame(step);
    window.addEventListener("resize", resize);
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseleave", onLeave);
    window.addEventListener("touchmove", onTouch, { passive: true });
    window.addEventListener("touchend", onLeave);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", resize);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseleave", onLeave);
      window.removeEventListener("touchmove", onTouch);
      window.removeEventListener("touchend", onLeave);
    };
  }, []);

  return <canvas ref={canvasRef} className="particle-field" />;
}

window.ParticleField = ParticleField;
