'use client';

import { useEffect, useRef, useState } from 'react';
import { motion, MotionValue, useInView, useSpring, useTransform } from 'framer-motion';

function Digit({
  place,
  value,
  digitHeight,
  duration,
}: {
  place: number;
  value: number;
  digitHeight: number;
  duration: number;
}) {
  const valueRoundedToPlace = Math.floor(value / place);
  const animatedValue = useSpring(valueRoundedToPlace, {
    duration: duration * 1000, // Convert to milliseconds
  });

  useEffect(() => {
    animatedValue.set(valueRoundedToPlace);
  }, [animatedValue, valueRoundedToPlace]);

  return (
    <div style={{ height: digitHeight }} className="relative w-[1ch] tabular-nums overflow-hidden">
      {Array.from({ length: 10 }, (_, i) => (
        <Number key={i} mv={animatedValue} number={i} digitHeight={digitHeight} />
      ))}
    </div>
  );
}

function Number({ mv, number, digitHeight }: { mv: MotionValue<number>; number: number; digitHeight: number }) {
  const y = useTransform(mv, (latest: number) => {
    const placeValue = latest % 10;
    const offset = (10 + number - placeValue) % 10;

    let memo = offset * digitHeight;

    if (offset > 5) {
      memo -= 10 * digitHeight;
    }

    return memo;
  });

  return (
    <motion.span style={{ y }} className="absolute inset-0 flex items-center justify-center">
      {number}
    </motion.span>
  );
}

interface SlidingNumberProps {
  from: number;
  to: number;
  duration?: number;
  delay?: number;
  startOnView?: boolean;
  once?: boolean;
  className?: string;
  onComplete?: () => void;
  digitHeight?: number;
}

export function SlidingNumber({
  from,
  to,
  duration = 2,
  delay = 0,
  startOnView = true,
  once = false,
  className = '',
  onComplete,
  digitHeight = 40,
}: SlidingNumberProps) {
  const ref = useRef(null);
  const isInView = useInView(ref, { once: false }); // Always use false, manage once manually
  const [currentValue, setCurrentValue] = useState(from);
  const [hasAnimated, setHasAnimated] = useState(false);
  const [animationKey, setAnimationKey] = useState(0);

  // Reset animation state on component mount (route changes)
  useEffect(() => {
    setCurrentValue(from);
    setHasAnimated(false);
    setAnimationKey((prev) => prev + 1);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // Empty dependency array - runs on every mount

  // Reset animation state when from/to values change
  useEffect(() => {
    setCurrentValue(from);
    setHasAnimated(false);
    setAnimationKey((prev) => prev + 1);
  }, [from, to]);

  // Manage animation triggering manually
  useEffect(() => {
    if (!startOnView || !isInView) return;

    // If once=true and already animated on this mount, don't animate again
    if (once && hasAnimated) return;

    // Trigger animation
    const timer = setTimeout(() => {
      setAnimationKey((prev) => prev + 1);
    }, 50);

    return () => clearTimeout(timer);
  }, [isInView, startOnView, once, hasAnimated]);

  const shouldStart = !startOnView || (isInView && (!once || !hasAnimated));

  useEffect(() => {
    if (!shouldStart) return;
    setHasAnimated(true);

    const timer = setTimeout(() => {
      const startTime = Date.now();
      const startValue = currentValue;
      const difference = to - startValue;

      const animate = () => {
        const elapsed = Date.now() - startTime;
        const progress = Math.min(elapsed / (duration * 1000), 1);
        const easeOutCubic = 1 - Math.pow(1 - progress, 3);
        const newValue = startValue + difference * easeOutCubic;

        setCurrentValue(newValue);

        if (progress < 1) {
          requestAnimationFrame(animate);
        } else {
          setCurrentValue(to);
          onComplete?.();
        }
      };

      requestAnimationFrame(animate);
    }, delay * 1000);

    return () => clearTimeout(timer);
  }, [shouldStart, currentValue, to, duration, delay, onComplete]);

  // Round the current value to avoid showing decimals during animation
  const roundedValue = Math.round(currentValue);
  const absValue = Math.abs(roundedValue);

  // Determine the maximum number of digits needed
  const maxDigits = Math.max(Math.abs(from).toString().length, Math.abs(to).toString().length);

  // Create array of place values (1, 10, 100, 1000, etc.)
  const places = Array.from({ length: maxDigits }, (_, i) => Math.pow(10, maxDigits - i - 1));

  return (
    <div ref={ref} className={`flex items-center ${className}`}>
      {roundedValue < 0 && '-'}
      {places.map((place) => (
        <Digit
          key={`${place}-${animationKey}`}
          place={place}
          value={absValue}
          digitHeight={digitHeight}
          duration={duration}
        />
      ))}
    </div>
  );
}
