"use client";

import { motion, useReducedMotion, type Variants } from "motion/react";
import { cn } from "@/lib/utils";

type AnimatedTextProps = {
  text: string;
  as?: "h1" | "h2" | "h3" | "h4" | "p" | "span";
  className?: string;
  split?: "words" | "chars";
  stagger?: number;
  delay?: number;
  duration?: number;
  once?: boolean;
};

const container = (stagger: number, delay: number, reduce: boolean): Variants => ({
  hidden: {},
  show: {
    transition: {
      staggerChildren: reduce ? 0 : stagger,
      delayChildren: delay,
    },
  },
});

const itemMotion = (duration: number): Variants => ({
  hidden: { y: "115%", opacity: 0, filter: "blur(6px)" },
  show: {
    y: "0%",
    opacity: 1,
    filter: "blur(0px)",
    transition: { duration, ease: [0.22, 1, 0.36, 1] },
  },
});

const itemReduce: Variants = {
  hidden: { opacity: 0 },
  show: { opacity: 1, transition: { duration: 0.25 } },
};

export function AnimatedText({
  text,
  as: Tag = "h2",
  className,
  split = "words",
  stagger = 0.06,
  delay = 0,
  duration = 0.85,
  once = true,
}: AnimatedTextProps) {
  const reduce = useReducedMotion() ?? false;
  const tokens = split === "words" ? text.split(" ") : Array.from(text);
  const MotionTag = motion[Tag] as typeof motion.div;
  const variants = reduce ? itemReduce : itemMotion(duration);

  return (
    <MotionTag
      className={cn("inline-flex flex-wrap leading-[1.05]", className)}
      variants={container(stagger, delay, reduce)}
      initial="hidden"
      whileInView="show"
      viewport={{ once, amount: 0.3 }}
      aria-label={text}
    >
      {tokens.map((token, index) => (
        <span
          key={`${token}-${index}`}
          className="relative inline-block overflow-hidden pb-[0.12em] pr-[0.25em]"
          aria-hidden
        >
          <motion.span className="inline-block will-change-transform" variants={variants}>
            {token === " " ? " " : token}
          </motion.span>
        </span>
      ))}
    </MotionTag>
  );
}
