// effects.jsx — Reveal, Counter, scroll progress, parallax, sticky pin.

const { useEffect, useRef, useState } = React;

function Reveal({ children, as: As = "div", delay = 0, className = "", once = true, threshold = 0.18, ...rest }) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) { setSeen(true); if (once) io.unobserve(el); }
        else if (!once) setSeen(false);
      });
    }, { threshold });
    io.observe(el);
    return () => io.disconnect();
  }, [once, threshold]);
  const cls = `reveal ${seen ? "in" : ""} ${delay ? `reveal-d${delay}` : ""} ${className}`.trim();
  return <As ref={ref} className={cls} {...rest}>{children}</As>;
}

function Counter({ to, prefix = "", suffix = "", duration = 1600, decimals = 0 }) {
  const ref = useRef(null);
  const [val, setVal] = useState(0);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    let raf;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          io.unobserve(el);
          const start = performance.now();
          const tick = (now) => {
            const p = Math.min(1, (now - start) / duration);
            const ease = 1 - Math.pow(1 - p, 3);
            setVal(ease * to);
            if (p < 1) raf = requestAnimationFrame(tick);
          };
          raf = requestAnimationFrame(tick);
        }
      });
    }, { threshold: 0.35 });
    io.observe(el);
    return () => { io.disconnect(); if (raf) cancelAnimationFrame(raf); };
  }, [to, duration]);
  const v = decimals > 0 ? val.toFixed(decimals) : Math.round(val).toLocaleString("pt-BR");
  return <span ref={ref}>{prefix}{v}{suffix}</span>;
}

// Returns [ref, progress] — progress is 0..1 over the element's viewport pass.
// 0 when bottom of element is at top of viewport, 1 when top of element is at bottom.
function useScrollProgress() {
  const ref = useRef(null);
  const [p, setP] = useState(0);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    let raf = null;
    function update() {
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const total = vh + r.height;
      const traveled = vh - r.top;
      const prog = Math.max(0, Math.min(1, traveled / total));
      setP(prog);
      raf = null;
    }
    function 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, p];
}

// useScrollScene — lerped, rAF-driven scroll-progress vars for smoothness.
// Continuously animates --enter/--exit toward their target values so the
// scene transforms feel inertial instead of jumpy on every scroll event.
function useScrollScene(intensity = 1) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    let raf = null;
    let cur = { enter: 0, exit: 0, p: 0 };
    let tgt = { enter: 0, exit: 0, p: 0 };
    function target() {
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const mid = (r.top + r.height / 2 - vh / 2) / (vh / 2 + r.height / 2);
      const p = Math.max(-1.2, Math.min(1.2, mid));
      tgt.enter = Math.max(0, Math.min(1, 1 - p));
      tgt.exit  = Math.max(0, Math.min(1, -p));
      tgt.p     = p;
    }
    function loop() {
      // ease toward target
      cur.enter += (tgt.enter - cur.enter) * 0.12;
      cur.exit  += (tgt.exit  - cur.exit)  * 0.12;
      cur.p     += (tgt.p     - cur.p)     * 0.12;
      el.style.setProperty("--enter", cur.enter.toFixed(3));
      el.style.setProperty("--exit",  cur.exit.toFixed(3));
      el.style.setProperty("--p",     cur.p.toFixed(3));
      el.style.setProperty("--ix",    String(intensity));
      raf = requestAnimationFrame(loop);
    }
    target();
    // snap to target on mount
    cur.enter = tgt.enter; cur.exit = tgt.exit; cur.p = tgt.p;
    function onScroll() { target(); }
    raf = requestAnimationFrame(loop);
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      if (raf) cancelAnimationFrame(raf);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, [intensity]);
  return ref;
}

Object.assign(window, { Reveal, Counter, useScrollProgress, useScrollScene });
