/* global React */
const { useEffect, useRef, useState } = React;

/* Count up to target when in view */
function useCountUp(target, { duration = 1800, decimals = 0, start = 0 } = {}) {
  const [val, setVal] = useState(start);
  const [ref, inView] = useInView();
  useEffect(() => {
    if (!inView) return;
    const t0 = performance.now();
    let raf;
    const tick = (now) => {
      const p = Math.min(1, (now - t0) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(start + (target - start) * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
      else setVal(target);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [inView, target]);
  return [ref, decimals === 0 ? Math.round(val) : val.toFixed(decimals)];
}

function useInView(threshold = 0.15) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    // If any ancestor has data-eager-inview, report in-view immediately.
    // This lets us pre-run entry animations during idle time while the
    // element is off-screen or hidden behind an overlay.
    if (ref.current.closest && ref.current.closest('[data-eager-inview]')) {
      setInView(true);
      return;
    }
    const io = new IntersectionObserver(
      ([e]) => { if (e.isIntersecting) { setInView(true); io.disconnect(); } },
      { threshold }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  return [ref, inView];
}

/* Reveal: adds `in` class when element enters view */
function Reveal({ children, className = '', stagger = false, delay = 0, as = 'div' }) {
  const [ref, inView] = useInView(0.1);
  const Tag = as;
  const cls = `${stagger ? 'reveal-stagger' : 'reveal'} ${inView ? 'in' : ''} ${className}`;
  return h(Tag, { ref, className: cls, style: { transitionDelay: inView ? `${delay}ms` : '0ms' } }, children);
}

/* format number with commas + decimal */
function fmt(v, decimals = 0) {
  const n = typeof v === 'number' ? v : parseFloat(v);
  if (Number.isNaN(n)) return v;
  return n.toLocaleString('en-US', {
    minimumFractionDigits: decimals,
    maximumFractionDigits: decimals,
  });
}

/* Live-updating number that pulses toward a new value every few seconds */
function useLivePulse(baseTarget, { jitter = 0.04, periodMs = 4200 } = {}) {
  const [target, setTarget] = useState(baseTarget);
  useEffect(() => {
    const tick = () => {
      // Skip update if user is actively scrolling — avoids React re-renders during scroll frames.
      if (document.body.hasAttribute('data-scrolling')) return;
      const delta = baseTarget * jitter * (Math.random() * 2 - 1);
      setTarget(Math.max(0, Math.round(baseTarget + delta)));
    };
    const id = setInterval(tick, periodMs);
    return () => clearInterval(id);
  }, [baseTarget, jitter, periodMs]);
  return target;
}

/* Mouse-tilt hook for 3D hero */
function useTilt(maxDeg = 6) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf = 0;
    let tx = 0, ty = 0;
    const onMove = (e) => {
      const rect = el.getBoundingClientRect();
      const cx = window.innerWidth / 2;
      const cy = window.innerHeight / 2;
      const dx = (e.clientX - cx) / window.innerWidth;
      const dy = (e.clientY - cy) / window.innerHeight;
      tx = Math.max(-1, Math.min(1, -dy * 2)) * maxDeg;
      ty = Math.max(-1, Math.min(1, dx * 2)) * maxDeg;
      if (!raf) {
        raf = requestAnimationFrame(() => {
          el.style.transform = `rotateX(${tx}deg) rotateY(${ty}deg) translateZ(0)`;
          raf = 0;
        });
      }
    };
    window.addEventListener('mousemove', onMove);
    return () => {
      window.removeEventListener('mousemove', onMove);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [maxDeg]);
  return ref;
}

/* Magnetic buttons */
function useMagnetic(strength = 0.3) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const x = e.clientX - r.left - r.width / 2;
      const y = e.clientY - r.top - r.height / 2;
      el.style.transform = `translate(${x * strength}px, ${y * strength}px)`;
    };
    const onLeave = () => { el.style.transform = ''; };
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
    return () => {
      el.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
    };
  }, [strength]);
  return ref;
}

Object.assign(window, {
  useCountUp, useInView, Reveal, fmt, useLivePulse, useTilt, useMagnetic,
});
