const { useState, useEffect, useRef, useMemo, useCallback } = React;

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

/* Reveal-on-scroll: add .in when element enters viewport. Re-scans when `key` changes. */
function useReveal(key) {
  useEffect(() => {
    const els = document.querySelectorAll('.reveal:not(.in)');
    if (!('IntersectionObserver' in window)) {
      els.forEach(e => e.classList.add('in'));
      return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); }
      });
    }, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
    els.forEach(e => io.observe(e));
    // Safety net: force-reveal any elements still hidden after 800ms.
    const fallback = setTimeout(() => {
      document.querySelectorAll('.reveal:not(.in)').forEach(e => e.classList.add('in'));
    }, 800);
    return () => { io.disconnect(); clearTimeout(fallback); };
  }, [key]);
}

/* Counter that animates to `target` once visible. Supports decimals + prefix/suffix. */
function CountUp({ target, duration = 1400, prefix = '', suffix = '', format = (v)=>Math.round(v).toLocaleString() }) {
  const ref = useRef(null);
  const [val, setVal] = useState(0);
  const reduced = useReducedMotion();

  useEffect(() => {
    if (reduced) { setVal(target); return; }
    const el = ref.current; if (!el) return;
    let raf = 0; let started = false;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (!started && e.isIntersecting) {
          started = true;
          const t0 = performance.now();
          const step = (now) => {
            const p = Math.min(1, (now - t0) / duration);
            const eased = 1 - Math.pow(1 - p, 3);
            setVal(target * eased);
            if (p < 1) raf = requestAnimationFrame(step);
          };
          raf = requestAnimationFrame(step);
          io.unobserve(el);
        }
      });
    }, { threshold: 0.3 });
    io.observe(el);
    return () => { io.disconnect(); cancelAnimationFrame(raf); };
  }, [target, duration, reduced]);

  return <span ref={ref}>{prefix}{format(val)}{suffix}</span>;
}

/* Auto-hide nav on scroll down, show on scroll up. */
function useAutoHideNav() {
  useEffect(() => {
    const nav = document.getElementById('site-nav');
    if (!nav) return;
    let lastY = window.scrollY;
    let ticking = false;
    const onScroll = () => {
      if (ticking) return;
      ticking = true;
      requestAnimationFrame(() => {
        const y = window.scrollY;
        if (y > 80 && y > lastY) nav.classList.add('nav-hidden');
        else nav.classList.remove('nav-hidden');
        if (y > 20) nav.classList.add('nav-scrolled'); else nav.classList.remove('nav-scrolled');
        lastY = y;
        ticking = false;
      });
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
}

window.useReducedMotion = useReducedMotion;
window.useReveal = useReveal;
window.CountUp = CountUp;
window.useAutoHideNav = useAutoHideNav;
