// Animation hooks & utilities

// Scroll reveal — flips a `revealed` flag when the element enters viewport.
// Tracking this in React state (rather than imperatively adding a class) is
// important: the consumer rebuilds className every render from props, so an
// imperatively-added `.in` would be wiped whenever any prop changes (e.g. the
// `delay` prop shifts when an item's list position changes between audiences).
window.useReveal = function useReveal(options = {}) {
  const ref = React.useRef(null);
  const [revealed, setRevealed] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el || revealed) return;
    if (typeof IntersectionObserver === 'undefined') {
      setRevealed(true);
      return;
    }
    const obs = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          setRevealed(true);
          obs.unobserve(el);
        }
      });
    }, { threshold: options.threshold ?? 0.15, rootMargin: options.rootMargin ?? '0px 0px -40px 0px' });
    obs.observe(el);
    return () => obs.disconnect();
  }, [revealed]);
  return [ref, revealed];
};

// <Reveal> wrapper: <Reveal delay={2}>…</Reveal>
window.Reveal = function Reveal({ children, delay = 0, as = 'div', className = '', style = {}, ...rest }) {
  const [ref, revealed] = useReveal();
  const cls = `reveal${revealed ? ' in' : ''}${delay ? ' reveal-delay-' + delay : ''} ${className}`.trim();
  return React.createElement(as, { ref, className: cls, style, ...rest }, children);
};

// Animated counter — counts from 0 to target when visible
// Accepts optional prefix/suffix. target can be numeric (300) or string ("300+").
window.AnimatedCounter = function AnimatedCounter({ target, prefix = '', suffix = '', duration = 1600, style = {}, className = '' }) {
  const ref = React.useRef(null);
  const [val, setVal] = React.useState(0);
  const [done, setDone] = React.useState(false);

  // Parse trailing suffix from target if it's a string like "300+" or "20–80%"
  let num = typeof target === 'number' ? target : parseFloat(String(target));
  let parsedSuffix = suffix;
  if (typeof target === 'string') {
    const m = String(target).match(/^[\-\+\$]?([\d.,]+)(.*)$/);
    if (m) {
      num = parseFloat(m[1].replace(/,/g, ''));
      parsedSuffix = (m[2] || '') + suffix;
    }
  }
  if (!isFinite(num)) num = 0;

  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (typeof IntersectionObserver === 'undefined') { setVal(num); setDone(true); return; }
    const obs = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting && !done) {
          const start = performance.now();
          const tick = (now) => {
            const p = Math.min(1, (now - start) / duration);
            const eased = 1 - Math.pow(1 - p, 3);
            setVal(num * eased);
            if (p < 1) requestAnimationFrame(tick);
            else setDone(true);
          };
          requestAnimationFrame(tick);
          obs.unobserve(el);
        }
      });
    }, { threshold: 0.4 });
    obs.observe(el);
    return () => obs.disconnect();
  }, [num]);

  // Format: if original target had a decimal, keep it; else integer
  const originalHadDecimal = String(target).includes('.');
  const display = originalHadDecimal
    ? val.toFixed(1)
    : Math.round(val).toLocaleString();

  return React.createElement('span', { ref, className, style },
    prefix, display, parsedSuffix
  );
};

// Tilt on hover — subtle 3d tilt following mouse
window.useTilt = function useTilt(max = 6) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el || window.matchMedia('(hover: none)').matches) return;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const px = (e.clientX - r.left) / r.width;
      const py = (e.clientY - r.top) / r.height;
      const rx = (py - 0.5) * -max * 2;
      const ry = (px - 0.5) * max * 2;
      el.style.transform = `perspective(900px) rotateX(${rx}deg) rotateY(${ry}deg) translateZ(0)`;
    };
    const onLeave = () => { el.style.transform = ''; };
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
    return () => {
      el.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
    };
  }, [max]);
  return ref;
};
