import * as stylex from "@stylexjs/stylex";
import type { CompiledStyles } from "@stylexjs/stylex/lib/StyleXTypes"; // This seems like a hack
import { type CSSProperties, memo } from "react";

import { controlColor } from "./theme.stylex";
import { color, size } from "./tokens.stylex";

/**
 * Omit `max` and `value` to make the progress bar indeterminate.
 */
export interface ProgressBarProps {
	/**
	 * This attribute specifies how much of the task that has been completed. It must be a valid floating point number between 0 and max, or between 0 and 1 if max is omitted.
	 * If there is no value attribute, the progress bar is indeterminate; this indicates that an activity is ongoing with no indication of how long it is expected to take.
	 */
	value?: number;

	/**
	 * This attribute describes how much work the task indicated by the progress element requires.
	 * The max attribute, if present, must have a value greater than 0 and be a valid floating point number. The default value is `1`.
	 */
	max?: number;

	/**
	 * When set to true, will be `display: inline;`
	 */
	inline?: boolean;

	label?: string;

	error?: boolean;
}

const styles = stylex.create({
	bar: {
		display: "block",
		height: size.px1,
		width: "100%",
		backgroundColor: color.gray400,
	},

	inline: {
		display: "inline",
	},

	error: {
		backgroundColor: controlColor.progressErrorBackground,
	},
	indeterminate: {
		overflow: "hidden",
	},
});

const indeterminate = stylex.keyframes({
	"0%": {
		transform: "translateX(0) scaleX(0)",
	},
	"40%": {
		transform: "translateX(0) scaleX(0.4)",
	},
	"100%": {
		transform: "translateX(100%) scaleX(0.5)",
	},
});

const innerStyles = stylex.create({
	inner: {
		height: "100%",
		width: "var(--progress-width)",
		transition: "width 0.25s ease-in-out",
		backgroundColor: controlColor.buttonTertiaryColor,
	},

	error: {
		backgroundColor: controlColor.progressErrorBackground,
	},

	indeterminate: {
		width: "100%",
		transformOrigin: "0% 50%",

		animationName: indeterminate,
		animationDuration: "2s",
		animationTimingFunction: "linear",
		animationIterationCount: "infinite",
	},
});

const clamp = (value: number, min: number, max: number) =>
	value < min ? min : value > max ? max : value;

export default memo(function ProgressBar(props: ProgressBarProps) {
	// Not using <progress /> because it was a hell to style. This is far more reliable.

	if (props.max === undefined && props.value === undefined) {
		const outerProps = stylex.props(
			styles.bar,
			props.inline && styles.inline,
			styles.indeterminate,
		);

		const innerProps = stylex.props(
			innerStyles.inner,
			props.error && innerStyles.error,
			innerStyles.indeterminate,
		);

		return (
			// biome-ignore lint/a11y/useFocusableInteractive: Nope
			<div
				{...outerProps}
				// biome-ignore lint/a11y/useSemanticElements: Normal progress bar is a hell to style
				// biome-ignore lint/a11y/useAriaPropsForRole: This is indeterminate
				role="progressbar"
				aria-label={props.label}
			>
				<div {...innerProps} />
			</div>
		);
	}

	const max = props.max ?? 1;
	const value = props.value ?? 0;
	const percentage = Math.round(clamp(value / max, 0, 1) * 100);
	const style = {
		// biome-ignore lint/style/useTemplate: Nope
		"--progress-width": percentage + "%",
	} as unknown as CSSProperties;

	return (
		// biome-ignore lint/a11y/useFocusableInteractive: Nope
		// biome-ignore lint/a11y/useSemanticElements: Normal progress bar is a hell to style
		<div
			role="progressbar"
			{...stylex.props(styles.bar, props.inline && styles.inline)}
			aria-label={props.label}
			aria-valuemin={0}
			aria-valuemax={100}
			aria-valuenow={percentage}
		>
			<div
				{...stylex.props(
					innerStyles.inner,
					props.error && innerStyles.error,
					style as unknown as CompiledStyles,
				)}
			/>
		</div>
	);
});
