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

import { color, duration, size, timingFunction } from "./tokens.stylex";

// This needs styling. The component currently only exists
// so that we can use it with its API

const fadeIn = stylex.keyframes({
	from: {
		opacity: 0,
	},
	to: {
		opacity: 1,
	},
});

const shake = stylex.keyframes({
	"0%": {
		transform: "translateX(0)",
	},
	"25%": {
		transform: "translateX(-4px)",
	},
	"50%": {
		transform: "translateX(4px)",
	},
	"75%": {
		transform: "translateX(-4px)",
	},
	"100%": {
		transform: "translateX(0)",
	},
});

const pop = stylex.keyframes({
	"0%": {
		transform: "scale(1, 1)",
	},
	"50%": {
		transform: "scale(1.05, 1.05)",
	},
	"100%": {
		transform: "scale(1, 1)",
	},
});

const flash = stylex.keyframes({
	"0%": {
		opacity: 0,
		filter: "brightness(1)",
	},
	"50%": {
		opacity: 1,
		filter: "brightness(1.75)",
	},
	"100%": {
		filter: "brightness(1)",
	},
});

const styles = stylex.create({
	alert: {
		display: "flex",
		alignItems: "center",
		gap: size.px4,

		padding: size.px4,
		margin: `${size.px4} 0`,
	},
	error: {
		backgroundColor: color.red400,
	},
	warning: {
		backgroundColor: color.apricot300,
	},
	info: {
		backgroundColor: color.blue400,
	},
	success: {
		backgroundColor: color.apple400,
	},

	icon: {
		width: size.px6,
		height: size.px6,
		minWidth: size.px6,
		minHeight: size.px6,
		mixBlendMode: "overlay",
	},
});

const fadeInStyles = stylex.create({
	info: {
		animationName: fadeIn,
		animationDuration: duration.slow,
		animationTimingFunction: timingFunction.fast,
	},
	error: {
		animationName: `${fadeIn}, ${shake}`,
		animationDuration: `${duration.slow}, ${duration.default}`,
		animationTimingFunction: `${timingFunction.fast}, ${timingFunction.fast}`,
	},
	warning: {
		animationName: flash,
		animationDuration: duration.default,
		animationTimingFunction: "ease-in-out",
		animationFillMode: "both",
	},
	success: {
		animationName: `${fadeIn}, ${pop}`,
		animationDuration: `${duration.default}, ${duration.default}`,
		animationTimingFunction: "ease-in-out, ease-in-out",
		// eslint-disable-next-line @stylexjs/valid-styles
		animationFillMode: "both, both", // TODO: Issue?
	},
});

/**
 * A simple alert banner.
 *
 * Inspired by:
 * https://mui.com/material-ui/react-alert/
 */
export interface AlertProps extends PropsWithChildren {
	severity: "error" | "warning" | "info" | "success";

	// TODO: title?: string;
	icon?: React.ReactNode;
	fadeIn?: boolean;

	/**
	 * When true, the alert will scroll into view when it is rendered.
	 */
	scrollIntoView?: boolean;
}

//#region Icons

// These icons were optimized using SVGOMG and originate from our internal icon set: https://jakearchibald.github.io/svgomg

const styleXIconProps = stylex.props(styles.icon);

const ErrorIcon = memo(() => (
	// biome-ignore lint/a11y/noSvgWithoutTitle: :nopers:
	<svg
		xmlns="http://www.w3.org/2000/svg"
		viewBox="0 0 24 24"
		width="1em"
		{...styleXIconProps}
	>
		<path
			fill="#fff"
			d="M12 0a12 12 0 1 0 12 12A12 12 0 0 0 12 0Zm5.49 16.07a1 1 0 0 1-1.41 1.42l-3.9-3.9a.25.25 0 0 0-.36 0l-3.9 3.9a1 1 0 1 1-1.41-1.42l3.9-3.89a.25.25 0 0 0 0-.36l-3.9-3.9a1 1 0 0 1 1.41-1.41l3.9 3.9a.25.25 0 0 0 .36 0l3.9-3.9a1 1 0 0 1 1.41 1.41l-3.9 3.9a.25.25 0 0 0 0 .36Z"
		/>
	</svg>
));
const WarningIcon = memo(() => (
	// biome-ignore lint/a11y/noSvgWithoutTitle: :nopers:
	<svg
		xmlns="http://www.w3.org/2000/svg"
		viewBox="0 0 24 24"
		width="1em"
		{...styleXIconProps}
	>
		<path
			fill="#fff"
			d="m23.77 20.57-10-19A2 2 0 0 0 12 .5a2 2 0 0 0-1.77 1.07l-10 19a2 2 0 0 0 .06 2A2 2 0 0 0 2 23.5h20a2 2 0 0 0 1.77-2.93ZM11 8.5a1 1 0 0 1 2 0v6a1 1 0 0 1-2 0ZM12.05 20a1.53 1.53 0 0 1-1.52-1.47A1.48 1.48 0 0 1 12 17a1.53 1.53 0 0 1 1.52 1.47A1.48 1.48 0 0 1 12.05 20Z"
		/>
	</svg>
));

const InformationIcon = memo(() => (
	// biome-ignore lint/a11y/noSvgWithoutTitle: :nopers:
	<svg
		xmlns="http://www.w3.org/2000/svg"
		viewBox="0 0 24 24"
		{...styleXIconProps}
	>
		<path
			fill="#fff"
			d="M12 0a12 12 0 1 0 12 12A12 12 0 0 0 12 0Zm.25 5a1.5 1.5 0 1 1-1.5 1.5 1.5 1.5 0 0 1 1.5-1.5Zm2.25 13.5h-4a1 1 0 0 1 0-2h.75a.25.25 0 0 0 .25-.25v-4.5a.25.25 0 0 0-.25-.25h-.75a1 1 0 0 1 0-2h1a2 2 0 0 1 2 2v4.75a.25.25 0 0 0 .25.25h.75a1 1 0 0 1 0 2Z"
		/>
	</svg>
));

const SuccessIcon = memo(() => (
	// biome-ignore lint/a11y/noSvgWithoutTitle: :nopers:
	<svg
		xmlns="http://www.w3.org/2000/svg"
		viewBox="0 0 24 24"
		{...styleXIconProps}
	>
		<path
			fill="#fff"
			d="M12 0a12 12 0 1 0 12 12A12 12 0 0 0 12 0Zm6.93 8.2-6.85 9.29a1 1 0 0 1-1.43.19l-4.89-3.91a1 1 0 0 1-.15-1.41A1 1 0 0 1 7 12.21l4.08 3.26L17.32 7a1 1 0 0 1 1.39-.21 1 1 0 0 1 .22 1.41Z"
		/>
	</svg>
));

//#endregion

const iconMap = {
	error: <ErrorIcon />,
	warning: <WarningIcon />,
	info: <InformationIcon />,
	success: <SuccessIcon />,
};
const ariaLiveMap = {
	error: "assertive",
	warning: "polite",
	info: undefined, // Same as "off"
	success: undefined, // Same as "off"
} as const;

// TODO: More a11ly according to https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/alert_role
export default memo(function Alert(props: AlertProps) {
	const divRef = useRef<HTMLDivElement>(null);

	useEffect(() => {
		if (props.scrollIntoView) {
			divRef.current?.scrollIntoView({ behavior: "smooth" });
		}
	}, [props.scrollIntoView]);

	return (
		<div
			{...stylex.props(
				styles.alert,
				styles[props.severity],
				props.fadeIn && fadeInStyles[props.severity],
			)}
			role="alert"
			aria-live={ariaLiveMap[props.severity]}
			ref={divRef}
		>
			{props.icon ?? iconMap[props.severity]}
			<div>{props.children}</div>
		</div>
	);
});
