/* RINAU HEX — Cybernetic Particle Constellation
   Canvas-based interactive shader-feel:
   - particles drift, wrap edges, connect to nearby neighbors
   - mouse REPELS nearby particles and pulls red link-lines from cursor
   - click fires a teal shockwave + red sparks + outward impulse
   - data-packet "blips" travel along random edges for cyber-feel
*/

function ParticleConstellation({
  teal = '#14B8A6',
  red  = '#EF4444',
  density = 1, // multiplier on auto-calculated count
}) {
  const canvasRef = React.useRef(null);

  React.useEffect(() => {
    const cvs = canvasRef.current;
    if (!cvs) return;
    const ctx = cvs.getContext('2d');
    const dpr = Math.min(window.devicePixelRatio || 1, 2);

    const st = {
      particles: [],
      blips: [],   // {a, b, t, life} traveling along edges
      ripples: [], // {x, y, r, alpha}
      sparks: [],  // {x, y, vx, vy, life}
      mouse: { x: -9999, y: -9999, active: false, down: false },
      w: 0, h: 0,
      raf: 0,
    };

    function resize() {
      const rect = cvs.getBoundingClientRect();
      st.w = rect.width;
      st.h = rect.height;
      cvs.width  = Math.max(1, Math.floor(rect.width * dpr));
      cvs.height = Math.max(1, Math.floor(rect.height * dpr));
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      seed();
    }

    function seed() {
      const auto = Math.floor((st.w * st.h) / 14000);
      const N = Math.max(40, Math.min(140, Math.floor(auto * density)));
      st.particles = [];
      for (let i = 0; i < N; i++) {
        st.particles.push({
          x: Math.random() * st.w,
          y: Math.random() * st.h,
          vx: (Math.random() - 0.5) * 0.22,
          vy: (Math.random() - 0.5) * 0.22,
          r: Math.random() < 0.92 ? 1.2 : 2.2,
          accent: Math.random() < 0.08, // small % red accent particles
          phase: Math.random() * Math.PI * 2,
        });
      }
      st.blips = [];
    }

    function onMove(e) {
      const rect = cvs.getBoundingClientRect();
      st.mouse.x = e.clientX - rect.left;
      st.mouse.y = e.clientY - rect.top;
      st.mouse.active = true;
    }
    function onLeave() { st.mouse.active = false; }
    function onDown(e) {
      const rect = cvs.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      st.ripples.push({ x, y, r: 0, alpha: 1 });
      // sparks
      const NS = 22;
      for (let i = 0; i < NS; i++) {
        const a = (Math.PI * 2 * i) / NS + Math.random() * 0.5;
        const s = 2 + Math.random() * 3.5;
        st.sparks.push({ x, y, vx: Math.cos(a) * s, vy: Math.sin(a) * s, life: 1, hue: Math.random() < 0.6 ? red : teal });
      }
      // push particles outward
      st.particles.forEach(p => {
        const dx = p.x - x, dy = p.y - y;
        const d2 = dx*dx + dy*dy;
        if (d2 < 240 * 240) {
          const d = Math.sqrt(d2) || 1;
          const f = (240 - d) / 240 * 7;
          p.vx += (dx / d) * f;
          p.vy += (dy / d) * f;
        }
      });
    }

    function spawnBlip() {
      const ps = st.particles;
      if (ps.length < 2) return;
      const i = Math.floor(Math.random() * ps.length);
      const a = ps[i];
      // find a nearby neighbor
      let best = -1, bestD = 1e9;
      for (let j = 0; j < ps.length; j++) {
        if (j === i) continue;
        const dx = ps[j].x - a.x, dy = ps[j].y - a.y;
        const d2 = dx*dx + dy*dy;
        if (d2 < bestD && d2 < 130 * 130) { bestD = d2; best = j; }
      }
      if (best >= 0) st.blips.push({ a: i, b: best, t: 0, life: 1, speed: 0.012 + Math.random() * 0.02 });
    }

    cvs.addEventListener('mousemove', onMove);
    cvs.addEventListener('mouseleave', onLeave);
    cvs.addEventListener('mousedown', onDown);
    window.addEventListener('resize', resize);
    resize();

    let last = performance.now();
    let blipTimer = 0;

    function frame(now) {
      const dt = Math.min(33, now - last); last = now;
      ctx.clearRect(0, 0, st.w, st.h);

      const ps = st.particles;

      // update particles
      for (let p of ps) {
        if (st.mouse.active) {
          const dx = p.x - st.mouse.x, dy = p.y - st.mouse.y;
          const d2 = dx*dx + dy*dy;
          if (d2 < 170 * 170) {
            const d = Math.sqrt(d2) || 1;
            const f = (170 - d) / 170 * 0.09;
            p.vx += (dx / d) * f;
            p.vy += (dy / d) * f;
          }
        }
        p.vx *= 0.985;
        p.vy *= 0.985;
        // micro-jitter so it never feels frozen
        p.phase += 0.01;
        p.x += p.vx + Math.cos(p.phase) * 0.04;
        p.y += p.vy + Math.sin(p.phase * 0.9) * 0.04;
        // wrap
        if (p.x < -12) p.x = st.w + 12;
        if (p.x > st.w + 12) p.x = -12;
        if (p.y < -12) p.y = st.h + 12;
        if (p.y > st.h + 12) p.y = -12;
      }

      // draw constellation lines
      ctx.lineWidth = 0.7;
      const maxLine = 115;
      for (let i = 0; i < ps.length; i++) {
        const a = ps[i];
        for (let j = i + 1; j < ps.length; j++) {
          const b = ps[j];
          const dx = a.x - b.x, dy = a.y - b.y;
          const d2 = dx*dx + dy*dy;
          if (d2 < maxLine * maxLine) {
            const d = Math.sqrt(d2);
            const alpha = (1 - d / maxLine) * 0.38;
            ctx.strokeStyle = `rgba(20,184,166,${alpha.toFixed(3)})`;
            ctx.beginPath();
            ctx.moveTo(a.x, a.y);
            ctx.lineTo(b.x, b.y);
            ctx.stroke();
          }
        }
        // cursor link-lines (red)
        if (st.mouse.active) {
          const dx = a.x - st.mouse.x, dy = a.y - st.mouse.y;
          const d2 = dx*dx + dy*dy;
          const maxM = 170;
          if (d2 < maxM * maxM) {
            const d = Math.sqrt(d2);
            const alpha = (1 - d / maxM) * 0.55;
            ctx.strokeStyle = `rgba(239,68,68,${alpha.toFixed(3)})`;
            ctx.lineWidth = 0.9;
            ctx.beginPath();
            ctx.moveTo(a.x, a.y);
            ctx.lineTo(st.mouse.x, st.mouse.y);
            ctx.stroke();
            ctx.lineWidth = 0.7;
          }
        }
      }

      // blips traveling along edges (cyber data packets)
      blipTimer += dt;
      if (blipTimer > 220) { blipTimer = 0; if (st.blips.length < 14) spawnBlip(); }
      st.blips = st.blips.filter(b => {
        b.t += b.speed;
        return b.t < 1 && ps[b.a] && ps[b.b];
      });
      for (let bl of st.blips) {
        const A = ps[bl.a], B = ps[bl.b];
        if (!A || !B) continue;
        const x = A.x + (B.x - A.x) * bl.t;
        const y = A.y + (B.y - A.y) * bl.t;
        ctx.fillStyle = teal;
        ctx.shadowColor = teal;
        ctx.shadowBlur = 8;
        ctx.fillRect(x - 1.4, y - 1.4, 2.8, 2.8);
        ctx.shadowBlur = 0;
      }

      // draw particles (diamonds)
      for (let p of ps) {
        ctx.save();
        ctx.translate(p.x, p.y);
        ctx.rotate(Math.PI / 4);
        if (p.accent) {
          ctx.fillStyle = red;
          ctx.shadowColor = red;
          ctx.shadowBlur = 10;
        } else {
          ctx.fillStyle = teal;
        }
        const s = p.r;
        ctx.fillRect(-s, -s, s * 2, s * 2);
        ctx.restore();
      }

      // ripples (teal shockwaves)
      st.ripples = st.ripples.filter(r => r.alpha > 0.02);
      for (let r of st.ripples) {
        r.r += 4.5;
        r.alpha *= 0.955;
        ctx.strokeStyle = `rgba(20,184,166,${r.alpha.toFixed(3)})`;
        ctx.lineWidth = 1.3;
        ctx.beginPath();
        ctx.arc(r.x, r.y, r.r, 0, Math.PI * 2);
        ctx.stroke();
        // inner ring
        if (r.r > 14) {
          ctx.strokeStyle = `rgba(239,68,68,${(r.alpha * 0.7).toFixed(3)})`;
          ctx.beginPath();
          ctx.arc(r.x, r.y, r.r - 14, 0, Math.PI * 2);
          ctx.stroke();
        }
      }

      // sparks
      st.sparks = st.sparks.filter(s => s.life > 0.02);
      for (let s of st.sparks) {
        s.x += s.vx; s.y += s.vy;
        s.vx *= 0.95; s.vy *= 0.95;
        s.life *= 0.94;
        const rgba = s.hue === red
          ? `rgba(239,68,68,${s.life.toFixed(3)})`
          : `rgba(20,184,166,${s.life.toFixed(3)})`;
        ctx.fillStyle = rgba;
        ctx.fillRect(s.x - 1.3, s.y - 1.3, 2.6, 2.6);
      }

      // cursor crosshair when active
      if (st.mouse.active) {
        ctx.strokeStyle = 'rgba(20,184,166,0.55)';
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.arc(st.mouse.x, st.mouse.y, 6, 0, Math.PI * 2);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(st.mouse.x - 14, st.mouse.y);
        ctx.lineTo(st.mouse.x - 7,  st.mouse.y);
        ctx.moveTo(st.mouse.x + 7,  st.mouse.y);
        ctx.lineTo(st.mouse.x + 14, st.mouse.y);
        ctx.moveTo(st.mouse.x, st.mouse.y - 14);
        ctx.lineTo(st.mouse.x, st.mouse.y - 7);
        ctx.moveTo(st.mouse.x, st.mouse.y + 7);
        ctx.lineTo(st.mouse.x, st.mouse.y + 14);
        ctx.stroke();
      }

      st.raf = requestAnimationFrame(frame);
    }
    st.raf = requestAnimationFrame(frame);

    return () => {
      cancelAnimationFrame(st.raf);
      cvs.removeEventListener('mousemove', onMove);
      cvs.removeEventListener('mouseleave', onLeave);
      cvs.removeEventListener('mousedown', onDown);
      window.removeEventListener('resize', resize);
    };
  }, [teal, red, density]);

  return (
    <canvas
      ref={canvasRef}
      className="absolute inset-0 w-full h-full block cursor-crosshair"
      style={{ touchAction: 'none' }}
    />
  );
}

window.ParticleConstellation = ParticleConstellation;
