// motion.jsx — scroll/animation primitives for CruiseN'Shine
// Exposes: Reveal, CountUp, Parallax, Marquee, MagneticButton, SplitWords,
//          useScrollY, useReducedMotion.
// Pure CSS transitions + IntersectionObserver; no popmotion needed.

function useReducedMotion() {
  const [reduced, setReduced] = React.useState(false);
  React.useEffect(() => {
    const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
    setReduced(mq.matches);
    const on = () => setReduced(mq.matches);
    mq.addEventListener?.('change', on);
    return () => mq.removeEventListener?.('change', on);
  }, []);
  return reduced;
}

function useInView(opts = {}) {
  const ref = React.useRef(null);
  const [inView, setInView] = React.useState(false);
  React.useEffect(() => {
    if (!ref.current) return;
    if (typeof IntersectionObserver === 'undefined') { setInView(true); return; }
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setInView(true); if (opts.once !== false) io.disconnect(); }
      else if (opts.once === false) setInView(false);
    }, { rootMargin: opts.rootMargin || '0px 0px -10% 0px', threshold: opts.threshold ?? 0.1 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  return [ref, inView];
}

function useScrollY() {
  const [y, setY] = React.useState(0);
  React.useEffect(() => {
    let raf = 0;
    const on = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(() => setY(window.scrollY)); };
    window.addEventListener('scroll', on, { passive: true });
    return () => { window.removeEventListener('scroll', on); cancelAnimationFrame(raf); };
  }, []);
  return y;
}

// Reveal: fade + slide-up when scrolled into view
function Reveal({ children, delay = 0, y = 24, duration = 700, as: As = 'div', style, once = true }) {
  const reduced = useReducedMotion();
  const [ref, inView] = useInView({ once });
  const shown = reduced || inView;
  return (
    <As ref={ref} style={{
      ...style,
      transform: shown ? 'translate3d(0,0,0)' : `translate3d(0, ${y}px, 0)`,
      opacity: shown ? 1 : 0,
      transition: reduced ? 'none' : `transform ${duration}ms cubic-bezier(.2,.7,.2,1) ${delay}ms, opacity ${duration}ms cubic-bezier(.2,.7,.2,1) ${delay}ms`,
      willChange: 'transform, opacity',
    }}>{children}</As>
  );
}

// CountUp: animates from 0 → target when in view. Supports decimals and suffixes.
function CountUp({ to, suffix = '', decimals = 0, duration = 1600, style }) {
  const reduced = useReducedMotion();
  const [ref, inView] = useInView();
  const [val, setVal] = React.useState(0);
  React.useEffect(() => {
    if (!inView) return;
    if (reduced) { setVal(to); return; }
    let start = 0; let rafId = 0;
    const step = (ts) => {
      if (!start) start = ts;
      const t = Math.min(1, (ts - start) / duration);
      const eased = 1 - Math.pow(1 - t, 3);
      setVal(eased * to);
      if (t < 1) rafId = requestAnimationFrame(step);
    };
    rafId = requestAnimationFrame(step);
    return () => cancelAnimationFrame(rafId);
  }, [inView, to, duration, reduced]);
  return <span ref={ref} style={{ fontVariantNumeric: 'tabular-nums', ...style }}>{val.toFixed(decimals)}{suffix}</span>;
}

// Parallax: translates Y as element scrolls past the viewport.
function Parallax({ children, speed = 0.2, style }) {
  const reduced = useReducedMotion();
  const ref = React.useRef(null);
  const [offset, setOffset] = React.useState(0);
  React.useEffect(() => {
    if (reduced) return;
    let raf = 0;
    const on = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        if (!ref.current) return;
        const r = ref.current.getBoundingClientRect();
        const center = r.top + r.height / 2 - window.innerHeight / 2;
        setOffset(-center * speed);
      });
    };
    on();
    window.addEventListener('scroll', on, { passive: true });
    window.addEventListener('resize', on);
    return () => { window.removeEventListener('scroll', on); window.removeEventListener('resize', on); cancelAnimationFrame(raf); };
  }, [speed, reduced]);
  return <div ref={ref} style={{ ...style, transform: `translate3d(0, ${offset}px, 0)`, willChange: 'transform' }}>{children}</div>;
}

// Marquee: infinite horizontal scroll. items is React children (we duplicate them).
function Marquee({ children, speed = 40, direction = 'left', gap = 64, style, fontSize = 18 }) {
  const reduced = useReducedMotion();
  const dur = `${speed}s`;
  return (
    <div style={{ overflow: 'hidden', width: '100%', position: 'relative', ...style }}>
      <style>{`
        @keyframes cns-marq-l { from { transform: translate3d(0,0,0); } to { transform: translate3d(-50%, 0, 0); } }
        @keyframes cns-marq-r { from { transform: translate3d(-50%,0,0); } to { transform: translate3d(0, 0, 0); } }
      `}</style>
      <div style={{
        display: 'flex', gap, whiteSpace: 'nowrap', width: 'max-content', fontSize,
        animation: reduced ? 'none' : `${direction === 'right' ? 'cns-marq-r' : 'cns-marq-l'} ${dur} linear infinite`,
      }}>
        {children}{children}
      </div>
    </div>
  );
}

// MagneticButton: subtle cursor-follow on hover. Falls back to no-op on touch.
function MagneticButton({ children, strength = 0.25, style, ...rest }) {
  const ref = React.useRef(null);
  const reduced = useReducedMotion();
  const onMove = (e) => {
    if (reduced) return;
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const x = ((e.clientX - r.left) / r.width - 0.5) * r.width * strength;
    const y = ((e.clientY - r.top) / r.height - 0.5) * r.height * strength;
    el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
  };
  const onLeave = () => { if (ref.current) ref.current.style.transform = 'translate3d(0,0,0)'; };
  return (
    <button ref={ref} onMouseMove={onMove} onMouseLeave={onLeave}
      style={{ transition: 'transform .35s cubic-bezier(.2,.7,.2,1), background .2s, color .2s', willChange: 'transform', ...style }}
      {...rest}>
      {children}
    </button>
  );
}

// SplitWords: staggered fade-up of each word on view.
function SplitWords({ children, delay = 0, stagger = 60, y = 18, style }) {
  const reduced = useReducedMotion();
  const [ref, inView] = useInView();
  const text = typeof children === 'string' ? children : '';
  const parts = text.split(' ');
  return (
    <span ref={ref} style={{ display: 'inline-block', ...style }}>
      {parts.map((w, i) => (
        <span key={i} style={{
          display: 'inline-block',
          marginRight: '0.28em',
          transform: reduced || inView ? 'translate3d(0,0,0)' : `translate3d(0, ${y}px, 0)`,
          opacity: reduced || inView ? 1 : 0,
          transition: reduced ? 'none' : `transform 800ms cubic-bezier(.2,.7,.2,1) ${delay + i * stagger}ms, opacity 800ms cubic-bezier(.2,.7,.2,1) ${delay + i * stagger}ms`,
          willChange: 'transform, opacity',
        }}>{w}</span>
      ))}
    </span>
  );
}

// Tilt: 3D tilt on hover for cards.
function Tilt({ children, max = 6, style }) {
  const ref = React.useRef(null);
  const reduced = useReducedMotion();
  const onMove = (e) => {
    if (reduced) return;
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const rx = ((e.clientY - r.top) / r.height - 0.5) * -2 * max;
    const ry = ((e.clientX - r.left) / r.width - 0.5) * 2 * max;
    el.style.transform = `perspective(900px) rotateX(${rx}deg) rotateY(${ry}deg)`;
  };
  const onLeave = () => { if (ref.current) ref.current.style.transform = 'perspective(900px) rotateX(0) rotateY(0)'; };
  return (
    <div ref={ref} onMouseMove={onMove} onMouseLeave={onLeave}
      style={{ transition: 'transform .35s cubic-bezier(.2,.7,.2,1)', transformStyle: 'preserve-3d', willChange: 'transform', ...style }}>
      {children}
    </div>
  );
}

// Expose globally so other Babel scripts can use them
Object.assign(window, { Reveal, CountUp, Parallax, Marquee, MagneticButton, SplitWords, Tilt, useScrollY, useReducedMotion, useInView });
