import * as stylex from "@stylexjs/stylex";
import { type PropsWithChildren, memo, useState } from "react";

import usePrefersReducedMotion from "./usePrefersReducedMotion";
import useRandomInterval from "./useRandomInterval";

// Taken and adapted from:
// https://www.joshwcomeau.com/react/animated-sparkles-in-react/

const spin = stylex.keyframes({
	"0%": { transform: "rotate(0deg)" },
	"100%": { transform: "rotate(180deg)" },
});
const comeInOut = stylex.keyframes({
	"0%": { transform: "scale(0)" },
	"50%": { transform: "scale(1)" },
	"100%": { transform: "scale(0)" },
});

const noPreference = "@media (prefers-reduced-motion: no-preference)";

const styles = stylex.create({
	wrapper: {
		display: "inline-block",
		position: "relative",
	},
	childWrapper: {
		position: "relative",
		zIndex: 1,
		fontWeight: 600,
	},
	sparkleSvg: {
		display: "block",
		animationName: {
			[noPreference]: spin,
		},
		animationTimingFunction: {
			[noPreference]: "linear",
		},
		animationDuration: {
			[noPreference]: "1000ms",
		},
	},
	sparkleWrapper: {
		display: "block",
		position: "absolute",
		animationName: {
			[noPreference]: comeInOut,
		},
		animationDuration: {
			[noPreference]: "850ms",
		},
		animationFillMode: {
			[noPreference]: "forwards",
		},
	},
});

const DEFAULT_COLOR = "var(--t-subscription-color)";

const random = (min: number, max: number) =>
	((Math.random() * (max - min)) | 0) + min;

const generateSpark = (color: string) => ({
	id: String(random(10000, 99999)),
	createdAt: Date.now(),
	color,
	size: random(10, 20),
	style: {
		top: `${random(-50, 100)}%`,
		left: `${random(-10, 100)}%`,
	},
});

export interface SparklesProps extends PropsWithChildren {
	color?: string;
	// Support for delegated props were removed. Maybe we'll add them later.
	// ...delegated
}

export default memo(function Sparkles(props: SparklesProps) {
	const color = props.color ?? DEFAULT_COLOR;

	const [sparkles, setSparkles] = useState(() => {
		return [
			generateSpark(color),
			generateSpark(color),
			generateSpark(color),
		];
	});

	const prefersReducedMotion = usePrefersReducedMotion();
	useRandomInterval(
		() => {
			const sparkle = generateSpark(color);
			const now = Date.now();
			const nextSparkles = sparkles.filter(
				(sp) => now - sp.createdAt < 1000,
			);
			nextSparkles.push(sparkle);
			setSparkles(nextSparkles);
		},
		prefersReducedMotion ? null : 450,
		prefersReducedMotion ? null : 800,
	);

	return (
		<span {...stylex.props(styles.wrapper)}>
			{sparkles.map((sparkle) => (
				<Sparkle
					key={sparkle.id}
					color={sparkle.color}
					size={sparkle.size}
					style={sparkle.style as SparkleProps["style"]}
				/>
			))}
			<strong {...stylex.props(styles.childWrapper)}>
				{props.children}
			</strong>
		</span>
	);
});

interface SparkleProps {
	size: number;
	color: string;
	style: stylex.StaticStyles<{ top: number | string; left: number | string }>;
}

const Sparkle = memo(function Sparkle({ size, color, style }: SparkleProps) {
	const path =
		"M26.5 25.5C19.0043 33.3697 0 34 0 34C0 34 19.1013 35.3684 26.5 43.5C33.234 50.901 34 68 34 68C34 68 36.9884 50.7065 44.5 43.5C51.6431 36.647 68 34 68 34C68 34 51.6947 32.0939 44.5 25.5C36.5605 18.2235 34 0 34 0C34 0 33.6591 17.9837 26.5 25.5Z";
	return (
		<span {...stylex.props(styles.sparkleWrapper, style)}>
			{/* biome-ignore lint/a11y/noSvgWithoutTitle: It's a spark. */}
			<svg
				{...stylex.props(styles.sparkleSvg)}
				width={size}
				height={size}
				viewBox="0 0 68 68"
				fill="none"
			>
				<path d={path} fill={color} />
			</svg>
		</span>
	);
});
