"use client";

import { motion, useInView, useReducedMotion } from "motion/react";
import { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";

type IndexRollProps = {
  /** Target value. The roll counts up 00 → value, one step at a time. */
  value: number;
  /** Zero-pad to this many digits. Default: digits in `value`, min 2. */
  pad?: number;
  /** ms between each increment. Default 330 (matches the deck). */
  step?: number;
  /** ms before the roll starts once in view. Default 420. */
  startDelay?: number;
  className?: string;
};

/**
 * IndexRoll — the giant section index of the event-deck level-divider.
 *
 * Counts 00 → value one unit at a time; only the digit that changes "rides up"
 * (translateY 0.85em → 0, opacity 0 → 1, 280 ms ease-out-expo), like an elevator.
 * Distinct from Counter (value tick) and LetterCounter (letter spin).
 * Pair with `font-display` at a large size. See sd EVENT-DECK.md → level divider.
 */
export function IndexRoll({ value, pad, step = 330, startDelay = 420, className }: IndexRollProps) {
  const ref = useRef<HTMLSpanElement>(null);
  const inView = useInView(ref, { once: true, amount: 0.5 });
  const reduce = useReducedMotion();
  const len = Math.max(pad ?? String(Math.abs(Math.trunc(value))).length, 2);
  const [cur, setCur] = useState(0);

  useEffect(() => {
    if (!inView) return;
    if (reduce) {
      setCur(value);
      return;
    }
    setCur(0);
    let n = 0;
    let timer: ReturnType<typeof setTimeout>;
    const start = setTimeout(function tick() {
      n += 1;
      setCur(n);
      if (n < value) timer = setTimeout(tick, step);
    }, startDelay);
    return () => {
      clearTimeout(start);
      clearTimeout(timer);
    };
  }, [inView, reduce, value, step, startDelay]);

  const chars = String(cur).padStart(len, "0").split("");

  return (
    <span
      ref={ref}
      className={cn(className)}
      style={{ display: "inline-flex" }}
      aria-label={String(value)}
    >
      {chars.map((c, i) => (
        // Stable key per position; the inner span re-mounts (and re-animates) only
        // when its own digit changes — so untouched digits stay put.
        <span key={i} style={{ display: "inline-block", fontVariantNumeric: "tabular-nums" }}>
          <motion.span
            key={c}
            style={{ display: "inline-block" }}
            initial={reduce ? false : { y: "0.85em", opacity: 0 }}
            animate={{ y: 0, opacity: 1 }}
            transition={{ duration: 0.28, ease: [0.16, 1, 0.3, 1] }}
          >
            {c}
          </motion.span>
        </span>
      ))}
    </span>
  );
}
