import { useAnimate, type AnimationSequence } from "framer-motion";
import { Button } from "~/components/ui/button";
import { IconMatrix, type IconType } from "~/components/IconComponents";
import { cn } from "~/lib/utils";
import {
  forwardRef,
  useImperativeHandle,
  type ComponentPropsWithoutRef,
} from "react";

const randomNumberBetween = (min: number, max: number) => {
  return Math.floor(Math.random() * (max - min + 1) + min);
};

export interface AnimatedButtonRef {
  sparkle: () => void;
}

interface AnimatedButtonProps extends ComponentPropsWithoutRef<typeof Button> {
  sparklesRadius?: number;
  sparklesNumber?: number;
  sparklesClassName?: string;
  sparklesIcon?: IconType;
  sparklesScale?: [number, number];
}

export const AnimatedButton = forwardRef<
  AnimatedButtonRef,
  AnimatedButtonProps
>(
  (
    {
      sparklesRadius = 100,
      sparklesNumber = 20,
      sparklesClassName = "fill-blue-500 stroke-transparent",
      sparklesIcon = "star",
      sparklesScale = [1.5, 2.5],
      ...props
    },
    ref,
  ) => {
    const [scope, animate] = useAnimate();

    const Sparkles = () => {
      const sparkles = Array.from({ length: sparklesNumber });
      const sparklesAnimation: AnimationSequence = sparkles.map((_, index) => [
        `.sparkle-${index}`,
        {
          x: randomNumberBetween(-sparklesRadius, sparklesRadius),
          y: randomNumberBetween(-sparklesRadius, sparklesRadius),
          scale: randomNumberBetween(sparklesScale[0], sparklesScale[1]),
          opacity: 1,
        },
        {
          duration: 0.4,
          at: "<",
          delay: index * 0.01,
        },
      ]);

      const sparklesFadeOut: AnimationSequence = sparkles.map((_, index) => [
        `.sparkle-${index}`,
        {
          opacity: 0,
          scale: 0,
        },
        {
          duration: 0.3,
          at: "<",
        },
      ]);

      const sparklesReset: AnimationSequence = sparkles.map((_, index) => [
        `.sparkle-${index}`,
        {
          x: 0,
          y: 0,
        },
        {
          duration: 0.000001,
        },
      ]);

      animate([
        ...sparklesReset,
        ["button", { scale: 0.8 }, { duration: 0.1, at: "<" }],
        ["button", { scale: 1 }, { duration: 0.1 }],
        ...sparklesAnimation,
        ["button", { duration: 0.000001 }],
        ...sparklesFadeOut,
      ]);
    };

    useImperativeHandle(ref, () => ({
      sparkle: Sparkles,
    }));

    return (
      <div ref={scope} className="w-full">
        <Button
          {...props}
          className={cn("relative", props.className)}
          aria-label="Animated Button"
        >
          {props.children}
          <span
            aria-hidden
            className="pointer-events-none absolute inset-0 z-10 block"
          >
            {Array.from({ length: sparklesNumber }).map((_, index) => (
              <IconMatrix
                type={sparklesIcon}
                className={cn(
                  // eslint-disable-next-line better-tailwindcss/no-unregistered-classes
                  `absolute top-1/2 left-1/2 opacity-0 sparkle-${index}`,
                  sparklesClassName,
                )}
                key={index}
              />
            ))}
          </span>
        </Button>
      </div>
    );
  },
);

AnimatedButton.displayName = "AnimatedButton";
