"use client";
import { useEffect, useState, type ElementType, type ReactNode } from "react";

import { useReveal } from "@/lib/useReveal";
import { cn } from "@/lib/utils";

/** Escalones de delay. Tailwind necesita las clases completas en el código para no purgarlas. */
const DELAY = ["", "delay-[90ms]", "delay-[180ms]", "delay-[270ms]", "delay-[360ms]", "delay-[450ms]"];

export function Reveal({
  children,
  className,
  delay = 0,
  as: Tag = "div",
}: {
  children: ReactNode;
  className?: string;
  /** 0–5: escalona la entrada de elementos hermanos. */
  delay?: number;
  as?: ElementType;
}) {
  const { ref, shown } = useReveal<HTMLDivElement>();

  return (
    <Tag
      ref={ref}
      className={cn(
        "transition-[opacity,transform] duration-700 ease-out motion-reduce:transition-none",
        DELAY[Math.min(delay, 5)],
        shown
          ? "translate-y-0 opacity-100"
          : "translate-y-6 opacity-0 motion-reduce:translate-y-0 motion-reduce:opacity-100",
        className,
      )}
    >
      {children}
    </Tag>
  );
}

/** Número que sube desde 0 la primera vez que entra en viewport. Para la banda de datos. */
export function CountUp({ to, suffix = "", className }: { to: number; suffix?: string; className?: string }) {
  const { ref, shown } = useReveal<HTMLSpanElement>();
  const [value, setValue] = useState(0);

  useEffect(() => {
    if (!shown) return;
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
      setValue(to);
      return;
    }
    let raf = 0;
    const t0 = performance.now();
    const tick = (t: number) => {
      const p = Math.min((t - t0) / 1100, 1);
      setValue(Math.round(to * (1 - Math.pow(1 - p, 3)))); // ease-out cubic
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [to, shown]);

  return (
    <span ref={ref} className={className}>
      {value}
      {suffix}
    </span>
  );
}
