"use client";

import { useEffect, useRef, useState } from "react";

interface CountUpProps {
  /** Target number to count up to */
  to: number;
  /** String appended after the number (e.g. "%", " MB", "+") */
  suffix?: string;
  /** String prepended before the number */
  prefix?: string;
  /** Animation duration in ms */
  duration?: number;
  /** Decimal places to show */
  decimals?: number;
}

export default function CountUp({
  to,
  suffix = "",
  prefix = "",
  duration = 1800,
  decimals = 0,
}: CountUpProps) {
  const ref = useRef<HTMLSpanElement>(null);
  const [current, setCurrent] = useState(0);
  const [started, setStarted] = useState(false);

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

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting && !started) {
          setStarted(true);
          observer.disconnect();
        }
      },
      { threshold: 0.5 },
    );

    observer.observe(el);
    return () => observer.disconnect();
  }, [started]);

  useEffect(() => {
    if (!started) return;

    // Skip animation if user prefers reduced motion
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
      setCurrent(to);
      return;
    }

    const startTime = performance.now();
    let raf: number;

    function tick(now: number) {
      const elapsed = now - startTime;
      const progress = Math.min(elapsed / duration, 1);
      // Ease-out quart — fast start, smooth finish
      const eased = 1 - Math.pow(1 - progress, 4);
      setCurrent(eased * to);
      if (progress < 1) raf = requestAnimationFrame(tick);
    }

    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [started, to, duration]);

  const display =
    decimals > 0 ? current.toFixed(decimals) : Math.floor(current).toString();

  return (
    <span ref={ref}>
      {prefix}
      {display}
      {suffix}
    </span>
  );
}
