// Minimal composition clock for embedding the Confirmations piece in the site.
// Provides the same API surface the piece expects (CompositionStage, useComposition,
// Easing, clamp, interpolate) without the authoring/timeline chrome.
const { useState, useEffect, useRef, useMemo, useContext, createContext } = React;

const Easing = {
  linear: t => t,
  easeOutQuad: t => 1 - (1 - t) * (1 - t),
  easeInOutQuad: t => t < .5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,
  easeOutCubic: t => 1 - Math.pow(1 - t, 3),
  easeInOutCubic: t => t < .5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
  easeOutBack: t => { const c1 = 1.70158, c3 = c1 + 1; return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2); },
};
const clamp = (v, lo, hi) => Math.min(Math.max(v, lo), hi);
const interpolate = (t, [i0, i1], [o0, o1], ease) => {
  const p = clamp((t - i0) / (i1 - i0 || 1e-6), 0, 1);
  return o0 + (o1 - o0) * (ease ? ease(p) : p);
};
const animate = (t, from, to, dur, ease) => interpolate(t, [from, from + dur], [0, 1], ease) * 0 + interpolate(t, [from, from + dur], [0, to], ease);
const Shot = ({ children }) => children || null;

const CompositionCtx = createContext({ T: 0, CUES: {}, authoredTotal: 0 });
const useComposition = () => useContext(CompositionCtx);

function CompositionStage({ width = 1200, height = 580, scenes, bg, once, children }) {
  const parsed = useMemo(() => {
    let list = [];
    try { list = typeof scenes === 'string' ? JSON.parse(scenes) : (scenes || []); } catch (e) { list = []; }
    const table = {}; let acc = 0;
    list.forEach(s => { table[s.name] = acc; acc += +s.dur || 0; });
    return { CUES: table, total: acc || 1 };
  }, [scenes]);

  const [T, setT] = useState(0);
  const tRef = useRef(0);
  const [runId, setRunId] = useState(0);
  const hostRef = useRef(null);
  const visible = useRef(false);

  useEffect(() => {
    const el = hostRef.current;
    let io;
    if (el && 'IntersectionObserver' in window) {
      io = new IntersectionObserver(es => es.forEach(e => { visible.current = e.isIntersecting; }), { threshold: 0.15 });
      io.observe(el);
    } else visible.current = true;
    let raf, last = performance.now();
    tRef.current = 0; setT(0);
    const stopAt = parsed.total - 1.2; // hold the resolved end state instead of looping
    const tick = now => {
      const dt = Math.min((now - last) / 1000, 0.1); last = now;
      if (visible.current) {
        if (once) { if (tRef.current < stopAt) { tRef.current = Math.min(tRef.current + dt, stopAt); setT(tRef.current); } }
        else { tRef.current = (tRef.current + dt) % parsed.total; setT(tRef.current); }
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => { cancelAnimationFrame(raf); if (io) io.disconnect(); };
  }, [parsed.total, once, runId]);

  const ctx = useMemo(() => ({ T, CUES: parsed.CUES, authoredTotal: parsed.total }), [T, parsed]);
  return (
    <div ref={hostRef} style={{ position: 'relative', width: '100%', aspectRatio: width + ' / ' + height, background: bg || 'transparent' }}>
      <CompositionCtx.Provider value={ctx}>{children}</CompositionCtx.Provider>
      <button
        type="button"
        onClick={() => setRunId(n => n + 1)}
        style={{ position: 'absolute', right: 'auto', left: 10, bottom: 10, display: 'inline-flex', alignItems: 'center', gap: 6, padding: '5px 10px', background: 'rgba(255,255,255,.9)', border: '1px solid #e0e0e0', borderRadius: 3, color: '#00705c', fontSize: '10.5px', fontWeight: 500, lineHeight: 1, fontFamily: 'inherit', letterSpacing: '.1em', textTransform: 'uppercase', cursor: 'pointer' }}
      >
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 11a8 8 0 10-2.6 6.9" /><path d="M20 4v7h-7" /></svg>
        Replay
      </button>
    </div>
  );
}

Object.assign(window, { CompositionStage, useComposition, CompositionCtx, Easing, clamp, interpolate, animate, Shot });
