// DemoKit.jsx — shared helpers for the homepage feature demos.
// Timeline-driven, auto-looping mini-UIs. Everything is keyed off a single
// looping clock (useDemoLoop) so demos are deterministic and seekable
// (set window.__DEMO_FREEZE = <ms> to freeze every demo at a timestamp).

(function () {
  const { useState, useEffect, useMemo, useRef } = React;

  // ── clock ──────────────────────────────────────────────────────────
  function useDemoLoop(duration, stillAt) {
    const reduced = useMemo(
      () => window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches,
      []
    );
    const [t, setT] = useState(reduced ? (stillAt != null ? stillAt : duration - 50) : 0);
    useEffect(() => {
      if (reduced) return;
      let raf;
      const start = performance.now();
      let last = -1000;
      const update = (now) => {
        const f = window.__DEMO_FREEZE;
        const tt = f != null ? Math.min(f, duration - 1) : (now - start) % duration;
        if (Math.abs(tt - last) > 28 || tt < last) { last = tt; setT(tt); }
      };
      const tick = (now) => { update(now); raf = requestAnimationFrame(tick); };
      raf = requestAnimationFrame(tick);
      // fallback: keeps the clock moving when rAF is throttled (hidden/embedded views)
      const iv = setInterval(() => update(performance.now()), 200);
      return () => { cancelAnimationFrame(raf); clearInterval(iv); };
    }, [duration, reduced]);
    return t;
  }

  // ── timeline math ──────────────────────────────────────────────────
  function dkProg(t, a, b) { return Math.max(0, Math.min(1, (t - a) / (b - a))); }
  function dkEase(p) { return p < 0.5 ? 2 * p * p : 1 - Math.pow(-2 * p + 2, 2) / 2; }
  function dkTyped(text, t, a, b) {
    const p = dkProg(t, a, b);
    return text.slice(0, Math.round(p * text.length));
  }
  // fade the stage out at the end of the loop so the restart feels soft
  // (no fade-in: t=0 must render fully visible even if the clock is paused)
  function dkFade(t, d) {
    const f = 320;
    if (t > d - f) return Math.max(0, (d - t) / f);
    return 1;
  }
  // cursor state from waypoints [{at,x,y,hidden}] + click times [ms]
  function dkCursor(t, waypoints, clicks = []) {
    let pos = waypoints[0];
    for (const w of waypoints) if (t >= w.at) pos = w;
    const down = clicks.some((c) => t >= c && t < c + 180);
    const fired = clicks.filter((c) => t >= c).length;
    return { x: pos.x, y: pos.y, hidden: !!pos.hidden, down, fired };
  }
  // € 1.234,56 (PT format per README)
  function dkEUR(v) {
    const [int, dec] = v.toFixed(2).split('.');
    return '€ ' + int.replace(/\B(?=(\d{3})+(?!\d))/g, '.') + ',' + dec;
  }

  // ── atoms ──────────────────────────────────────────────────────────
  function DemoCaret({ show = true, dark }) {
    if (!show) return null;
    return (
      <span className="demo-caret" style={{ background: dark ? '#FAFAFA' : '#18181B' }}></span>
    );
  }

  function DemoCursor({ s }) {
    return (
      <div
        className="absolute z-30 pointer-events-none"
        style={{
          left: s.x, top: s.y,
          opacity: s.hidden ? 0 : 1,
          transition: 'left .7s cubic-bezier(.3,.7,.2,1), top .7s cubic-bezier(.3,.7,.2,1), opacity .35s',
        }}
      >
        {s.fired > 0 && <span key={s.fired} className="demo-ripple"></span>}
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none"
          style={{
            transform: s.down ? 'scale(.8)' : 'scale(1)',
            transformOrigin: '20% 10%',
            transition: 'transform .12s',
            filter: 'drop-shadow(0 2px 5px rgba(0,0,0,.3))',
          }}>
          <path d="M5 2 L5 17.5 L9.2 13.6 L11.7 19.3 L14.7 18 L12.2 12.6 L17.5 12.2 Z"
            fill="#FFFFFF" stroke="#18181B" strokeWidth="1.5" strokeLinejoin="round" />
        </svg>
      </div>
    );
  }

  function DemoToast({ show, text, icon = 'check-circle-2', dark }) {
    return (
      <div
        className={
          'absolute left-1/2 bottom-4 z-40 flex items-center gap-2 h-9 px-3.5 rounded-lg border text-[12px] font-medium whitespace-nowrap ' +
          (dark
            ? 'bg-zinc-50 border-zinc-200 text-zinc-900 shadow-lg'
            : 'bg-zinc-900 border-zinc-800 text-zinc-50 shadow-lg')
        }
        style={{
          transform: 'translateX(-50%) translateY(' + (show ? 0 : 16) + 'px)',
          opacity: show ? 1 : 0,
          transition: 'transform .5s cubic-bezier(.2,.8,.2,1), opacity .5s',
        }}
      >
        <L name={icon} className={'w-4 h-4 ' + (dark ? 'text-emerald-600' : 'text-emerald-400')} />
        {text}
      </div>
    );
  }

  function MiniWindow({ title, icon, right, dark, width = 480, children }) {
    return (
      <div
        className={'relative rounded-xl border overflow-hidden ' + (dark ? 'border-zinc-800 bg-zinc-950' : 'border-zinc-200 bg-white')}
        style={{ width, boxShadow: '0 16px 40px -16px rgba(24,24,27,.28), 0 2px 6px rgba(24,24,27,.06)' }}
      >
        <div className={'h-9 px-3.5 flex items-center gap-2 border-b ' + (dark ? 'border-zinc-800 bg-zinc-900' : 'border-zinc-100 bg-zinc-50')}>
          <div className="flex items-center gap-1.5 mr-1.5">
            <span className={'h-2.5 w-2.5 rounded-full ' + (dark ? 'bg-zinc-700' : 'bg-zinc-200')}></span>
            <span className={'h-2.5 w-2.5 rounded-full ' + (dark ? 'bg-zinc-700' : 'bg-zinc-200')}></span>
            <span className={'h-2.5 w-2.5 rounded-full ' + (dark ? 'bg-zinc-700' : 'bg-zinc-200')}></span>
          </div>
          <L name={icon} className={'w-3.5 h-3.5 ' + (dark ? 'text-zinc-500' : 'text-zinc-400')} />
          <span className={'text-[12px] font-medium ' + (dark ? 'text-zinc-300' : 'text-zinc-600')}>{title}</span>
          <div className="ml-auto flex items-center gap-2">{right}</div>
        </div>
        {children}
      </div>
    );
  }

  // Self-scaling 560×400 stage: fills its container's width, keeps ratio.
  function DemoStage({ width = 560, height = 400, dark, t, dur, children }) {
    const ref = useRef(null);
    const [scale, setScale] = useState(1);
    useEffect(() => {
      const el = ref.current;
      if (!el) return;
      const upd = () => setScale(el.clientWidth / width);
      upd();
      const ro = new ResizeObserver(upd);
      ro.observe(el);
      return () => ro.disconnect();
    }, [width]);
    const fade = t != null && dur ? dkFade(t, dur) : 1;
    return (
      <div ref={ref} className="relative w-full overflow-hidden" style={{ height: height * scale }}>
        <div
          className="absolute top-0 left-0"
          style={{
            width, height,
            transform: 'scale(' + scale + ')',
            transformOrigin: 'top left',
            backgroundColor: dark ? '#18181B' : '#FAFAFA',
            backgroundImage: 'radial-gradient(circle, ' + (dark ? '#27272A' : '#E4E4E7') + ' 1px, transparent 1px)',
            backgroundSize: '18px 18px',
          }}
        >
          <div className="absolute inset-0 flex items-center justify-center"
               style={{ opacity: fade, transition: 'opacity 120ms linear' }}>
            {children}
          </div>
        </div>
      </div>
    );
  }

  // Deterministic fake-QR placeholder (squares only).
  function FakeQR({ size = 52 }) {
    const cells = useMemo(() => {
      const a = [];
      let s = 5;
      for (let i = 0; i < 100; i++) { s = (s * 37 + 11) % 53; a.push(s % 2 === 0); }
      return a;
    }, []);
    return (
      <div className="relative bg-white" style={{ width: size, height: size, display: 'grid', gridTemplateColumns: 'repeat(10, 1fr)' }}>
        {cells.map((on, i) => (
          <span key={i} style={{ background: on ? '#18181B' : 'transparent' }}></span>
        ))}
        <span className="absolute" style={{ left: 0, top: 0, width: 14, height: 14, border: '3px solid #18181B', background: '#FFFFFF', boxSizing: 'border-box' }}></span>
        <span className="absolute" style={{ right: 0, top: 0, width: 14, height: 14, border: '3px solid #18181B', background: '#FFFFFF', boxSizing: 'border-box' }}></span>
        <span className="absolute" style={{ left: 0, bottom: 0, width: 14, height: 14, border: '3px solid #18181B', background: '#FFFFFF', boxSizing: 'border-box' }}></span>
      </div>
    );
  }

  Object.assign(window, {
    useDemoLoop, dkProg, dkEase, dkTyped, dkFade, dkCursor, dkEUR,
    DemoCaret, DemoCursor, DemoToast, MiniWindow, DemoStage, FakeQR,
  });
})();
