// scroll_stage.jsx — scroll-driven replacement for <Stage>.
// Exposes the SAME TimelineContext shape so all existing Sprite/scene components
// work untouched. Instead of a RAF playback loop, the "time" is derived from
// document scroll position: scrollProgress (0..1) × duration.
//
// Page layout: an outer "scroll track" element sets a tall height (e.g. 700vh).
// The canvas is position:sticky at top:0 and stays pinned while the user scrolls.
// A progress rail in the right margin shows where we are.

function ScrollStage({
  width = 1280,
  height = 720,
  duration = 10,
  background = '#f2f0ec',
  scrollMultiplier = 7, // how many viewport heights of scroll track
  children,
}) {
  const [time, setTime] = React.useState(0);
  const [scale, setScale] = React.useState(1);
  const [progress, setProgress] = React.useState(0);

  const trackRef = React.useRef(null);
  const canvasWrapRef = React.useRef(null);

  // Scale the canvas to fit the viewport
  React.useEffect(() => {
    const measure = () => {
      const w = window.innerWidth;
      const h = window.innerHeight;
      const s = Math.min(w / width, h / height);
      setScale(Math.max(0.1, s));
    };
    measure();
    window.addEventListener('resize', measure);
    return () => window.removeEventListener('resize', measure);
  }, [width, height]);

  // Scroll → progress → time
  React.useEffect(() => {
    let raf = null;
    const update = () => {
      raf = null;
      const el = trackRef.current;
      if (!el) return;
      const rect = el.getBoundingClientRect();
      const viewportH = window.innerHeight;
      // The sticky canvas occupies the first viewport of the track. The scroll
      // range we animate across is (trackHeight - viewportH), during which the
      // top of the track moves from 0 down to -(trackHeight - viewportH).
      const scrollable = el.offsetHeight - viewportH;
      const scrolled = -rect.top;
      const p = Math.max(0, Math.min(1, scrolled / Math.max(1, scrollable)));
      setProgress(p);
      setTime(p * duration);
    };
    const onScroll = () => {
      if (raf == null) raf = requestAnimationFrame(update);
    };
    update();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [duration]);

  const ctxValue = React.useMemo(
    () => ({ time, duration, playing: false, setTime: () => {}, setPlaying: () => {} }),
    [time, duration]
  );

  return (
    <div
      ref={trackRef}
      style={{
        position: 'relative',
        width: '100%',
        height: `${scrollMultiplier * 100}vh`,
        background,
      }}
    >
      {/* Sticky viewport-sized frame */}
      <div style={{
        position: 'sticky', top: 0, left: 0,
        width: '100%', height: '100vh',
        overflow: 'hidden',
        background,
      }}>
        {/* Scaled canvas */}
        <div
          ref={canvasWrapRef}
          style={{
            position: 'absolute',
            left: '50%', top: '50%',
            transform: `translate(-50%, -50%) scale(${scale})`,
            transformOrigin: 'center',
            width, height,
            background,
            overflow: 'hidden',
          }}
        >
          <TimelineContext.Provider value={ctxValue}>
            {children}
          </TimelineContext.Provider>
        </div>

        {/* Scroll progress rail — right edge */}
        <ScrollRail progress={progress} duration={duration} time={time} />

        {/* Scroll hint — only visible at the very top */}
        <ScrollHint progress={progress} />
      </div>
    </div>
  );
}

function ScrollRail({ progress, duration, time }) {
  const pct = progress * 100;

  const hitRef = React.useRef(null);
  const dragging = React.useRef(false);

  const seekToY = React.useCallback((clientY) => {
    const el = hitRef.current;
    if (!el) return;
    const rect = el.getBoundingClientRect();
    const frac = Math.max(0, Math.min(1, (clientY - rect.top) / rect.height));
    const scrollable = document.documentElement.scrollHeight - window.innerHeight;
    window.scrollTo({ top: frac * scrollable });
  }, []);

  React.useEffect(() => {
    const onMove = (e) => { if (dragging.current) seekToY(e.clientY); };
    const onUp   = () => { dragging.current = false; };
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup',   onUp);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup',   onUp);
    };
  }, [seekToY]);

  return (
    <>
      {/* Visual rail — appearance unchanged */}
      <div style={{
        position: 'absolute',
        right: 24, top: '50%',
        transform: 'translateY(-50%)',
        height: 'min(60vh, 520px)',
        width: 3,
        background: 'rgba(10,10,10,0.08)',
        borderRadius: 2,
        pointerEvents: 'none',
      }}>
        <div style={{
          position: 'absolute', left: 0, top: 0,
          width: '100%', height: `${pct}%`,
          background: '#0a0a0a', borderRadius: 2,
        }}/>
        <div style={{
          position: 'absolute',
          left: -5, top: `calc(${pct}% - 6px)`,
          width: 13, height: 13, borderRadius: '50%', background: '#0a0a0a',
        }}/>
        <div style={{
          position: 'absolute',
          right: 16, top: `calc(${pct}% - 8px)`,
          fontFamily: "'JetBrains Mono', ui-monospace, monospace",
          fontSize: 10, letterSpacing: '0.14em',
          color: '#0a0a0a', whiteSpace: 'nowrap', fontVariantNumeric: 'tabular-nums',
          pointerEvents: 'none',
        }}>
          {Math.floor(pct)}%
        </div>
      </div>

      {/* Transparent wider hit zone — same height, overlaps the rail */}
      <div
        ref={hitRef}
        onMouseDown={e => { dragging.current = true; seekToY(e.clientY); e.preventDefault(); }}
        style={{
          position: 'absolute',
          right: 8, top: '50%',
          transform: 'translateY(-50%)',
          height: 'min(60vh, 520px)',
          width: 36,
          cursor: 'pointer',
        }}
      />
    </>
  );
}

function ScrollHint({ progress }) {
  // Fade out quickly once user starts scrolling
  const opacity = Math.max(0, 1 - progress * 20);
  if (opacity <= 0.01) return null;
  return (
    <div style={{
      position: 'absolute',
      left: '50%', bottom: 40,
      transform: 'translateX(-50%)',
      opacity,
      fontFamily: "'JetBrains Mono', ui-monospace, monospace",
      fontSize: 11, letterSpacing: '0.28em',
      color: '#0a0a0a',
      textTransform: 'uppercase',
      display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10,
      pointerEvents: 'none',
    }}>
      <div>Scroll to explore</div>
      <div style={{
        width: 1, height: 32,
        background: '#0a0a0a',
        animation: 'scrollHintDrop 1.6s ease-in-out infinite',
        transformOrigin: 'top',
      }}/>
    </div>
  );
}

Object.assign(window, { ScrollStage });
