// hp-anim.jsx — shared animation hooks.
// Exposes window.useReveal (fade/slide on scroll into view) and
// window.useCountUp (animated number count with prefix/suffix support).
// Respects prefers-reduced-motion.

(function setupHpAnim() {
  const { useState, useEffect, useRef } = React;

  const prefersReduced = () =>
    window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;

  // ---------------------------------------------------------
  // useReveal — IntersectionObserver-based scroll reveal.
  // Returns [ref, isRevealed]. Apply ref to the element and toggle
  // the "is-revealed" class via isRevealed.
  // ---------------------------------------------------------
  function useReveal(opts) {
    const o = opts || {};
    const ref = useRef(null);
    const [revealed, setRevealed] = useState(false);

    useEffect(() => {
      const el = ref.current;
      if (!el) return;
      if (prefersReduced()) { setRevealed(true); return; }
      if (typeof IntersectionObserver === "undefined") { setRevealed(true); return; }

      const io = new IntersectionObserver(
        (entries) => {
          entries.forEach((e) => {
            if (e.isIntersecting) {
              setRevealed(true);
              io.disconnect();
            }
          });
        },
        {
          threshold: o.threshold != null ? o.threshold : 0.15,
          rootMargin: o.rootMargin || "0px 0px -10% 0px",
        }
      );
      io.observe(el);
      return () => io.disconnect();
    }, []);

    return [ref, revealed];
  }

  // ---------------------------------------------------------
  // useCountUp — animates the numeric portion of a value like
  // "$2M", "25", "2" from 0 → target with ease-out-cubic.
  // Keeps any prefix (e.g. "$") and trailing letters (e.g. "M") static.
  // Returns [ref, displayText].
  // ---------------------------------------------------------
  function useCountUp(targetValue, duration) {
    const d = duration || 1600;
    const ref = useRef(null);
    const [text, setText] = useState(String(targetValue));

    useEffect(() => {
      const el = ref.current;
      if (!el) return;

      const raw = String(targetValue);
      const m = raw.match(/^(\D*?)(\d+(?:\.\d+)?)(.*)$/);
      if (!m) { setText(raw); return; }
      const prefix = m[1] || "";
      const numStr = m[2];
      const suffix = m[3] || "";
      const target = parseFloat(numStr);
      const decimals = (numStr.split(".")[1] || "").length;

      if (prefersReduced()) { setText(raw); return; }

      // Start at "prefix0suffix"
      const zero = (0).toFixed(decimals);
      setText(prefix + zero + suffix);

      if (typeof IntersectionObserver === "undefined") { setText(raw); return; }

      const io = new IntersectionObserver(
        (entries) => {
          entries.forEach((e) => {
            if (!e.isIntersecting) return;
            let start = null;
            const step = (ts) => {
              if (start == null) start = ts;
              const p = Math.min((ts - start) / d, 1);
              const eased = 1 - Math.pow(1 - p, 3);
              const current = (eased * target).toFixed(decimals);
              setText(prefix + current + suffix);
              if (p < 1) requestAnimationFrame(step);
            };
            requestAnimationFrame(step);
            io.disconnect();
          });
        },
        { threshold: 0.35 }
      );
      io.observe(el);
      return () => io.disconnect();
    }, [targetValue, d]);

    return [ref, text];
  }

  // ---------------------------------------------------------
  // Helper: assemble className with optional reveal class + delay var.
  // Returns object: { className, style }.
  // ---------------------------------------------------------
  function revealProps(baseClass, revealed, opts) {
    const o = opts || {};
    const variant = o.variant || "fade-up";
    const cls = [baseClass || "", variant, revealed ? "is-revealed" : ""]
      .filter(Boolean).join(" ");
    const style = {};
    if (o.delay != null) style["--hp-delay"] = o.delay + "ms";
    if (o.style) Object.assign(style, o.style);
    return { className: cls, style };
  }

  window.useReveal = useReveal;
  window.useCountUp = useCountUp;
  window.revealProps = revealProps;
})();
