// motion.jsx — animation utilities: count-up hook, magnetic button,
// scroll progress bar, animated number, in-view trigger.

// ───── In-view hook (returns ref + boolean) ─────
// Reveals on scroll into view. Uses IntersectionObserver when it fires, but ALSO
// keeps a scroll/resize fallback: some engines throttle IO in backgrounded or
// automated tabs (and occasionally on iOS Safari after a bfcache restore), which
// would leave scroll-reveal content stuck invisible. Scroll events always fire,
// so the geometry check guarantees the content is never left hidden.
function useInView(opts = {}) {
  const ref = React.useRef(null);
  const [inView, setInView] = React.useState(false);
  React.useEffect(() => {
    const node = ref.current;
    if (!node) return;
    const threshold = opts.threshold || 0.15;
    let done = false;
    const cleanup = () => {
      if (io) io.disconnect();
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
    const reveal = () => {
      if (done) return;
      done = true;
      setInView(true);
      cleanup();
    };
    const check = () => {
      const r = node.getBoundingClientRect();
      const vh = window.innerHeight || document.documentElement.clientHeight;
      if (r.bottom <= 0 || r.top >= vh) return; // fully off-screen
      const visible = Math.min(r.bottom, vh) - Math.max(r.top, 0);
      if (visible >= Math.min(r.height, vh) * threshold) reveal();
    };
    const onScroll = () => check();
    const io = ('IntersectionObserver' in window)
      ? new IntersectionObserver(([e]) => { if (e.isIntersecting) reveal(); },
          { threshold, rootMargin: opts.rootMargin || '0px' })
      : null;
    if (io) io.observe(node);
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    check(); // catch elements already in view on mount
    return cleanup;
  }, []);
  return [ref, inView];
}

// ───── Count-up (animated number) ─────
function useCountUp(target, { duration = 1400, start = 0, decimals = 0, trigger = true } = {}) {
  const [v, setV] = React.useState(start);
  const triggered = React.useRef(false);
  React.useEffect(() => {
    if (!trigger || triggered.current) return;
    triggered.current = true;
    let raf;
    const t0 = performance.now();
    const animate = (t) => {
      const p = Math.min(1, (t - t0) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setV(start + (target - start) * eased);
      if (p < 1) raf = requestAnimationFrame(animate);
    };
    raf = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(raf);
  }, [trigger, target]);
  return Number(v.toFixed(decimals));
}

// ───── Smooth number transition (no animation on first paint) ─────
function useSmoothNumber(target, duration = 600) {
  const [v, setV] = React.useState(target);
  const prev = React.useRef(target);
  React.useEffect(() => {
    const from = prev.current;
    const to = target;
    if (from === to) return;
    let raf;
    const t0 = performance.now();
    const animate = (t) => {
      const p = Math.min(1, (t - t0) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setV(from + (to - from) * eased);
      if (p < 1) raf = requestAnimationFrame(animate);
      else prev.current = to;
    };
    raf = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(raf);
  }, [target]);
  return Math.round(v);
}

// ───── Magnetic hover (button moves slightly toward cursor) ─────
function useMagnet(strength = 0.25) {
  const ref = React.useRef(null);
  React.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;
}

// ───── Scroll progress bar (fixed at top) ─────
function ScrollProgress() {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const onScroll = () => {
      const max = document.documentElement.scrollHeight - window.innerHeight;
      const pct = max > 0 ? (window.scrollY / max) * 100 : 0;
      if (ref.current) ref.current.style.width = pct + '%';
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return <div className="scroll-progress"><div ref={ref} className="scroll-progress__bar"></div></div>;
}

// ───── In-view + count-up combo for stats ─────
function CountStat({ value, suffix = '', prefix = '', label, sub, duration = 1400 }) {
  const [ref, inView] = useInView({ threshold: 0.4 });
  const display = useCountUp(value, { duration, trigger: inView });
  return (
    <div className="count-stat" ref={ref}>
      <div className="count-stat-n h-display">{prefix}{display.toLocaleString()}{suffix}</div>
      <div className="count-stat-l">{label}</div>
      {sub && <div className="count-stat-c">{sub}</div>}
    </div>
  );
}

// ───── Marquee row (with icon separators) ─────
function MarqueeRow({ items, speed = 38 }) {
  const dup = [...items, ...items, ...items];
  return (
    <div className="mq">
      <div className="mq-track" style={{ animationDuration: speed + 's' }}>
        {dup.map((t, i) => (
          <span key={i} className="mq-item">
            <IconAsterisk size={11} sw={1.2} className="mq-star" />
            {t}
          </span>
        ))}
      </div>
    </div>
  );
}

// ───── Parallax image (subtle translateY on scroll) ─────
function useParallax(strength = 0.15) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf;
    const apply = () => {
      const r = el.getBoundingClientRect();
      const mid = r.top + r.height / 2;
      const offset = (window.innerHeight / 2 - mid) * strength;
      el.style.setProperty('--parallax', offset + 'px');
    };
    const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(apply); };
    window.addEventListener('scroll', onScroll, { passive: true });
    apply();
    return () => { window.removeEventListener('scroll', onScroll); cancelAnimationFrame(raf); };
  }, [strength]);
  return ref;
}

Object.assign(window, {
  useInView, useCountUp, useSmoothNumber, useMagnet, useParallax,
  ScrollProgress, CountStat, MarqueeRow,
});
