/* global React */
// Standalone Particles network background — usable across pages.
// Registers window.ParticlesBackground.

(function () {
  const { useRef, useEffect } = React;
  function ParticlesBackground() {
    const canvasRef = useRef(null);
    useEffect(() => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const ctx = canvas.getContext("2d");
      const reduced = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      let particles = [];
      let sparks = [];     // accent shimmering particles
      let ripples = [];    // mouse click ripples
      let mouse = { x: -9999, y: -9999, active: false, vx: 0, vy: 0, prevX: 0, prevY: 0 };
      let raf = null;
      let w = 0, h = 0;
      let frame = 0;
      let scrollOffset = 0;
      let lastScroll = window.scrollY || 0;

      function size() {
        w = window.innerWidth;
        h = window.innerHeight;
        canvas.width = Math.floor(w * dpr);
        canvas.height = Math.floor(h * dpr);
        canvas.style.width = w + "px";
        canvas.style.height = h + "px";
        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      }

      function spawn() {
        const dense = w < 720 ? 70 : (w < 1100 ? 130 : 180);
        particles = Array.from({ length: dense }, () => ({
          x: Math.random() * w,
          y: Math.random() * h,
          vx: (Math.random() - 0.5) * 0.36,
          vy: (Math.random() - 0.5) * 0.36,
          r: 1.4 + Math.random() * 2.0,
          phase: Math.random() * Math.PI * 2,    // pulse phase
          freq: 0.012 + Math.random() * 0.018,   // pulse frequency
          accent: Math.random() < 0.10,          // 10% are accent (gold)
        }));
        // Pre-spawn some sparks
        sparks = [];
      }

      function spawnSpark() {
        sparks.push({
          x: Math.random() * w,
          y: Math.random() * h,
          life: 0,
          maxLife: 90 + Math.random() * 60,
          r: 0.8 + Math.random() * 1.4,
        });
      }

      function tick() {
        frame++;
        // gentle global wave field strength based on time
        const t = frame * 0.005;

        // detect scroll motion for parallax kick
        const ny = window.scrollY || 0;
        const scrollDelta = ny - lastScroll;
        lastScroll = ny;
        scrollOffset += scrollDelta * 0.04;
        scrollOffset *= 0.86;

        ctx.clearRect(0, 0, w, h);
        const NAVY = "30, 42, 120";
        const ACCENT = "201, 169, 110";
        const MAX_LINK = 142;
        const MAX_MOUSE = 180;

        // occasionally spawn a spark
        if (frame % 20 === 0 && sparks.length < 18) spawnSpark();

        // mouse velocity
        mouse.vx = mouse.x - mouse.prevX;
        mouse.vy = mouse.y - mouse.prevY;
        mouse.prevX = mouse.x; mouse.prevY = mouse.y;

        for (const p of particles) {
          // drift + sin wave field
          p.x += p.vx + Math.sin(t + p.phase) * 0.08;
          p.y += p.vy + Math.cos(t * 0.85 + p.phase) * 0.08 - scrollOffset * 0.02;

          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;

          if (mouse.active) {
            const dx = p.x - mouse.x, dy = p.y - mouse.y;
            const d2 = dx * dx + dy * dy;
            if (d2 < MAX_MOUSE * MAX_MOUSE) {
              const d = Math.sqrt(d2) || 1;
              const f = (1 - d / MAX_MOUSE) * 0.26;
              p.vx += (dx / d) * f;
              p.vy += (dy / d) * f;
              const speed = Math.sqrt(p.vx * p.vx + p.vy * p.vy);
              const cap = 1.0;
              if (speed > cap) { p.vx = (p.vx / speed) * cap; p.vy = (p.vy / speed) * cap; }
            }
          }
          p.vx *= 0.992; p.vy *= 0.992;
          if (Math.abs(p.vx) < 0.05) p.vx += (Math.random() - 0.5) * 0.05;
          if (Math.abs(p.vy) < 0.05) p.vy += (Math.random() - 0.5) * 0.05;
        }

        // Network lines (between particles)
        for (let i = 0; i < particles.length; i++) {
          for (let j = i + 1; j < particles.length; j++) {
            const a = particles[i], b = particles[j];
            const dx = a.x - b.x, dy = a.y - b.y;
            const d2 = dx * dx + dy * dy;
            if (d2 < MAX_LINK * MAX_LINK) {
              const d = Math.sqrt(d2);
              const baseAlpha = (1 - d / MAX_LINK) * 0.72;
              // gentle pulse on lines
              const pulse = 0.85 + 0.15 * Math.sin(t * 1.4 + (a.phase + b.phase) * 0.5);
              const alpha = baseAlpha * pulse;
              const accent = a.accent && b.accent;
              ctx.strokeStyle = accent ? `rgba(${ACCENT}, ${alpha})` : `rgba(${NAVY}, ${alpha})`;
              ctx.lineWidth = 1.0;
              ctx.beginPath();
              ctx.moveTo(a.x, a.y);
              ctx.lineTo(b.x, b.y);
              ctx.stroke();
            }
          }
        }

        // Mouse-particle accent links
        if (mouse.active) {
          for (const p of particles) {
            const dx = p.x - mouse.x, dy = p.y - mouse.y;
            const d2 = dx * dx + dy * dy;
            if (d2 < MAX_MOUSE * MAX_MOUSE) {
              const d = Math.sqrt(d2);
              const alpha = (1 - d / MAX_MOUSE) * 0.55;
              ctx.strokeStyle = `rgba(${ACCENT}, ${alpha})`;
              ctx.lineWidth = 1.0;
              ctx.beginPath();
              ctx.moveTo(p.x, p.y);
              ctx.lineTo(mouse.x, mouse.y);
              ctx.stroke();
            }
          }
        }

        // Ripples
        for (let i = ripples.length - 1; i >= 0; i--) {
          const r = ripples[i];
          r.life++;
          const progress = r.life / r.maxLife;
          if (progress >= 1) {
            ripples.splice(i, 1);
            continue;
          }
          const radius = r.maxRadius * easeOutQuad(progress);
          const alpha = (1 - progress) * 0.45;
          ctx.strokeStyle = `rgba(${ACCENT}, ${alpha})`;
          ctx.lineWidth = 1.6 - progress * 1.0;
          ctx.beginPath();
          ctx.arc(r.x, r.y, radius, 0, Math.PI * 2);
          ctx.stroke();
        }

        // Particles (with pulse)
        for (const p of particles) {
          const pulseR = p.r * (0.85 + 0.30 * Math.sin(t * 2 + p.phase));
          if (p.accent) {
            // glow accent
            const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, pulseR * 4);
            grad.addColorStop(0, `rgba(${ACCENT}, 0.95)`);
            grad.addColorStop(0.4, `rgba(${ACCENT}, 0.35)`);
            grad.addColorStop(1, `rgba(${ACCENT}, 0)`);
            ctx.fillStyle = grad;
            ctx.beginPath();
            ctx.arc(p.x, p.y, pulseR * 4, 0, Math.PI * 2);
            ctx.fill();
            ctx.fillStyle = `rgba(${ACCENT}, 0.98)`;
            ctx.beginPath();
            ctx.arc(p.x, p.y, pulseR, 0, Math.PI * 2);
            ctx.fill();
          } else {
            ctx.fillStyle = `rgba(${NAVY}, 0.95)`;
            ctx.beginPath();
            ctx.arc(p.x, p.y, pulseR, 0, Math.PI * 2);
            ctx.fill();
          }
        }

        // Sparks (twinkling stars)
        for (let i = sparks.length - 1; i >= 0; i--) {
          const s = sparks[i];
          s.life++;
          if (s.life >= s.maxLife) { sparks.splice(i, 1); continue; }
          const progress = s.life / s.maxLife;
          const fade = Math.sin(progress * Math.PI); // 0->1->0
          const alpha = fade * 0.85;
          // small bright dot with glow
          const grad = ctx.createRadialGradient(s.x, s.y, 0, s.x, s.y, s.r * 6);
          grad.addColorStop(0, `rgba(255, 245, 220, ${alpha})`);
          grad.addColorStop(0.4, `rgba(${ACCENT}, ${alpha * 0.4})`);
          grad.addColorStop(1, `rgba(${ACCENT}, 0)`);
          ctx.fillStyle = grad;
          ctx.beginPath();
          ctx.arc(s.x, s.y, s.r * 6, 0, Math.PI * 2);
          ctx.fill();
          // bright core
          ctx.fillStyle = `rgba(255, 250, 230, ${alpha})`;
          ctx.beginPath();
          ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
          ctx.fill();
          // cross flare
          ctx.strokeStyle = `rgba(255, 245, 220, ${alpha * 0.7})`;
          ctx.lineWidth = 0.7;
          ctx.beginPath();
          ctx.moveTo(s.x - s.r * 5, s.y);
          ctx.lineTo(s.x + s.r * 5, s.y);
          ctx.moveTo(s.x, s.y - s.r * 5);
          ctx.lineTo(s.x, s.y + s.r * 5);
          ctx.stroke();
        }

        raf = requestAnimationFrame(tick);
      }

      function easeOutQuad(t) { return 1 - (1 - t) * (1 - t); }

      function onMove(e) {
        const rect = canvas.getBoundingClientRect();
        mouse.x = e.clientX - rect.left;
        mouse.y = e.clientY - rect.top;
        mouse.active = mouse.x >= 0 && mouse.x <= w && mouse.y >= 0 && mouse.y <= h;
      }
      function onLeave() { mouse.active = false; }
      function onClick(e) {
        // Add ripple at click position
        const rect = canvas.getBoundingClientRect();
        const cx = e.clientX - rect.left;
        const cy = e.clientY - rect.top;
        if (cx < 0 || cx > w || cy < 0 || cy > h) return;
        ripples.push({ x: cx, y: cy, life: 0, maxLife: 60, maxRadius: 180 });
        // also push particles outward
        for (const p of particles) {
          const dx = p.x - cx, dy = p.y - cy;
          const d2 = dx * dx + dy * dy;
          if (d2 < 200 * 200) {
            const d = Math.sqrt(d2) || 1;
            const f = (1 - d / 200) * 2.5;
            p.vx += (dx / d) * f;
            p.vy += (dy / d) * f;
          }
        }
      }
      function onResize() { size(); spawn(); }

      size(); spawn();
      if (!reduced) tick();
      else {
        ctx.clearRect(0, 0, w, h);
        for (const p of particles) {
          ctx.fillStyle = "rgba(30, 42, 120, 0.55)";
          ctx.beginPath();
          ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
          ctx.fill();
        }
      }
      window.addEventListener("mousemove", onMove);
      window.addEventListener("mouseleave", onLeave);
      window.addEventListener("click", onClick);
      window.addEventListener("resize", onResize);
      return () => {
        if (raf) cancelAnimationFrame(raf);
        window.removeEventListener("mousemove", onMove);
        window.removeEventListener("mouseleave", onLeave);
        window.removeEventListener("click", onClick);
        window.removeEventListener("resize", onResize);
      };
    }, []);
    return <canvas ref={canvasRef} className="particles-canvas" aria-hidden="true" />;
  }
  window.ParticlesBackground = ParticlesBackground;
})();
