// === Animation engine (hand-rolled) ===
const { useState, useEffect, useRef, useMemo, useContext, createContext, useCallback, useLayoutEffect } = React;

const clamp = (v, a=0, b=1) => Math.max(a, Math.min(b, v));
const lerp = (a,b,t) => a + (b-a) * t;

const Easing = {
  linear: t => t,
  easeInQuad: t => t*t,
  easeOutQuad: t => 1 - (1-t)*(1-t),
  easeInOutQuad: t => t<0.5 ? 2*t*t : 1 - Math.pow(-2*t+2, 2)/2,
  easeInCubic: t => t*t*t,
  easeOutCubic: t => 1 - Math.pow(1-t, 3),
  easeInOutCubic: t => t<0.5 ? 4*t*t*t : 1 - Math.pow(-2*t+2, 3)/2,
  easeInQuart: t => t*t*t*t,
  easeOutQuart: t => 1 - Math.pow(1-t, 4),
  easeInOutQuart: t => t<0.5 ? 8*t*t*t*t : 1 - Math.pow(-2*t+2, 4)/2,
  easeInExpo: t => t===0 ? 0 : Math.pow(2, 10*t-10),
  easeOutExpo: t => t===1 ? 1 : 1 - Math.pow(2, -10*t),
  easeInOutExpo: t => t===0?0:t===1?1:t<0.5?Math.pow(2,20*t-10)/2:(2-Math.pow(2,-20*t+10))/2,
  easeInSine: t => 1 - Math.cos((t*Math.PI)/2),
  easeOutSine: t => Math.sin((t*Math.PI)/2),
  easeInOutSine: t => -(Math.cos(Math.PI*t)-1)/2,
  easeInBack: t => { const c1=1.70158, c3=c1+1; return c3*t*t*t - c1*t*t; },
  easeOutBack: t => { const c1=1.70158, c3=c1+1; return 1 + c3*Math.pow(t-1,3) + c1*Math.pow(t-1,2); },
  easeInOutBack: t => { const c1=1.70158, c2=c1*1.525; return t<0.5 ? (Math.pow(2*t,2)*((c2+1)*2*t-c2))/2 : (Math.pow(2*t-2,2)*((c2+1)*(t*2-2)+c2)+2)/2; },
  easeOutElastic: t => { const c4=(2*Math.PI)/3; return t===0?0:t===1?1:Math.pow(2,-10*t)*Math.sin((t*10-0.75)*c4)+1; },
};

// Popmotion-style keyframe interpolator
function interpolate(input, output, ease = Easing.linear) {
  return (v) => {
    if (v <= input[0]) return output[0];
    if (v >= input[input.length-1]) return output[output.length-1];
    for (let i=0;i<input.length-1;i++){
      if (v >= input[i] && v <= input[i+1]){
        const t = (v - input[i]) / (input[i+1] - input[i]);
        const eased = ease(t);
        const a = output[i], b = output[i+1];
        if (typeof a === 'number') return lerp(a, b, eased);
        return a; // non-numeric: step
      }
    }
    return output[output.length-1];
  };
}

function animate({ from, to, start, end, ease=Easing.linear, t }){
  if (t <= start) return from;
  if (t >= end) return to;
  const p = (t - start) / (end - start);
  return lerp(from, to, ease(p));
}

// Stage context — provides current time (s) within slide
const StageCtx = createContext({ t: 0, duration: 0, playing: true, setPlaying: ()=>{} });

function Stage({ duration=10, children, playing=true, resetKey=0, onProgress }) {
  const [t, setT] = useState(0);
  const raf = useRef(0);
  const startRef = useRef(0);
  const accumRef = useRef(0);
  const playingRef = useRef(playing);
  const [forcedEnd, setForcedEnd] = useState(false);
  useEffect(()=>{
    const onSeek = () => { accumRef.current = duration; setT(duration); setForcedEnd(true); };
    const onSetTime = (ev) => { const tt = ev.detail && typeof ev.detail.t === 'number' ? ev.detail.t : 0; accumRef.current = tt; setT(tt); setForcedEnd(true); };
    window.addEventListener('__deckSeekEnd', onSeek);
    window.addEventListener('__deckSetTime', onSetTime);
    return () => { window.removeEventListener('__deckSeekEnd', onSeek); window.removeEventListener('__deckSetTime', onSetTime); };
  }, [duration]);
  useEffect(()=>{ setForcedEnd(false); }, [resetKey]);

  useEffect(()=>{ playingRef.current = playing; startRef.current = performance.now(); }, [playing]);

  useEffect(()=>{
    accumRef.current = 0;
    startRef.current = performance.now();
    setT(0);
  }, [resetKey]);

  useEffect(()=>{
    let mounted = true;
    const loop = (now) => {
      if (!mounted) return;
      if (playingRef.current && !forcedEnd) {
        const delta = (now - startRef.current) / 1000;
        startRef.current = now;
        const speed = (typeof window.__deckSpeed === 'number' && window.__deckSpeed > 0) ? window.__deckSpeed : 1;
        accumRef.current = Math.min(duration, accumRef.current + delta * speed);
        setT(accumRef.current);
        if (onProgress) onProgress(accumRef.current / duration);
      } else {
        startRef.current = now;
      }
      raf.current = requestAnimationFrame(loop);
    };
    raf.current = requestAnimationFrame(loop);
    return () => { mounted = false; cancelAnimationFrame(raf.current); };
  }, [duration, onProgress]);

  return (
    <StageCtx.Provider value={{ t, duration, playing }}>
      {children}
    </StageCtx.Provider>
  );
}

function useTime(){ return useContext(StageCtx).t; }
function useDuration(){ return useContext(StageCtx).duration; }

Object.assign(window, { clamp, lerp, Easing, interpolate, animate, Stage, StageCtx, useTime, useDuration });
