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

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}`,
				{
					opacity: 1,
					scale: randomNumberBetween(sparklesScale[0], sparklesScale[1]),
					x: randomNumberBetween(-sparklesRadius, sparklesRadius),
					y: randomNumberBetween(-sparklesRadius, sparklesRadius),
				},
				{
					at: "<",
					delay: index * 0.01,
					duration: 0.4,
				},
			]);

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

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

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

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

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

AnimatedButton.displayName = "AnimatedButton";
