"use client";

import { animate, useInView, useReducedMotion } from "framer-motion";
import { useEffect, useMemo, useRef, useState } from "react";

type CountUpStatProps = {
  value: string;
  label: string;
};

type ParsedValue = {
  prefix: string;
  target: number;
  suffix: string;
};

const smoothEase = [0.22, 1, 0.36, 1] as const;

function parseValue(value: string): ParsedValue | null {
  const match = value.match(/^([^0-9]*)([\d,]+)(.*)$/);

  if (!match) {
    return null;
  }

  const [, prefix, rawNumber, suffix] = match;
  const target = Number.parseInt(rawNumber.replace(/,/g, ""), 10);

  if (Number.isNaN(target)) {
    return null;
  }

  return { prefix, target, suffix };
}

function formatValue(parsed: ParsedValue, current: number) {
  return `${parsed.prefix}${new Intl.NumberFormat("en-US").format(current)}${parsed.suffix}`;
}

export function CountUpStat({ value, label }: CountUpStatProps) {
  const ref = useRef<HTMLDivElement | null>(null);
  const isInView = useInView(ref, { once: true, amount: 0.45 });
  const shouldReduceMotion = useReducedMotion();
  const parsed = useMemo(() => parseValue(value), [value]);
  const [displayValue, setDisplayValue] = useState(() =>
    parsed ? formatValue(parsed, 0) : value,
  );
  const hasAnimated = useRef(false);

  useEffect(() => {
    if (!parsed) {
      setDisplayValue(value);
      return;
    }

    if (!isInView || hasAnimated.current) {
      return;
    }

    hasAnimated.current = true;

    if (shouldReduceMotion) {
      setDisplayValue(formatValue(parsed, parsed.target));
      return;
    }

    const controls = animate(0, parsed.target, {
      duration: 1.4,
      ease: smoothEase,
      onUpdate(latest) {
        setDisplayValue(formatValue(parsed, Math.round(latest)));
      },
    });

    return () => {
      controls.stop();
    };
  }, [isInView, parsed, shouldReduceMotion, value]);

  return (
    <div ref={ref} className="stat-item">
      <span className="stat-value">{displayValue}</span>
      <span className="stat-label">{label}</span>
    </div>
  );
}
