// scroll-anim.jsx — shared scroll-animation primitives for both options.
// Uses IntersectionObserver + rAF. No external deps. All hooks/components
// are exported to `window` so each option's file can use them.

const { useEffect, useRef, useState, useCallback, useLayoutEffect } = React;

// ---------- useInView: fires once when element scrolls into view ----------
function useInView(opts = {}) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const obs = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setInView(true);
          if (opts.once !== false) obs.disconnect();
        } else if (opts.once === false) {
          setInView(false);
        }
      },
      { threshold: opts.threshold ?? 0.15, rootMargin: opts.rootMargin ?? '0px 0px -10% 0px' }
    );
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, []);
  return [ref, inView];
}

// ---------- useScrollProgress: 0→1 progress of element through viewport ----
// Returns progress where:
//   0 when the element's top hits the bottom of viewport
//   1 when the element's bottom hits the top of viewport
function useScrollProgress(opts = {}) {
  const ref = useRef(null);
  const [progress, setProgress] = useState(0);
  useEffect(() => {
    if (!ref.current) return;
    let raf = 0;
    const update = () => {
      raf = 0;
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const total = r.height + vh;
      const passed = vh - r.top;
      const p = Math.max(0, Math.min(1, passed / total));
      setProgress(p);
    };
    const onScroll = () => {
      if (!raf) raf = requestAnimationFrame(update);
    };
    update();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);
  return [ref, progress];
}

// ---------- useStickyProgress: progress during sticky section ----------
// Returns progress 0→1 of how far through the *sticky scroll range* we are.
// Wrap your container with height = (1 + N) × 100vh; the sticky child
// uses the progress to drive scenes (0 → 1/N → 2/N → … → 1).
function useStickyProgress() {
  const ref = useRef(null);
  const [progress, setProgress] = useState(0);
  useEffect(() => {
    if (!ref.current) return;
    let raf = 0;
    const update = () => {
      raf = 0;
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const scrollable = r.height - vh;
      if (scrollable <= 0) {
        setProgress(0);
        return;
      }
      const p = Math.max(0, Math.min(1, -r.top / scrollable));
      setProgress(p);
    };
    const onScroll = () => {
      if (!raf) raf = requestAnimationFrame(update);
    };
    update();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);
  return [ref, progress];
}

// ---------- Reveal: fade + slide up when in view ----------
function Reveal({ children, delay = 0, y = 24, duration = 0.9, as = 'div', style = {} }) {
  const [ref, inView] = useInView();
  const Tag = as;
  return (
    <Tag
      ref={ref}
      style={{
        ...style,
        opacity: inView ? 1 : 0,
        transform: inView ? 'translate3d(0,0,0)' : `translate3d(0,${y}px,0)`,
        transition: `opacity ${duration}s cubic-bezier(.2,.7,.2,1) ${delay}s, transform ${duration}s cubic-bezier(.2,.7,.2,1) ${delay}s`,
        willChange: 'opacity, transform',
      }}
    >
      {children}
    </Tag>
  );
}

// ---------- WordReveal: split string into spans, stagger them ----------
function WordReveal({ text, delay = 0, stagger = 0.04, y = 28, style = {}, italicWords = [], italicStyle = {} }) {
  const [ref, inView] = useInView();
  const words = text.split(' ');
  return (
    <span ref={ref} style={{ ...style, display: 'inline-block', overflow: 'visible' }}>
      {words.map((w, i) => {
        const isItalic = italicWords.includes(w.toLowerCase().replace(/[.,!?]/g, ''));
        return (
          <React.Fragment key={i}>
            <span style={{
              display: 'inline-block',
              overflow: 'hidden',
              paddingBottom: '0.14em',
              marginBottom: '-0.14em',
              paddingRight: '0.12em',
              marginRight: '-0.12em',
              verticalAlign: 'top',
            }}>
              <span style={{
                display: 'inline-block',
                opacity: inView ? 1 : 0,
                transform: inView ? 'translate3d(0,0,0)' : `translate3d(0,${y}px,0)`,
                transition: `opacity 0.9s cubic-bezier(.2,.7,.2,1) ${delay + i * stagger}s, transform 0.9s cubic-bezier(.2,.7,.2,1) ${delay + i * stagger}s`,
                ...(isItalic ? italicStyle : {}),
              }}>
                {w}
              </span>
            </span>
            {i < words.length - 1 && <span>{'\u00A0'}</span>}
          </React.Fragment>
        );
      })}
    </span>
  );
}

// ---------- Counter: animates from 0 to target when in view ----------
function Counter({ value, prefix = '', suffix = '', duration = 1.8, decimals = 0, style = {} }) {
  const [ref, inView] = useInView();
  const [n, setN] = useState(0);
  useEffect(() => {
    if (!inView) return;
    let raf = 0;
    const start = performance.now();
    const tick = (now) => {
      const t = Math.min(1, (now - start) / (duration * 1000));
      const eased = 1 - Math.pow(1 - t, 3);
      setN(value * eased);
      if (t < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [inView, value, duration]);
  return (
    <span ref={ref} style={style}>
      {prefix}
      {decimals ? n.toFixed(decimals) : Math.round(n).toLocaleString('en-IN')}
      {suffix}
    </span>
  );
}

// ---------- Marquee: infinite horizontal scroll ----------
function Marquee({ children, speed = 40, style = {}, direction = 'left' }) {
  return (
    <div style={{ ...style, overflow: 'hidden', display: 'flex', whiteSpace: 'nowrap' }}>
      <div style={{
        display: 'flex', gap: 56, paddingRight: 56,
        animation: `dc-marquee-${direction} ${speed}s linear infinite`,
        flexShrink: 0,
      }}>
        {children}
      </div>
      <div aria-hidden style={{
        display: 'flex', gap: 56, paddingRight: 56,
        animation: `dc-marquee-${direction} ${speed}s linear infinite`,
        flexShrink: 0,
      }}>
        {children}
      </div>
    </div>
  );
}

// Inject marquee keyframes once
if (typeof document !== 'undefined' && !document.getElementById('scroll-anim-css')) {
  const style = document.createElement('style');
  style.id = 'scroll-anim-css';
  style.textContent = `
    @keyframes dc-marquee-left {
      from { transform: translateX(0); }
      to { transform: translateX(-100%); }
    }
    @keyframes dc-marquee-right {
      from { transform: translateX(-100%); }
      to { transform: translateX(0); }
    }
    @keyframes dc-pulse-dot {
      0%, 100% { opacity: 1; transform: scale(1); }
      50% { opacity: 0.5; transform: scale(0.85); }
    }
    @keyframes dc-float {
      0%, 100% { transform: translateY(0); }
      50% { transform: translateY(-8px); }
    }
    @keyframes dc-shimmer {
      0% { background-position: -200% 0; }
      100% { background-position: 200% 0; }
    }
  `;
  document.head.appendChild(style);
}

// ---------- Parallax: simple Y-translate based on scroll progress ----------
function Parallax({ children, strength = 60, style = {} }) {
  const [ref, p] = useScrollProgress();
  const y = (p - 0.5) * strength * -1;
  return (
    <div ref={ref} style={{ ...style, transform: `translate3d(0, ${y}px, 0)`, willChange: 'transform' }}>
      {children}
    </div>
  );
}

Object.assign(window, {
  useInView, useScrollProgress, useStickyProgress,
  Reveal, WordReveal, Counter, Marquee, Parallax,
});
