"use client";

import { useRef, useEffect, memo } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { SplitText } from "gsap/SplitText";
import { cls } from "@/lib/utils";

interface ScrollTriggerConfig {
  trigger: HTMLElement;
  start: string;
  end: string;
  markers: boolean;
  toggleActions: string;
}

type AnimationType = "entrance-slide" | "reveal-blur" | "background-highlight";
type VariantType = "trigger" | "words-trigger";

interface TextAnimationProps {
  type?: AnimationType;
  text: string;
  children?: React.ReactNode;
  className?: string;
  duration?: number;
  stagger?: number;
  start?: string;
  end?: string;
  variant?: VariantType;
  ariaLabel?: string;
  as?: "div" | "span" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p";
  gradientColors?: {
    from: string;
    to: string;
  };
}

const ANIMATION_CONFIG = {
  trigger: {
    stagger: 0.0075,
    useDuration: false,
    duration: 0.6,
  },
  "words-trigger": {
    stagger: 0.03,
    useDuration: false,
    duration: 0.6,
  },
} as const;

const ANIMATION_TYPES = {
  "entrance-slide": {
    classPrefix: "slide",
    fromVars: {
      opacity: 0,
      yPercent: 50,
    },
    toVars: {
      opacity: 1,
      yPercent: 0,
      ease: "power1",
    },
  },
  "reveal-blur": {
    classPrefix: "blur",
    fromVars: {
      autoAlpha: 0,
      filter: "blur(10px)",
    },
    toVars: {
      autoAlpha: 1,
      filter: "blur(0px)",
      ease: "power1.inOut",
    },
  },
  "background-highlight": {
    classPrefix: "highlight",
    fromVars: {
      opacity: 0.25,
    },
    toVars: {
      opacity: 1,
      ease: "power2.inOut",
    },
  },
} as const;

const TextAnimation = ({
  type = "entrance-slide",
  text,
  children,
  className = "",
  duration = 1,
  stagger,
  start = "top 80%",
  end = "top 20%",
  variant = "trigger",
  ariaLabel,
  as = "div",
  gradientColors,
}: TextAnimationProps) => {
  const textRef = useRef<HTMLDivElement | HTMLSpanElement>(null);
  const splitRef = useRef<SplitText | null>(null);
  const scrollTriggerRef = useRef<ScrollTrigger | null>(null);

  useEffect(() => {
    if (!textRef.current) return;

    const ctx = gsap.context(() => {
      const isWords = variant === "words-trigger";
      const animConfig = ANIMATION_TYPES[type];
      const classPrefix = animConfig.classPrefix;

      splitRef.current = new SplitText(textRef.current!, {
        type: isWords ? "lines,words" : "lines,words,chars",
        linesClass: `${classPrefix}-line`,
        wordsClass: `${classPrefix}-word`,
        charsClass: isWords ? undefined : `${classPrefix}-char`,
      });

      const lines = splitRef.current.lines;
      if (type === "entrance-slide") {
        gsap.set(lines, {
          overflow: "hidden",
          display: as === "span" ? "inline" : undefined,
        });
      } else if (as === "span") {
        gsap.set(lines, {
          display: "inline",
        });
      }

      const words = splitRef.current.words;
      gsap.set(words, {
        display: "inline-block",
        whiteSpace: "nowrap",
      });

      const animateTarget = isWords
        ? splitRef.current.words
        : splitRef.current.chars;
      const config = ANIMATION_CONFIG[variant];
      const animationDuration = config.useDuration
        ? duration
        : config.duration!;
      const animationStagger = stagger ?? config.stagger;

      if (gradientColors) {
        animateTarget.forEach((element) => {
          gsap.set(element as HTMLElement, {
            backgroundImage: `linear-gradient(180deg, ${gradientColors.from} 0%, ${gradientColors.to} 100%)`,
            WebkitBackgroundClip: "text",
            backgroundClip: "text",
            WebkitTextFillColor: "transparent",
            display: "inline-block",
          });
        });
      }

      const scrollTriggerConfig: ScrollTriggerConfig = {
        trigger: textRef.current!,
        start: start,
        end: end,
        markers: false,
        toggleActions: "play none none none",
      };

      if (type === "reveal-blur") {
        const tl = gsap.timeline({
          scrollTrigger: scrollTriggerConfig,
        });

        const parentElement = textRef.current;
        const splitInstance = splitRef.current;

        tl.fromTo(
          animateTarget,
          animConfig.fromVars,
          {
            ...animConfig.toVars,
            duration: animationDuration,
            stagger: animationStagger,
            force3D: true,
            onStart: function () {
              if (this._targets && this._targets.length > 0) {
                this._targets.forEach((target: HTMLElement) => {
                  target.style.willChange = "filter, opacity, transform";
                });
              }
            },
            onComplete: function () {
              if (this._targets && this._targets.length > 0) {
                this._targets.forEach((target: HTMLElement) => {
                  target.style.willChange = "auto";
                });
              }

              if (parentElement && splitInstance) {
                gsap.set(parentElement, {
                  opacity: 1,
                  visibility: 'visible',
                  filter: 'blur(0px)',
                  transform: 'none'
                });

                splitInstance.revert();
                splitRef.current = null;
              }
            },
          }
        );

        scrollTriggerRef.current = tl.scrollTrigger as ScrollTrigger;
      } else {
        const parentElement = textRef.current;
        const splitInstance = splitRef.current;

        gsap.fromTo(
          animateTarget,
          animConfig.fromVars,
          {
            ...animConfig.toVars,
            duration: animationDuration,
            stagger: animationStagger,
            force3D: true,
            scrollTrigger: scrollTriggerConfig,
            onComplete: () => {
              if (parentElement && splitInstance) {
                gsap.set(parentElement, {
                  opacity: 1,
                  transform: 'none'
                });

                splitInstance.revert();
                splitRef.current = null;
              }
            },
          }
        );
      }
    }, textRef);

    const currentTextRef = textRef.current;

    return () => {
      if (
        type === "reveal-blur" &&
        scrollTriggerRef.current &&
        scrollTriggerRef.current.trigger === currentTextRef
      ) {
        scrollTriggerRef.current.kill();
      }
      ctx.revert();
      if (splitRef.current) {
        splitRef.current.revert();
      }
    };
  }, [text, type, duration, stagger, start, end, variant, as, gradientColors]);

  const animConfig = ANIMATION_TYPES[type];
  const Component = as;

  return (
    <Component
      // @ts-expect-error - Dynamic component type requires flexible ref
      ref={textRef}
      className={cls(
        `${animConfig.classPrefix}-text`,
        className
      )}
      style={{
        fontKerning: 'none',
        textRendering: 'optimizeSpeed',
      }}
      aria-label={ariaLabel || text || undefined}
    >
      {children || text?.replace(/-/g, '\u2011')}
    </Component>
  );
};

TextAnimation.displayName = "TextAnimation";

export default memo(TextAnimation);