// ============ Motion helpers — Puhastid ============
// Thin layer on top of Framer Motion. Every primitive is reduced-motion-aware:
// when prefers-reduced-motion is set, transforms collapse to a no-op and only
// opacity (or nothing) is animated.

const FM = window.Motion || {};
const {
  motion,
  AnimatePresence,
  LayoutGroup,
  useReducedMotion,
  animate
} = FM;

// Signature easing — Material "decelerate" / out-expo-ish. Used everywhere.
const EASE_OUT = [0.22, 1, 0.36, 1];

// ---- Reveal: a single element fades + translates up when in view ----
function Reveal({
  children,
  y = 16,
  delay = 0,
  duration = 0.55,
  once = true,
  amount = 0.15,
  className,
  style,
  as = "div"
}) {
  const reduced = useReducedMotion();
  const Tag = motion[as] || motion.div;
  if (reduced) {
    const Plain = as;
    return <Plain className={className} style={style}>{children}</Plain>;
  }
  return (
    <Tag
      className={className}
      style={style}
      initial={{ opacity: 0, y }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once, amount }}
      transition={{ duration, ease: EASE_OUT, delay }}
    >
      {children}
    </Tag>
  );
}

// ---- Stagger: parent that staggers its motion-child reveals ----
function StaggerReveal({
  children,
  stagger = 0.08,
  delay = 0,
  once = true,
  amount = 0.15,
  className,
  style,
  as = "div"
}) {
  const reduced = useReducedMotion();
  const Tag = motion[as] || motion.div;
  if (reduced) {
    const Plain = as;
    return <Plain className={className} style={style}>{children}</Plain>;
  }
  return (
    <Tag
      className={className}
      style={style}
      initial="hidden"
      whileInView="visible"
      viewport={{ once, amount }}
      variants={{
        hidden: {},
        visible: { transition: { staggerChildren: stagger, delayChildren: delay } }
      }}
    >
      {children}
    </Tag>
  );
}

function StaggerItem({ children, y = 16, duration = 0.5, className, style, as = "div" }) {
  const reduced = useReducedMotion();
  const Tag = motion[as] || motion.div;
  if (reduced) {
    const Plain = as;
    return <Plain className={className} style={style}>{children}</Plain>;
  }
  return (
    <Tag
      className={className}
      style={style}
      variants={{
        hidden: { opacity: 0, y },
        visible: { opacity: 1, y: 0, transition: { duration, ease: EASE_OUT } }
      }}
    >
      {children}
    </Tag>
  );
}

// ---- SplitWords: render text as word-spans, each fading + rising in sequence ----
function SplitWords({ text, baseDelay = 0, perWord = 0.05, duration = 0.55, y = 12 }) {
  const reduced = useReducedMotion();
  if (text == null) return null;
  if (reduced) return text;
  // Split keeping whitespace so spacing is preserved
  const parts = String(text).split(/(\s+)/);
  let wordIdx = 0;
  return parts.map((p, i) => {
    if (/^\s+$/.test(p)) return <React.Fragment key={i}>{p}</React.Fragment>;
    const d = baseDelay + wordIdx * perWord;
    wordIdx += 1;
    return (
      <motion.span
        key={i}
        style={{ display: "inline-block", willChange: "transform, opacity" }}
        initial={{ opacity: 0, y }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration, delay: d, ease: EASE_OUT }}
      >
        {p}
      </motion.span>
    );
  });
}

// ---- AnimatedNumber: tweens between numeric values; format on render ----
function AnimatedNumber({ value, format = (v) => Math.round(v).toString(), duration = 0.4 }) {
  const reduced = useReducedMotion();
  const [display, setDisplay] = React.useState(value);
  const prev = React.useRef(value);

  React.useEffect(() => {
    if (reduced) {
      setDisplay(value);
      prev.current = value;
      return;
    }
    if (prev.current === value) return;
    const from = prev.current;
    const controls = animate(from, value, {
      duration,
      ease: EASE_OUT,
      onUpdate: (v) => setDisplay(v)
    });
    prev.current = value;
    return () => controls.stop();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [value, reduced]);

  return format(display);
}

// ---- FadeSwap: cross-fade between content as `swapKey` changes ----
function FadeSwap({ swapKey, children, duration = 0.15, className, style }) {
  const reduced = useReducedMotion();
  if (reduced) return <span className={className} style={style}>{children}</span>;
  return (
    <AnimatePresence mode="wait" initial={false}>
      <motion.span
        key={swapKey}
        className={className}
        style={style}
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        transition={{ duration, ease: "easeOut" }}
      >
        {children}
      </motion.span>
    </AnimatePresence>
  );
}

Object.assign(window, {
  PMotion: {
    motion, AnimatePresence, LayoutGroup, useReducedMotion, animate, EASE_OUT
  },
  Reveal,
  StaggerReveal,
  StaggerItem,
  SplitWords,
  AnimatedNumber,
  FadeSwap
});
