'use client';

import {useEffect, useRef} from 'react';
import {
  motion,
  useMotionValue,
  useReducedMotion,
  useSpring,
  useTransform,
} from 'framer-motion';

/**
 * Count animation for KPI values.
 *
 * Two deliberate constraints:
 *
 * 1. NO count-up on first paint. Springs leave their origin slowly, so
 *    animating 0 → 32,700 renders a literal "0" for the first couple of
 *    hundred milliseconds — four wrong numbers on a dashboard merchants load
 *    dozens of times a day. The initial value is therefore painted directly.
 *    The animation exists to show that a number *moved*, which is only
 *    meaningful when it had a previous value to move from.
 *
 * 2. Reduced motion skips the spring entirely rather than shortening it.
 *    <MotionConfig reducedMotion="user"> governs motion.* props, but a spring
 *    driving text content is outside its reach, so it is handled here.
 */
export function AnimatedNumber({
  value,
  format,
}: {
  value: number;
  format: (v: number) => string;
}) {
  const reduced = useReducedMotion();

  // Seeded with the real value, so the first frame is already correct.
  const raw = useMotionValue(value);
  const spring = useSpring(raw, {stiffness: 120, damping: 24, mass: 0.5});
  const text = useTransform(spring, (v) => format(v));

  const isFirst = useRef(true);

  useEffect(() => {
    if (isFirst.current) {
      isFirst.current = false;
      return;
    }
    if (reduced) {
      // jump() moves the spring's internal state too, so no frame is scheduled.
      raw.jump(value);
      spring.jump(value);
      return;
    }
    raw.set(value);
  }, [value, reduced, raw, spring]);

  if (reduced) {
    return <>{format(value)}</>;
  }
  return <motion.span>{text}</motion.span>;
}
