// motion.jsx — shared scroll/hover/live-pulse motion primitives.
// Respects prefers-reduced-motion.

const PREFERS_REDUCED = (typeof window !== 'undefined' && window.matchMedia)
  ? window.matchMedia('(prefers-reduced-motion: reduce)').matches : false;

// Reveal: fades + lifts a child into view when it enters the viewport.
// Uses IntersectionObserver, fires once.
function Reveal({ children, delay = 0, y = 24, duration = 700, as = 'div', style, ...rest }) {
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(PREFERS_REDUCED);

  React.useEffect(() => {
    if (PREFERS_REDUCED) return;
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          setShown(true);
          io.unobserve(el);
        }
      });
    }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, []);

  const Tag = as;
  return (
    <Tag
      ref={ref}
      style={{
        ...style,
        opacity: shown ? 1 : 0,
        transform: shown ? 'translate3d(0,0,0)' : `translate3d(0, ${y}px, 0)`,
        transition: `opacity ${duration}ms cubic-bezier(.2,.7,.2,1) ${delay}ms, transform ${duration}ms cubic-bezier(.2,.7,.2,1) ${delay}ms`,
        willChange: shown ? 'auto' : 'opacity, transform',
      }}
      {...rest}
    >
      {children}
    </Tag>
  );
}

// RevealStagger: wraps children in <Reveal> with incrementing delay.
function RevealStagger({ children, step = 80, baseDelay = 0, ...common }) {
  const arr = React.Children.toArray(children);
  return arr.map((c, i) => (
    <Reveal key={i} delay={baseDelay + i * step} {...common}>{c}</Reveal>
  ));
}

// HoverCard: wraps content with a subtle lift + glow on hover. Use for interactive cards.
function HoverCard({ children, lift = 4, glow = 'rgba(56,189,248,0.18)', style, ...rest }) {
  const [hover, setHover] = React.useState(false);
  return (
    <div
      onMouseEnter={() => !PREFERS_REDUCED && setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        ...style,
        transform: hover ? `translate3d(0, -${lift}px, 0)` : 'translate3d(0,0,0)',
        boxShadow: hover ? `0 24px 60px -20px ${glow}, 0 0 0 1px rgba(255,255,255,0.04)` : (style && style.boxShadow) || 'none',
        transition: 'transform 280ms cubic-bezier(.2,.7,.2,1), box-shadow 280ms cubic-bezier(.2,.7,.2,1)',
      }}
      {...rest}
    >
      {children}
    </div>
  );
}

// LivePulse: a small dot that softly pulses, used to indicate "agent is working here".
function LivePulse({ color = 'var(--accent)', size = 8, style }) {
  return (
    <span style={{
      position: 'relative',
      display: 'inline-block',
      width: size, height: size,
      ...style,
    }}>
      <span style={{
        position: 'absolute', inset: 0, borderRadius: 999,
        background: color,
        boxShadow: `0 0 8px ${color}`,
      }} />
      {!PREFERS_REDUCED && (
        <span style={{
          position: 'absolute', inset: 0, borderRadius: 999,
          background: color,
          animation: 'om-pulse 2s ease-out infinite',
          opacity: 0.6,
        }} />
      )}
    </span>
  );
}

// LiveBar: a thin shimmer bar showing "the agent is processing"
function LiveBar({ color = 'var(--accent)', height = 2, style }) {
  if (PREFERS_REDUCED) {
    return <div style={{ height, background: color, opacity: 0.2, ...style }} />;
  }
  return (
    <div style={{ position: 'relative', height, overflow: 'hidden', borderRadius: 999, background: 'rgba(255,255,255,0.05)', ...style }}>
      <div style={{
        position: 'absolute', top: 0, bottom: 0, width: '40%',
        background: `linear-gradient(90deg, transparent, ${color}, transparent)`,
        animation: 'om-shimmer 2.4s linear infinite',
      }} />
    </div>
  );
}

// LiveCounter: ticks up a number every `interval` ms, by `step`. Caps at `max`.
function LiveCounter({ start = 0, step = 1, interval = 1800, max = Infinity, format = (n) => n }) {
  const [n, setN] = React.useState(start);
  React.useEffect(() => {
    if (PREFERS_REDUCED) return;
    const id = setInterval(() => setN((v) => Math.min(max, v + step)), interval);
    return () => clearInterval(id);
  }, [step, interval, max]);
  return <>{format(n)}</>;
}

// FlipZoomCard: maps element's viewport position to a 3D flip-in + zoom-out scroll animation.
function FlipZoomCard({ children, style, ...rest }) {
  const ref = React.useRef(null);
  const [t, setT] = React.useState(PREFERS_REDUCED ? 0.5 : 0);

  React.useEffect(() => {
    if (PREFERS_REDUCED) return;
    let raf = 0;
    const update = () => {
      const el = ref.current;
      if (!el) { raf = 0; return; }
      const rect = el.getBoundingClientRect();
      const vh = window.innerHeight || 800;
      const total = vh + rect.height;
      const traveled = vh - rect.top;
      const p = Math.max(0, Math.min(1, traveled / total));
      setT(p);
      raf = 0;
    };
    let alive = true;
    const tick = () => {
      if (!alive) return;
      update();
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => {
      alive = false;
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);

  const enterEnd = 0.45;
  const exitStart = 0.62;
  let rotY = 0, scale = 1, opacity = 1, ty = 0;
  if (t < enterEnd) {
    const k = t / enterEnd;
    const e = 1 - Math.pow(1 - k, 3);
    rotY = (1 - e) * 65;
    scale = 0.86 + e * 0.14;
    opacity = e;
    ty = (1 - e) * 50;
  } else if (t > exitStart) {
    const k = Math.min(1, (t - exitStart) / (1 - exitStart));
    const e = k * k;
    scale = 1 + e * 0.18;
    opacity = 1 - e * 0.5;
    ty = -e * 36;
  }

  return (
    <div
      ref={ref}
      style={{
        ...style,
        transform: `perspective(1400px) translate3d(0, ${ty}px, 0) rotateY(${rotY}deg) scale(${scale})`,
        opacity,
        transformStyle: 'preserve-3d',
        willChange: 'transform, opacity',
        transformOrigin: 'center center',
      }}
      {...rest}
    >
      {children}
    </div>
  );
}

// Inject keyframes once.
if (typeof document !== 'undefined' && !document.getElementById('om-motion-kf')) {
  const s = document.createElement('style');
  s.id = 'om-motion-kf';
  s.textContent = `
    @keyframes om-pulse { 0% { transform: scale(1); opacity: 0.55; } 80% { transform: scale(2.6); opacity: 0; } 100% { transform: scale(2.6); opacity: 0; } }
    @keyframes om-shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(350%); } }
    @keyframes om-blink { 50% { opacity: 0; } }
    @keyframes om-float { 0%, 100% { transform: translate3d(0,0,0); } 50% { transform: translate3d(0,-3px,0); } }
    @keyframes om-typing { from { width: 0; } to { width: 100%; } }
    @keyframes om-ring { 0% { transform: scale(1); opacity: 0.7; } 80% { transform: scale(1.35); opacity: 0; } 100% { transform: scale(1.35); opacity: 0; } }
    @keyframes om-reply-fly { 0% { transform: translate(0, 0) scale(.9); opacity: 0; } 25% { opacity: 1; } 100% { transform: translate(-60%, -30px) scale(1); opacity: 0; } }
    @keyframes om-callout { 0%, 60% { transform: translateX(0) scale(1); opacity: 1; } 75% { transform: translateX(20px) scale(.96); opacity: 0; } 90% { transform: translateX(-12px) scale(.98); opacity: 0; } 100% { transform: translateX(0) scale(1); opacity: 1; } }
  `;
  document.head.appendChild(s);
}

Object.assign(window, { Reveal, RevealStagger, HoverCard, LivePulse, LiveBar, LiveCounter, FlipZoomCard, PREFERS_REDUCED });
