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

import { sliderConfig, switchConfig } from "./ToggleSliderInput.vars.stylex";
import { interaction } from "./mixins";
import { controlColor } from "./theme.stylex";
import { color, duration, timingFunction } from "./tokens.stylex";

export interface ToggleSliderInputProps {
	name?: string;
	disabled?: boolean;
	title?: string;

	/** `value` and `onChange` are optional because we want to be able to use the control in an uncontrolled manner as well (for example, in forms) */
	value?: boolean;
	onChange?: (value: boolean) => void;

	defaultChecked?: boolean;
}

// Box around the slider
// Loosely based on:
// https://www.w3schools.com/howto/howto_css_switch.asp
// https://kittygiraudel.com/2021/04/05/an-accessible-toggle/#focused-styles
const switchStyle = stylex.create({
	base: {
		position: "relative",
		display: "inline-block",
		width: switchConfig.width,
		height: switchConfig.height,
	},
});

const sliderStyle = stylex.create({
	base: {
		position: "absolute",
		inset: 0,

		borderRadius: switchConfig.width,

		transition: `background-color ${duration.default} ${timingFunction.fast}`,

		"::before": {
			position: "absolute",
			content: "''",

			height: sliderConfig.size,
			width: sliderConfig.size,
			left: sliderConfig.offset,
			bottom: sliderConfig.offset,

			borderRadius: "50%",

			transition: `
				transform ${duration.default} ${timingFunction.fast},
				background-color ${duration.default} ${timingFunction.fast}
			`,
		},
	},

	off: {
		backgroundColor: color.gray400,
		"::before": {
			backgroundColor: color.gray000,
		},
	},
	on: {
		backgroundColor: controlColor.toggleCheckedBackground,
		"::before": {
			transform: `translateX(${sliderConfig.translationOffset})`,
			backgroundColor: controlColor.toggleCheckedPillBackground,
		},
	},
	disabledOff: {
		backgroundColor: color.gray400,
		"::before": {
			backgroundColor: color.gray100,
		},
	},
	disabledOn: {
		backgroundColor: controlColor.toggleCheckedDisabledBackground,
		"::before": {
			transform: `translateX(${sliderConfig.translationOffset})`,
			backgroundColor: controlColor.toggleCheckedDisabledPillBackground,
		},
	},
});

/** @remarks Exported for use in Drawer */
export default memo(function ToggleSliderInput(props: ToggleSliderInputProps) {
	return props.value !== undefined && props.onChange !== undefined ? (
		<InternalStateUncontrolledToggleSliderInput
			{...(props as InternalStateUncontrolledToggleSliderInputProps)}
		/>
	) : (
		<FakeUncontrolledToggleSliderInput
			{...(props as FakeUncontrolledToggleSliderInputProps)}
		/>
	);
});

interface FakeUncontrolledToggleSliderInputProps
	extends ToggleSliderInputProps {
	value?: undefined;
	onChange?: undefined;
}
const FakeUncontrolledToggleSliderInput = memo(
	function FakeUncontrolledToggleSliderInput(
		props: FakeUncontrolledToggleSliderInputProps,
	) {
		const [value, setValue] = useState(props.defaultChecked || false);
		return (
			<InternalStateUncontrolledToggleSliderInput
				{...props}
				value={value}
				onChange={setValue}
			/>
		);
	},
);

interface InternalStateUncontrolledToggleSliderInputProps
	extends ToggleSliderInputProps {
	value: boolean;
	onChange: (value: boolean) => void;
}

// We need to split this up into a fake uncontrolled component because StyleX does not support styling using the input's `checked` state.
// See: https://github.com/facebook/stylex/issues/536
const InternalStateUncontrolledToggleSliderInput = memo(
	function FakeUncontrolledToggleSliderInput({
		value,
		onChange,
		defaultChecked,
		disabled,
		name,
		title,
	}: InternalStateUncontrolledToggleSliderInputProps) {
		const onChangeEffective = useCallback(
			(e: ChangeEvent<HTMLInputElement>) => {
				onChange(e.target.checked);
			},
			[onChange],
		);

		return (
			<span {...stylex.props(switchStyle.base)}>
				<input
					type="checkbox"
					{...stylex.props(interaction.hideInput)}
					name={name}
					checked={value}
					onChange={onChangeEffective}
					defaultChecked={defaultChecked}
					disabled={disabled}
					title={title}
				/>
				<span
					{...stylex.props(
						sliderStyle.base,
						disabled
							? value
								? sliderStyle.disabledOn
								: sliderStyle.disabledOff
							: value
								? sliderStyle.on
								: sliderStyle.off,
					)}
				/>
			</span>
		);
	},
);
