import * as stylex from "@stylexjs/stylex";
import {
	type FormEvent,
	type ReactElement,
	createContext,
	memo,
	use,
} from "react";

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

import { a11y, interaction } from "./mixins";
import useRippleEffect from "./useRippleEffect";

const styles = stylex.create({
	fieldset: {
		display: "flex",
		gap: size.px0_5,

		borderStyle: "none",
		margin: 0,
		padding: 0,
	},

	legend: {
		fontSize: fontSize.body,
		marginBottom: size.px0_5,
	},

	option: {
		flex: "1",
		userSelect: "none",

		cursor: "pointer",
		padding: `${size.px2} ${size.px3}`,

		transition: `${transition.a11yOutline}, ${transition.themeBackground}, ${transition.themeColor}`,

		backgroundColor: {
			default: color.gray500,
			":has(input:checked)": controlColor.checkboxCheckedBackground,
		},

		fontSize: fontSize.sub,
		textAlign: "center",
		textTransform: "lowercase",
	},
});

type InlineRadioButtonGroupContextType = {
	/** Name for the HTML data value in forms */
	name: string;
	/** A selection is required */
	required: boolean;
	/** Disable entire group of radio buttons. */
	disabled: boolean;
	/** `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: string | undefined;
	/** `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) */
	onChange: ((value: string) => void) | undefined;
};

const InlineRadioButtonGroupContext =
	createContext<InlineRadioButtonGroupContextType>(
		// biome-ignore lint/style/noNonNullAssertion: Context always set in group component
		undefined!,
	);
InlineRadioButtonGroupContext.displayName = "InlineRadioButtonGroupContext";

export type InlineRadioButtonGroupProps = {
	label?: string;
	/** Name for the HTML data value in forms */
	name: string;
	/** A selection is required */
	required?: boolean;
	/** Disable entire group of radio buttons. */
	disabled?: boolean;
	/** `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?: string | undefined;
	/** `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) */
	onChange?: (value: string) => void;
	children:
		| ReactElement<InlineRadioButtonProps>
		| ReactElement<InlineRadioButtonProps>[];
};

export const InlineRadioButtonGroup = memo(function InlineRadioButtonGroup(
	props: InlineRadioButtonGroupProps,
) {
	const ctx = {
		value: props.value,
		onChange: props.onChange,
		name: props.name,
		required: !!props.required,
		disabled: !!props.disabled,
	};

	return (
		<fieldset {...stylex.props(styles.fieldset)}>
			{props.label && (
				<legend {...stylex.props(styles.legend)}>{props.label}</legend>
			)}
			<InlineRadioButtonGroupContext.Provider value={ctx}>
				{props.children}
			</InlineRadioButtonGroupContext.Provider>
		</fieldset>
	);
});

export type InlineRadioButtonProps = {
	label: string;
	value: string;

	/** Disabled this specific radio button in the group. */
	disabled?: boolean;
	defaultChecked?: boolean;
};

export const InlineRadioButton = memo(function InlineRadioButton(
	props: InlineRadioButtonProps,
) {
	const ctx = use(InlineRadioButtonGroupContext);

	const disabled = props.disabled || ctx.disabled;
	const ref = useRippleEffect(disabled);

	const checked =
		ctx.value !== undefined ? ctx.value === props.value : undefined;
	const onChange = ctx.onChange
		? (e: FormEvent<HTMLInputElement>) =>
				ctx.onChange?.(e.currentTarget.value)
		: undefined;

	return (
		<label
			{...stylex.props(
				interaction.disableTapHighlight,
				a11y.defaultOutline,
				styles.option,
			)}
			ref={ref}
		>
			<input
				type="radio"
				{...stylex.props(interaction.hideInput)}
				disabled={disabled}
				required={ctx.required}
				title={props.label}
				name={ctx.name}
				value={props.value}
				onChange={onChange}
				checked={checked}
				defaultChecked={props.defaultChecked}
			/>
			<span>{props.label}</span>
		</label>
	);
});
