// Shared hooks — make everything globally scoped via window.
const { useState, useEffect, useRef, useLayoutEffect, useMemo, useCallback } = React;

// Reveal-on-scroll
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll('.reveal, .reveal-stagger');
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.15, rootMargin: '0px 0px -60px 0px' });
    els.forEach(el => io.observe(el));
    return () => io.disconnect();
  }, []);
}

// Count-up on intersection
function useCountUp(target, duration = 1800, suffix = '') {
  const [val, setVal] = useState(0);
  const ref = useRef(null);
  const started = useRef(false);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting && !started.current) {
          started.current = true;
          const start = performance.now();
          const tick = (t) => {
            const p = Math.min(1, (t - start) / duration);
            const eased = 1 - Math.pow(1 - p, 3);
            setVal(target * eased);
            if (p < 1) requestAnimationFrame(tick);
          };
          requestAnimationFrame(tick);
        }
      });
    }, { threshold: 0.35 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [target, duration]);
  return [val, ref];
}

// Cursor blob
function CursorBlob() {
  const ref = useRef(null);
  useEffect(() => {
    let rx = window.innerWidth / 2, ry = window.innerHeight / 2;
    let tx = rx, ty = ry;
    let raf;
    const loop = () => {
      rx += (tx - rx) * 0.08;
      ry += (ty - ry) * 0.08;
      if (ref.current) ref.current.style.transform = `translate(${rx - 210}px, ${ry - 210}px)`;
      raf = requestAnimationFrame(loop);
    };
    const onMove = (e) => { tx = e.clientX; ty = e.clientY; };
    window.addEventListener('mousemove', onMove);
    raf = requestAnimationFrame(loop);
    return () => { window.removeEventListener('mousemove', onMove); cancelAnimationFrame(raf); };
  }, []);
  return <div ref={ref} className="cursor-blob" aria-hidden="true" />;
}

// Split-text kinetic — splits by character for stagger
function KineticText({ text, delay = 0, el = 'span', cls = '' }) {
  const chars = [...text];
  const E = el;
  return (
    <E className={cls}>
      {chars.map((c, i) => (
        <span key={i} style={{
          display: 'inline-block',
          opacity: 0,
          transform: 'translateY(30%)',
          animation: `charUp 0.8s cubic-bezier(0.2,0.8,0.2,1) ${delay + i * 0.025}s forwards`
        }}>{c === ' ' ? '\u00A0' : c}</span>
      ))}
    </E>
  );
}

// Scroll progress
function useScrollProgress() {
  const [p, setP] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const h = document.documentElement.scrollHeight - window.innerHeight;
      setP(h > 0 ? window.scrollY / h : 0);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return p;
}

// Nice tokyo clock
function useClock() {
  const [t, setT] = useState(new Date());
  useEffect(() => {
    const id = setInterval(() => setT(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  return t;
}

Object.assign(window, { useState, useEffect, useRef, useLayoutEffect, useMemo, useCallback, useReveal, useCountUp, CursorBlob, KineticText, useScrollProgress, useClock });

// Inject keyframe for charUp (used by KineticText)
(function injectKf() {
  const s = document.createElement('style');
  s.textContent = `@keyframes charUp { to { opacity: 1; transform: translateY(0); } }`;
  document.head.appendChild(s);
})();
