"use client";

import { type ReactNode } from "react";
import { motion, useReducedMotion } from "framer-motion";

interface AnimatedTextProps {
  children: string;
  className?: string;
  as?: "h1" | "h2" | "h3" | "h4" | "p" | "span";
  variant?: "blur-reveal" | "slide-up" | "fade" | "word-stagger";
  delay?: number;
  duration?: number;
  once?: boolean;
}

export default function AnimatedText({
  children,
  className,
  as = "p",
  variant = "blur-reveal",
  delay = 0,
  duration = 0.6,
  once = true,
}: AnimatedTextProps) {
  const shouldReduceMotion = useReducedMotion();
  const Tag = as;

  if (shouldReduceMotion) {
    return <Tag className={className}>{children}</Tag>;
  }

  if (variant === "word-stagger") {
    const words = children.split(" ");
    const Component = motion[as];

    return (
      <Component
        className={className}
        initial="hidden"
        whileInView="visible"
        viewport={{ once, margin: "-60px" }}
        variants={{
          hidden: {},
          visible: {
            transition: {
              staggerChildren: 0.04,
              delayChildren: delay,
            },
          },
        }}
      >
        {words.map((word, i) => (
          <motion.span
            key={`${word}-${i}`}
            className="inline-block"
            style={{ marginInlineEnd: "0.25em" }}
            variants={{
              hidden: { opacity: 0, y: 20, filter: "blur(4px)" },
              visible: {
                opacity: 1,
                y: 0,
                filter: "blur(0px)",
                transition: {
                  type: "spring",
                  stiffness: 100,
                  damping: 20,
                },
              },
            }}
          >
            {word}
          </motion.span>
        ))}
      </Component>
    );
  }

  const variantMap = {
    "blur-reveal": {
      hidden: { opacity: 0, y: 20, filter: "blur(10px)" },
      visible: { opacity: 1, y: 0, filter: "blur(0px)" },
    },
    "slide-up": {
      hidden: { opacity: 0, y: 40 },
      visible: { opacity: 1, y: 0 },
    },
    fade: {
      hidden: { opacity: 0 },
      visible: { opacity: 1 },
    },
  };

  const Component = motion[as];

  return (
    <Component
      className={className}
      initial="hidden"
      whileInView="visible"
      viewport={{ once, margin: "-60px" }}
      variants={{
        hidden: variantMap[variant].hidden,
        visible: {
          ...variantMap[variant].visible,
          transition: {
            duration,
            delay,
            ease: [0.25, 0.4, 0.25, 1],
          },
        },
      }}
    >
      {children}
    </Component>
  );
}

/** For non-string children that need animated wrapper */
export function AnimatedBlock({
  children,
  className,
  variant = "blur-reveal",
  delay = 0,
  duration = 0.6,
  once = true,
}: Omit<AnimatedTextProps, "children" | "as"> & { children: ReactNode }) {
  const shouldReduceMotion = useReducedMotion();

  if (shouldReduceMotion) {
    return <div className={className}>{children}</div>;
  }

  const variantMap = {
    "blur-reveal": {
      hidden: { opacity: 0, y: 20, filter: "blur(10px)" },
      visible: { opacity: 1, y: 0, filter: "blur(0px)" },
    },
    "slide-up": {
      hidden: { opacity: 0, y: 40 },
      visible: { opacity: 1, y: 0 },
    },
    fade: {
      hidden: { opacity: 0 },
      visible: { opacity: 1 },
    },
    "word-stagger": {
      hidden: { opacity: 0, y: 20 },
      visible: { opacity: 1, y: 0 },
    },
  };

  return (
    <motion.div
      className={className}
      initial="hidden"
      whileInView="visible"
      viewport={{ once, margin: "-60px" }}
      variants={{
        hidden: variantMap[variant].hidden,
        visible: {
          ...variantMap[variant].visible,
          transition: {
            duration,
            delay,
            ease: [0.25, 0.4, 0.25, 1],
          },
        },
      }}
    >
      {children}
    </motion.div>
  );
}
