// asiz-motion.jsx — reveal-on-scroll + helpers

// useIsMobile: true when viewport is phone-size. Updates on resize.
function useIsMobile(bp = 640) {
  const [is, setIs] = React.useState(() =>
    typeof window !== 'undefined' ? window.innerWidth <= bp : false
  );
  React.useEffect(() => {
    const m = window.matchMedia(`(max-width: ${bp}px)`);
    const handler = (e) => setIs(e.matches);
    m.addEventListener ? m.addEventListener('change', handler) : m.addListener(handler);
    setIs(m.matches);
    return () => {
      m.removeEventListener ? m.removeEventListener('change', handler) : m.removeListener(handler);
    };
  }, [bp]);
  return is;
}
window.useIsMobile = useIsMobile;

// useInView: adds 'is-in' when element enters viewport
function useInView(options = {}) {
  const ref = React.useRef(null);
  const [inView, setInView] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    // Respect tweak-driven motion disabling
    if (el.closest('[data-motion="0"]')) {
      setInView(true);
      el.classList.add('is-in');
      return;
    }
    // Find the scrollable ancestor to use as the IO root. Needed because the
    // site can be rendered inside a transformed/clipped design-canvas artboard
    // where the default viewport root never reports intersections.
    const findScrollParent = (node) => {
      let p = node.parentElement;
      while (p) {
        const cs = getComputedStyle(p);
        const oy = cs.overflowY;
        if ((oy === 'auto' || oy === 'scroll') && p.scrollHeight > p.clientHeight) {
          return p;
        }
        p = p.parentElement;
      }
      return null;
    };
    const root = findScrollParent(el);

    const reveal = () => {
      setInView(true);
      el.classList.add('is-in');
    };

    // Scroll-listener fallback — reliable inside transformed artboards where
    // IO can be unreliable. Checks against the scroll container's viewport.
    const container = root || document.documentElement;
    const check = () => {
      const cr = container.getBoundingClientRect
        ? container.getBoundingClientRect()
        : { top: 0, bottom: window.innerHeight };
      const er = el.getBoundingClientRect();
      const cTop = root ? cr.top : 0;
      const cBot = root ? cr.bottom : window.innerHeight;
      // Trigger when any part of el is within the container viewport
      const threshold = (options.threshold ?? 0.15);
      const visibleHeight = Math.min(er.bottom, cBot) - Math.max(er.top, cTop);
      const elHeight = er.height || 1;
      if (visibleHeight > 0 && (visibleHeight / elHeight) >= Math.min(threshold, 0.01)) {
        reveal();
        cleanup();
      }
    };

    const scrollTarget = root || window;
    const onScroll = () => check();
    scrollTarget.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll, { passive: true });

    const cleanup = () => {
      scrollTarget.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };

    // Run an initial check after first paint so in-viewport items reveal.
    const raf = requestAnimationFrame(() => {
      check();
      // Second check after a tick in case layout shifted
      setTimeout(check, 60);
    });

    return () => {
      cancelAnimationFrame(raf);
      cleanup();
    };
  }, []);
  return [ref, inView];
}

// Reveal wrapper
function Reveal({ children, delay = 0, kind = 'up', as = 'div', style, className = '', ...rest }) {
  const [ref] = useInView();
  const Tag = as;
  const cls =
    kind === 'blur' ? 'reveal-blur' :
    kind === 'scale' ? 'reveal-scale' :
    'reveal';
  return (
    <Tag
      ref={ref}
      className={`${cls} ${className}`}
      style={{ ...(style || {}), '--reveal-delay': `${delay}s` }}
      {...rest}
    >
      {children}
    </Tag>
  );
}

// Split-word reveal for headlines
function SplitHeadline({ text, className = '', delayStart = 0, step = 0.06, style }) {
  const [ref] = useInView();
  const words = String(text).split(/(\s+)/);
  let wIdx = 0;
  return (
    <span ref={ref} className={`reveal is-in-split ${className}`} style={style}>
      {words.map((w, i) => {
        if (/^\s+$/.test(w)) return <span key={i}>{w}</span>;
        const d = delayStart + wIdx * step;
        wIdx += 1;
        return (
          <span
            key={i}
            className="split-word"
            style={{ '--word-delay': `${d}s` }}
          >
            <span>{w}</span>
          </span>
        );
      })}
    </span>
  );
}

// Animated counter
function Counter({ to, suffix = '', prefix = '', decimals = 0, duration = 1.6 }) {
  const [ref, inView] = useInView({ threshold: 0.4 });
  const [val, setVal] = React.useState(0);
  React.useEffect(() => {
    if (!inView) return;
    const start = performance.now();
    const from = 0;
    let raf;
    const tick = (now) => {
      const p = Math.min(1, (now - start) / (duration * 1000));
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(from + (to - from) * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [inView, to, duration]);
  const formatted = val.toLocaleString('en-US', {
    minimumFractionDigits: decimals,
    maximumFractionDigits: decimals,
  });
  return <span ref={ref}>{prefix}{formatted}{suffix}</span>;
}

Object.assign(window, { useInView, Reveal, SplitHeadline, Counter });
