/* global React */
// Jullia — the hero backdrop: a generative, monochrome trace, not footage.
// Three hairline waveforms breathe behind the headline, same register as an
// oscilloscope reading a signal. On a fine pointer (desktop, trackpad) the
// trace answers to the cursor — amplitude and pitch bend toward where you
// look, "dynamics" made literal. On touch, there is no cursor to answer to,
// so the same trace follows a slow, fixed orbit instead — a closed loop, not
// a random idle, exactly as this brand's motion is always damped and never
// bouncy (see tokens/motion.css). Holds a single still frame for
// prefers-reduced-motion.

function useFinePointer() {
  const [fine, setFine] = React.useState(true);
  React.useEffect(() => {
    if (!window.matchMedia) return undefined;
    const mq = window.matchMedia('(pointer: fine)');
    const update = () => setFine(mq.matches);
    update();
    mq.addEventListener('change', update);
    return () => mq.removeEventListener('change', update);
  }, []);
  return fine;
}

const MUSIC_GRAIN_URL = "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>";

function MusicBackground({ style = {} }) {
  const wrapRef = React.useRef(null);
  const canvasRef = React.useRef(null);
  const isFinePointer = useFinePointer();
  const reduced = React.useMemo(
    () => !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches),
    []
  );

  React.useEffect(() => {
    const wrap = wrapRef.current;
    const canvas = canvasRef.current;
    if (!wrap || !canvas) return undefined;
    const ctx = canvas.getContext('2d');

    let w = 0, h = 0;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    const resize = () => {
      const rect = wrap.getBoundingClientRect();
      w = rect.width; h = rect.height;
      canvas.width = Math.max(1, w * dpr);
      canvas.height = Math.max(1, h * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(wrap);

    // pointer target, normalized to [-1, 1] on each axis
    const target = { x: 0, y: 0 };
    const current = { x: 0, y: 0 };

    const onMove = (e) => {
      const rect = wrap.getBoundingClientRect();
      target.x = Math.max(-1, Math.min(1, ((e.clientX - rect.left) / rect.width) * 2 - 1));
      target.y = Math.max(-1, Math.min(1, ((e.clientY - rect.top) / rect.height) * 2 - 1));
    };
    if (isFinePointer) window.addEventListener('mousemove', onMove, { passive: true });

    const LINES = 3;
    const t0 = performance.now();
    let raf = null;

    const render = (time) => {
      const t = (time - t0) / 1000;

      if (isFinePointer) {
        // damped follow — slow, settling, never a snap (brand's --ease-door in motion terms)
        current.x += (target.x - current.x) * 0.035;
        current.y += (target.y - current.y) * 0.035;
      } else {
        // no pointer to answer to on touch — a slow, fixed, closed orbit: "just looped"
        current.x = Math.sin(t * 0.12) * 0.7;
        current.y = Math.sin(t * 0.08) * 0.5 * Math.cos(t * 0.05);
      }

      ctx.clearRect(0, 0, w, h);

      const baseAmp = h * (0.045 + 0.03 * Math.abs(current.y));
      const freq = (1.0 + current.x * 0.45) / Math.max(w, 1);

      for (let i = 0; i < LINES; i += 1) {
        const spread = i - (LINES - 1) / 2;
        const amp = baseAmp * (1 - Math.abs(spread) * 0.18);
        const phase = t * (0.5 + i * 0.06) + spread * 0.9;
        const yOff = h / 2 + spread * h * 0.06 + current.y * h * 0.05;
        const opacity = 0.34 - Math.abs(spread) * 0.08;

        ctx.beginPath();
        for (let x = 0; x <= w; x += 5) {
          const y = yOff
            + Math.sin(x * freq * Math.PI * 2 + phase) * amp
            + Math.sin(x * freq * Math.PI * 4.3 + phase * 1.6) * amp * 0.22;
          if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
        }
        ctx.strokeStyle = `rgba(236, 236, 232, ${opacity})`;
        ctx.lineWidth = i === Math.floor(LINES / 2) ? 1.25 : 1;
        ctx.stroke();
      }

      raf = requestAnimationFrame(render);
    };

    if (reduced) render(t0);
    else raf = requestAnimationFrame(render);

    return () => {
      ro.disconnect();
      if (isFinePointer) window.removeEventListener('mousemove', onMove);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [isFinePointer, reduced]);

  return (
    <div ref={wrapRef} style={{ position: 'absolute', inset: 0, overflow: 'hidden', background: 'var(--gray-990)', ...style }}>
      <canvas ref={canvasRef} aria-hidden="true" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }} />

      {/* dark scrim — keeps type set above legible over the trace */}
      <div aria-hidden="true" style={{
        position: 'absolute', inset: 0, pointerEvents: 'none',
        background: 'linear-gradient(180deg, var(--overlay-scrim), rgba(0,0,0,0.38) 45%, var(--overlay-scrim) 100%)',
      }} />

      {/* near-invisible film grain, the brand's one allowed texture */}
      <div aria-hidden="true" style={{
        position: 'absolute', inset: 0, pointerEvents: 'none',
        backgroundImage: `url("${MUSIC_GRAIN_URL}")`,
        opacity: 0.15, mixBlendMode: 'overlay',
      }} />
    </div>
  );
}

window.MusicBackground = MusicBackground;
