// motion.jsx — shared motion primitives
const useReveal = (opts = {}) => {
  const { y = 12, delay = 0, threshold = 0.15, once = true } = opts;
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setShown(true); if (once) io.disconnect(); }
      else if (!once) setShown(false);
    }, { threshold });
    io.observe(el);
    return () => io.disconnect();
  }, [threshold, once]);
  const style = {
    transform: shown ? 'translate3d(0,0,0)' : `translate3d(0, ${y}px, 0)`,
    opacity: shown ? 1 : 0,
    transition: `opacity 700ms cubic-bezier(0.2,0,0,1) ${delay}ms, transform 700ms cubic-bezier(0.2,0,0,1) ${delay}ms`,
    willChange: 'transform, opacity'
  };
  return [ref, style, shown];
};

const Reveal = ({ as: Tag = 'div', y, delay, threshold, style, children, ...rest }) => {
  const [ref, revealStyle] = useReveal({ y, delay, threshold });
  return <Tag ref={ref} style={{ ...revealStyle, ...style }} {...rest}>{children}</Tag>;
};

// Counter that animates when its element enters view.
const useCounter = (target, { duration = 1600, decimals = 0 } = {}) => {
  const ref = React.useRef(null);
  const [val, setVal] = React.useState(0);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let started = false;
    const io = new IntersectionObserver(([e]) => {
      if (!started && e.isIntersecting) {
        started = true;
        const start = performance.now();
        const tick = (t) => {
          const k = Math.min(1, (t - start) / duration);
          const eased = 1 - Math.pow(1 - k, 3);
          setVal(target * eased);
          if (k < 1) requestAnimationFrame(tick);
          else setVal(target);
        };
        requestAnimationFrame(tick);
        io.disconnect();
      }
    }, { threshold: 0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [target, duration]);
  const formatted = decimals > 0 ? val.toFixed(decimals) : Math.round(val).toLocaleString();
  return [ref, formatted];
};

// Counter component that animates from start to end.
const Counter = ({ to, prefix = '', suffix = '', decimals = 0, duration = 1600, style, ...rest }) => {
  const [ref, val] = useCounter(to, { duration, decimals });
  return <span ref={ref} style={style} {...rest}>{prefix}{val}{suffix}</span>;
};

// Hook to track scroll progress through a section. Returns 0 when section bottom hits the viewport top, 1 when section top hits viewport bottom.
const useScrollProgress = (ref) => {
  const [p, setP] = React.useState(0);
  React.useEffect(() => {
    const onScroll = () => {
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      // 0 when element top is at vh, 1 when element bottom is at 0
      const total = r.height + vh;
      const passed = vh - r.top;
      const k = Math.max(0, Math.min(1, passed / total));
      setP(k);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, [ref]);
  return p;
};

// Animated underline link
const AnimatedLink = ({ children, color = 'var(--fg)', ...rest }) => (
  <a {...rest} style={{
    position: 'relative',
    color, textDecoration: 'none',
    paddingBottom: 2,
    backgroundImage: `linear-gradient(${color}, ${color})`,
    backgroundSize: '0% 1px',
    backgroundRepeat: 'no-repeat',
    backgroundPosition: '0% 100%',
    transition: 'background-size 240ms cubic-bezier(0.2,0,0,1)',
    ...rest.style
  }}
  onMouseEnter={e => e.currentTarget.style.backgroundSize = '100% 1px'}
  onMouseLeave={e => e.currentTarget.style.backgroundSize = '0% 1px'}
  >{children}</a>
);

Object.assign(window, { useReveal, Reveal, useCounter, Counter, useScrollProgress, AnimatedLink });
