import * as stylex from "@stylexjs/stylex";
import { type FormEvent, memo, useId } from "react";
import { sliderConfig, state } from "./Slider.vars.stylex";
import { controlColor } from "./theme.stylex";

const webkitThumb = "::-webkit-slider-thumb";
const firefoxThumb = "::-moz-range-thumb";

const webkitTack = "::-webkit-slider-runnable-track";
const firefoxTack = "::-moz-range-track";

const firefoxProgress = "::-moz-range-progress";

// Refs:
// - https://www.smashingmagazine.com/2021/12/create-custom-range-input-consistent-browsers/
// - https://css-tricks.com/styling-cross-browser-compatible-range-inputs-css/
// - https://stackoverflow.com/questions/18389224/how-to-style-html5-range-input-to-have-different-color-before-and-after-slider

const styles = stylex.create({
	base: {
		width: "100%",

		margin: 0,
		padding: 0,

		appearance: "none", // Hides the slider so that custom slider can be made
		backgroundColor: "transparent", // Otherwise white in Chrome

		accentColor: controlColor.sliderPassedTrackBackground,

		outline: {
			":focus": "none", // Removes the blue border. You should probably do some kind of focus styling for accessibility reasons though.
		},

		[state.cursor]: {
			default: "pointer",
			":disabled": "not-allowed",
		},

		[state.thumbBackground]: {
			default: sliderConfig.thumbBackground,
			":hover:not(:disabled)": controlColor.sliderThumbHoverBackground,
			":disabled": controlColor.sliderThumbDisabledBackground,
		},

		[state.trackBackground]: {
			default: controlColor.sliderTrackBackground,
			":disabled": controlColor.sliderTrackDisabledBackground,
		},

		[state.progressBackground]: {
			default: controlColor.sliderPassedTrackBackground,
			":disabled": controlColor.sliderDisabledPassedTrackBackground,
		},

		[webkitThumb]: {
			appearance: "none",

			cursor: state.cursor,

			width: sliderConfig.width,
			height: sliderConfig.height,

			borderStyle: sliderConfig.borderStyle,
			borderRadius: sliderConfig.borderRadius,

			background: state.thumbBackground,

			// This has to be a specified margin in Chrome, but in Firefox it is automatic
			marginTop: `calc(-1* ${sliderConfig.height} / 3)`,
		},

		// Note: DON'T use comma-separated selectors here. It won't work, since a browser will drop the entire rule if it doesn't understand one part of it.
		[firefoxThumb]: {
			cursor: state.cursor,

			width: sliderConfig.width,
			height: sliderConfig.height,

			borderStyle: sliderConfig.borderStyle,
			borderRadius: sliderConfig.borderRadius,

			background: state.thumbBackground,
		},

		[webkitTack]: {
			width: "100%",
			height: sliderConfig.trackHeight,

			cursor: state.cursor,

			background: state.trackBackground,

			borderStyle: sliderConfig.trackBorderStyle,
			borderRadius: sliderConfig.trackBorderRadius,
		},

		[firefoxTack]: {
			width: "100%",
			height: sliderConfig.trackHeight,

			cursor: state.cursor,

			background: state.trackBackground,

			borderStyle: sliderConfig.trackBorderStyle,
			borderRadius: sliderConfig.trackBorderRadius,
		},

		[firefoxProgress]: {
			background: state.progressBackground,
		},
	},
});

export interface SliderProps {
	name?: string;
	disabled?: boolean;
	defaultValue?: number;
	value?: number;
	onChange?: (value: number) => void;

	min?: number;
	max?: number;
	step?: number;
	list?: number[];
}

export default memo(function Slider({
	name,
	disabled,
	defaultValue,
	value,
	onChange,

	min,
	max,
	step,
	list,
}: SliderProps) {
	const markerId = useId();

	const onChangeWrapped = onChange
		? (e: FormEvent<HTMLInputElement>) =>
				onChange?.(e.currentTarget.valueAsNumber)
		: undefined;

	return (
		<>
			<input
				type="range"
				{...stylex.props(styles.base)}
				disabled={disabled}
				name={name}
				min={min}
				max={max}
				step={step}
				defaultValue={defaultValue}
				value={value}
				onChange={onChangeWrapped}
				list={list && markerId}
			/>
			{list && (
				<datalist id={markerId}>
					{list.map((value) => (
						<option key={value} value={value} />
					))}
				</datalist>
			)}
		</>
	);
});
