/* FieldVisual — magnetic orb field. Cursor attracts; triple-click forms ribbon. */
const FieldVisual = (function() {
  const { useState, useEffect, useRef } = React;

  function Visual({ onEgg, reduceMotion, accent }) {
    const wrapRef = useRef(null);
    const canvasRef = useRef(null);
    const mouseRef = useRef({ x: -1000, y: -1000, has: false });
    const formRef = useRef({ active: false, t: 0 }); // forming ribbon
    const clickTimes = useRef([]);

    useEffect(() => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const dpr = window.devicePixelRatio || 1;
      const ctx = canvas.getContext("2d");

      let W = 0, H = 0;
      const resize = () => {
        const r = wrapRef.current.getBoundingClientRect();
        W = r.width; H = r.height;
        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);
      };
      resize();
      const ro = new ResizeObserver(resize);
      ro.observe(wrapRef.current);

      // Spawn orbs
      const N = 60;
      const palette = ["#7CD4FD", "#84caff", "#a4bcfd", "#fec84b", "#75e0a7"];
      const orbs = Array.from({length: N}, (_, i) => ({
        x: Math.random() * W,
        y: Math.random() * H,
        vx: (Math.random() - 0.5) * 0.15,
        vy: (Math.random() - 0.5) * 0.15,
        r: 4 + Math.random() * 14,
        c: palette[i % palette.length],
        phase: Math.random() * Math.PI * 2,
        speed: 0.4 + Math.random() * 0.6,
      }));

      // Target positions for "form ribbon"
      // Sample a lemniscate / infinity curve
      const ribbonTargets = [];
      for (let i = 0; i < N; i++) {
        const t = (i / N) * Math.PI * 2;
        const scale = Math.min(W, H) * 0.32;
        // Lemniscate of Bernoulli
        const x = (scale * Math.cos(t)) / (1 + Math.sin(t)*Math.sin(t));
        const y = (scale * Math.sin(t) * Math.cos(t)) / (1 + Math.sin(t)*Math.sin(t));
        ribbonTargets.push({ x, y });
      }

      let raf;
      const draw = (now) => {
        ctx.clearRect(0, 0, W, H);

        // Background gradient
        const g = ctx.createRadialGradient(W*0.4, H*0.5, 0, W*0.4, H*0.5, Math.max(W,H));
        g.addColorStop(0, "#182230");
        g.addColorStop(1, "#0c111d");
        ctx.fillStyle = g;
        ctx.fillRect(0, 0, W, H);

        const cx = W / 2, cy = H / 2;

        // Update form animation
        if (formRef.current.active) {
          formRef.current.t = Math.min(1, formRef.current.t + 0.012);
          if (formRef.current.t >= 1 && now - formRef.current.startedAt > 2400) {
            formRef.current.active = false;
            formRef.current.t = 0;
          }
        } else if (formRef.current.t > 0) {
          formRef.current.t = Math.max(0, formRef.current.t - 0.008);
        }

        const formK = formRef.current.t; // 0..1

        // Update + draw orbs
        for (let i = 0; i < orbs.length; i++) {
          const o = orbs[i];

          // Heartbeat pulse
          const pulse = 1 + Math.sin(now * 0.0015 + o.phase) * 0.12;

          if (!reduceMotion) {
            // Cursor magnetism
            if (mouseRef.current.has) {
              const dx = mouseRef.current.x - o.x;
              const dy = mouseRef.current.y - o.y;
              const dist = Math.sqrt(dx*dx + dy*dy);
              if (dist < 220) {
                const f = (1 - dist / 220) * 0.08;
                o.vx += (dx / dist) * f;
                o.vy += (dy / dist) * f;
              }
            }

            // Drift toward ribbon target if forming
            if (formK > 0) {
              const tx = cx + ribbonTargets[i].x;
              const ty = cy + ribbonTargets[i].y;
              o.vx += (tx - o.x) * 0.004 * formK;
              o.vy += (ty - o.y) * 0.004 * formK;
              // Damping when close
              o.vx *= (1 - 0.05 * formK);
              o.vy *= (1 - 0.05 * formK);
            } else {
              // Gentle drift
              o.vx *= 0.98; o.vy *= 0.98;
              o.vx += (Math.random() - 0.5) * 0.02;
              o.vy += (Math.random() - 0.5) * 0.02;
            }

            o.x += o.vx; o.y += o.vy;
            // Bounce
            if (o.x < -50) o.x = W + 50;
            if (o.x > W + 50) o.x = -50;
            if (o.y < -50) o.y = H + 50;
            if (o.y > H + 50) o.y = -50;
          }

          // Glow
          const r = o.r * pulse;
          const grad = ctx.createRadialGradient(o.x, o.y, 0, o.x, o.y, r * 4);
          grad.addColorStop(0, o.c + "cc");
          grad.addColorStop(0.4, o.c + "33");
          grad.addColorStop(1, o.c + "00");
          ctx.fillStyle = grad;
          ctx.beginPath();
          ctx.arc(o.x, o.y, r * 4, 0, Math.PI * 2);
          ctx.fill();

          // Core
          ctx.fillStyle = "#ffffffee";
          ctx.beginPath();
          ctx.arc(o.x, o.y, r * 0.4, 0, Math.PI * 2);
          ctx.fill();
        }

        // Connecting lines when forming
        if (formK > 0.3) {
          ctx.strokeStyle = `rgba(132, 202, 255, ${(formK - 0.3) * 0.6})`;
          ctx.lineWidth = 1;
          for (let i = 0; i < orbs.length; i++) {
            const a = orbs[i], b = orbs[(i+1) % orbs.length];
            ctx.beginPath();
            ctx.moveTo(a.x, a.y);
            ctx.lineTo(b.x, b.y);
            ctx.stroke();
          }
        }

        raf = requestAnimationFrame(draw);
      };
      raf = requestAnimationFrame(draw);

      const onMove = (e) => {
        const r = wrapRef.current.getBoundingClientRect();
        mouseRef.current.x = e.clientX - r.left;
        mouseRef.current.y = e.clientY - r.top;
        mouseRef.current.has = true;
      };
      const onLeave = () => { mouseRef.current.has = false; };
      wrapRef.current.addEventListener("mousemove", onMove);
      wrapRef.current.addEventListener("mouseleave", onLeave);

      return () => {
        cancelAnimationFrame(raf);
        ro.disconnect();
        if (wrapRef.current) {
          wrapRef.current.removeEventListener("mousemove", onMove);
          wrapRef.current.removeEventListener("mouseleave", onLeave);
        }
      };
    }, [reduceMotion]);

    const onClick = () => {
      const t = Date.now();
      clickTimes.current = [...clickTimes.current.filter((x) => t - x < 600), t];
      if (clickTimes.current.length >= 3) {
        clickTimes.current = [];
        formRef.current.active = true;
        formRef.current.startedAt = performance.now();
        onEgg && onEgg("∞ — found it");
      }
    };

    return (
      <div ref={wrapRef} className="layer" onClick={onClick} style={{cursor:'pointer'}}>
        <canvas ref={canvasRef}/>
      </div>
    );
  }

  return Visual;
})();

window.FieldVisual = FieldVisual;
