/* Shared hooks & utilities — Grippy POC */

// Smooth scroll via Lenis (started in App.jsx)
window.__lenis = null;

// useScrollProgress: track 0..1 progress of an element's intersection with viewport
const useScrollProgress = (ref, opts = {}) => {
  const [progress, setProgress] = React.useState(0);
  const { start = 'top bottom', end = 'bottom top' } = opts;
  React.useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    const onScroll = () => {
      const rect = el.getBoundingClientRect();
      const vh = window.innerHeight;
      // start=top bottom => el top hits viewport bottom => rect.top = vh
      // end=bottom top => el bottom hits viewport top => rect.bottom = 0
      const startY = rect.top + window.scrollY - vh; // window scroll when start triggers
      const endY = rect.top + window.scrollY + rect.height; // when end triggers (bottom hits top)
      const total = endY - startY;
      const p = (window.scrollY - startY) / total;
      setProgress(Math.max(0, Math.min(1, p)));
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, [ref]);
  return progress;
};

// useReveal: add 'in' class when element enters viewport.
// Hardened: reveals above-the-fold content immediately and falls back to a
// scroll handler, so content never stays hidden if IntersectionObserver is
// throttled (background tabs, print, screenshot capture, reduced-motion).
const useReveal = () => {
  React.useEffect(() => {
    const inView = (el, slack = 0.92) => {
      const r = el.getBoundingClientRect();
      return r.top < window.innerHeight * slack && r.bottom > 0;
    };
    const sweep = () => {
      document.querySelectorAll('[data-reveal]:not(.in), .word-rev:not(.in)').forEach(el => {
        if (inView(el)) el.classList.add('in');
      });
    };
    // 1) reveal whatever is already on screen at mount
    sweep();
    // 2) IntersectionObserver for the staggered below-the-fold reveals
    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 -10% 0px' });
    document.querySelectorAll('[data-reveal]:not(.in), .word-rev:not(.in)').forEach(el => io.observe(el));
    // 3) safety net: scroll + a delayed sweep in case IO never initialises
    window.addEventListener('scroll', sweep, { passive: true });
    const t = setTimeout(sweep, 1400);
    return () => { io.disconnect(); window.removeEventListener('scroll', sweep); clearTimeout(t); };
  }, []);
};

// useMagnetic: pull element toward cursor
const useMagnetic = (strength = 0.35) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    const onMove = (e) => {
      const rect = el.getBoundingClientRect();
      const x = e.clientX - rect.left - rect.width / 2;
      const y = e.clientY - rect.top - rect.height / 2;
      el.style.transform = `translate(${x * strength}px, ${y * strength}px)`;
    };
    const onLeave = () => { el.style.transform = 'translate(0,0)'; };
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
    return () => { el.removeEventListener('mousemove', onMove); el.removeEventListener('mouseleave', onLeave); };
  }, [strength]);
  return ref;
};

// interpolate
const lerp = (a, b, t) => a + (b - a) * t;
const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
const ease = {
  outCubic: t => 1 - Math.pow(1 - t, 3),
  inOutCubic: t => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
  outExpo: t => t === 1 ? 1 : 1 - Math.pow(2, -10 * t),
};

// Wrap words for word-by-word reveal
const splitWords = (str) => str.split(' ').map((w, i) => (
  <span className="w" key={i}><span>{w}{i < str.split(' ').length - 1 ? '\u00a0' : ''}</span></span>
));

Object.assign(window, { useScrollProgress, useReveal, useMagnetic, lerp, clamp, ease, splitWords });
