/* global React */
const { useState: useCountState, useEffect: useCountEffect, useRef: useCountRef } = React;

/**
 * <CountUp> — animates 0 → `to` when scrolled into view.
 * Respects prefers-reduced-motion (shows final value immediately).
 * Replays each time it re-enters the viewport.
 */
function CountUp({ to, duration = 1600, format = (v) => Math.round(v).toString() }) {
  const [v, setV] = useCountState(0);
  const ref = useCountRef(null);

  useCountEffect(() => {
    const el = ref.current;
    if (!el) return;

    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduce) { setV(to); return; }

    let raf = 0;
    let active = false;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting && !active) {
            active = true;
            setV(0);
            const start = performance.now();
            const tick = (now) => {
              const t = Math.min(1, (now - start) / duration);
              const eased = 1 - Math.pow(1 - t, 3);
              setV(to * eased);
              if (t < 1) raf = requestAnimationFrame(tick);
            };
            raf = requestAnimationFrame(tick);
          } else if (!e.isIntersecting) {
            active = false;
            cancelAnimationFrame(raf);
          }
        });
      },
      { threshold: 0.4 }
    );
    io.observe(el);
    return () => { io.disconnect(); cancelAnimationFrame(raf); };
  }, [to, duration]);

  return <span ref={ref}>{format(v)}</span>;
}

window.CountUp = CountUp;
