// scroll-reveal.jsx
// Utilities for scroll-driven animations on the landing page.
//
// useInView(ref, { threshold, once }) -> boolean
//   True when the element's intersection ratio crosses threshold.
//
// useScrollProgress(ref) -> number 0..1
//   Progress of the element through the viewport (0 when entering, 1 when leaving).
//
// <Reveal as delay stagger style children />  — simple fade-up on first intersection.

const useInView = (ref, { threshold = 0.2, once = true } = {}) => {
  const [inView, setInView] = React.useState(false);
  React.useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    const obs = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        setInView(true);
        if (once) obs.disconnect();
      } else if (!once) {
        setInView(false);
      }
    }, { threshold, rootMargin: '0px 0px -10% 0px' });
    obs.observe(el);
    return () => obs.disconnect();
  }, [ref, threshold, once]);
  return inView;
};

// Element progress 0..1: 0 when element top hits viewport bottom; 1 when element bottom hits viewport top
const useScrollProgress = (ref) => {
  const [p, setP] = React.useState(0);
  React.useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    let raf = null;
    const compute = () => {
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const total = r.height + vh;
      const passed = vh - r.top;
      setP(Math.max(0, Math.min(1, passed / total)));
      raf = null;
    };
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(compute);
    };
    compute();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [ref]);
  return p;
};

// Simple "enter" wrapper — fade-up once in view.
const Reveal = ({
  as: Tag = 'div',
  delay = 0,
  y = 24,
  duration = 800,
  threshold = 0.2,
  className = '',
  style = {},
  children,
  ...rest
}) => {
  const ref = React.useRef(null);
  const inView = useInView(ref, { threshold, once: true });
  return (
    <Tag
      ref={ref}
      className={className}
      style={{
        opacity: inView ? 1 : 0,
        transform: inView ? 'translateY(0)' : `translateY(${y}px)`,
        transition: `opacity ${duration}ms cubic-bezier(.2,.7,.2,1) ${delay}ms, transform ${duration}ms cubic-bezier(.2,.7,.2,1) ${delay}ms`,
        ...style,
      }}
      {...rest}
    >
      {children}
    </Tag>
  );
};

// Run a callback repeatedly with requestAnimationFrame while `active`.
const useRaf = (callback, active = true) => {
  const cbRef = React.useRef(callback);
  cbRef.current = callback;
  React.useEffect(() => {
    if (!active) return;
    let raf;
    let start = performance.now();
    const loop = (t) => {
      cbRef.current((t - start) / 1000);
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [active]);
};

Object.assign(window, { useInView, useScrollProgress, Reveal, useRaf });
