// Cursor "lantern" — the signature interaction. The cursor leaves a slow-fading
// ink trail; objects in the scene have visibility = proximity to the trail.
//
// Implementation: a single fixed-position canvas above the scene paints a soft
// red radial under the cursor and decays old paint. Scene elements compute
// their own opacity from the live (x,y) and a configurable revealRadius.
//
// Exposes via context: { x, y, on } so any descendant can do reveal logic.

const LanternContext = React.createContext({ x: -9999, y: -9999, on: true });

function LanternProvider({ children, intensity = 1, color = 'rgba(232, 80, 90, .85)' }) {
  const [pos, setPos] = React.useState({ x: -9999, y: -9999, on: false });
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(0);
  const trailRef = React.useRef([]);
  // Throttle setPos to ~60fps via rAF — descendants reveal off this state, and
  // pointermove fires faster than that on high-poll mice.
  const pendingPos = React.useRef(null);

  React.useEffect(() => {
    let raf;
    const flush = () => {
      raf = null;
      if (pendingPos.current) { setPos(pendingPos.current); pendingPos.current = null; }
    };
    const onMove = (e) => {
      pendingPos.current = { x: e.clientX, y: e.clientY, on: true };
      trailRef.current.push({ x: e.clientX, y: e.clientY, t: performance.now() });
      if (trailRef.current.length > 60) trailRef.current.shift();
      if (!raf) raf = requestAnimationFrame(flush);
    };
    const onLeave = () => setPos((p) => ({ ...p, on: false }));
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseleave', onLeave);
    return () => {
      if (raf) cancelAnimationFrame(raf);
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseleave', onLeave);
    };
  }, []);

  React.useEffect(() => {
    const c = canvasRef.current;
    if (!c) return;
    const resize = () => {
      const dpr = Math.min(2, window.devicePixelRatio || 1);
      c.width = window.innerWidth * dpr;
      c.height = window.innerHeight * dpr;
      c.style.width = window.innerWidth + 'px';
      c.style.height = window.innerHeight + 'px';
      const ctx = c.getContext('2d');
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    window.addEventListener('resize', resize);

    const ctx = c.getContext('2d');
    const tick = () => {
      // Faster fade so the warm halo follows the cursor instead of smearing.
      ctx.globalCompositeOperation = 'destination-out';
      ctx.fillStyle = 'rgba(0,0,0,0.12)';
      ctx.fillRect(0, 0, window.innerWidth, window.innerHeight);

      // Add new ink at recent trail points + a strong primary halo at the head.
      ctx.globalCompositeOperation = 'lighter';
      const now = performance.now();
      const baseRadius = 320 * intensity;
      const trail = trailRef.current;
      for (let i = 0; i < trail.length; i++) {
        const p = trail[i];
        const age = (now - p.t) / 900; // 0.9s lifetime
        if (age > 1) continue;
        // Head of trail (most recent) is brightest.
        const head = i / Math.max(1, trail.length - 1);
        const a = (1 - age) * 0.42 * intensity * (0.4 + head * 0.6);
        const r = baseRadius * (1 - age * 0.3);
        const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r);
        grad.addColorStop(0, color.replace(/[\d.]+\)$/, `${a})`));
        grad.addColorStop(0.5, color.replace(/[\d.]+\)$/, `${a * 0.3})`));
        grad.addColorStop(1, 'rgba(0,0,0,0)');
        ctx.fillStyle = grad;
        ctx.beginPath();
        ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
        ctx.fill();
      }
      rafRef.current = requestAnimationFrame(tick);
    };
    tick();
    return () => {
      cancelAnimationFrame(rafRef.current);
      window.removeEventListener('resize', resize);
    };
  }, [intensity, color]);

  return (
    <LanternContext.Provider value={pos}>
      <canvas ref={canvasRef} className="lantern-canvas" />
      {children}
    </LanternContext.Provider>
  );
}

// Hook: returns 0..1 reveal strength based on cursor proximity to a DOM ref.
// Larger default radius so things actually light up at a usable distance.
function useReveal(ref, radius = 420) {
  const lantern = React.useContext(LanternContext);
  const [reveal, setReveal] = React.useState(0);
  React.useEffect(() => {
    if (!ref.current || !lantern.on) { setReveal(0); return; }
    const r = ref.current.getBoundingClientRect();
    const cx = r.left + r.width / 2;
    const cy = r.top + r.height / 2;
    const d = Math.hypot(lantern.x - cx, lantern.y - cy);
    // Smooth easing — feels lantern-shaped rather than linear.
    const t = Math.max(0, Math.min(1, 1 - d / radius));
    setReveal(t * t * (3 - 2 * t));
  }, [lantern.x, lantern.y, lantern.on, ref, radius]);
  return reveal;
}

Object.assign(window, { LanternProvider, LanternContext, useReveal });
