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

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

// Properties and some behavior of this textbox is based on the Text Field of MUI:
// https://mui.com/material-ui/react-text-field/
export interface TextBoxProps {
	// Commonly used text props
	name?: string;

	/**
	 * Try to avoid using placeholders as labels:
	 * - They are not accessible to screen readers.
	 * - They offer bad contrast.
	 * - They disappear when you start typing.
	 * See: https://www.nngroup.com/articles/form-design-placeholders/
	 * @example Consider using this instead:
	 * ```tsx
	 * 	<TextBoxLabel label="Betreff">
	 * 		<TextBox />
	 * 	</TextBoxLabel>
	 * ```
	 */
	placeholder?: string;
	"aria-label"?: string;
	disabled?: boolean;
	required?: boolean;
	autoComplete?: TextBoxAutoCompleteValue;
	autoFocus?: boolean;
	maxLength?: number;
	minLength?: number;
	pattern?: string;
	title?: string;
	defaultValue?: string;
	form?: string;
	/**
	 * Hints at the type of data that might be entered by the user while editing the element or its contents
	 * @see https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute
	 */
	inputMode?:
		| "none"
		| "text"
		| "tel"
		| "url"
		| "email"
		| "numeric"
		| "decimal"
		| "search"
		| 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) */
	value?: string;
	onChange?: (value: string, e: ChangeEvent<HTMLInputElement>) => void;

	onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;

	/**
	 * @remarks - Don't use `date` or `datetime-local`. Use the `DateInput` component instead.
	 */
	type?: // | "number" TODO: We don't support "number" yet because that is more complicated and most likely will be in a different component.
		| "text"
		| "password"
		| "email"
		| "tel"
		| "url"
		| "search"
		| "date"
		| "datetime-local";

	/** @remarks Don't use this. It is intended for internal use for {@link DateInput}. */
	min?: string;
	/** @remarks Don't use this. It is intended for internal use for {@link DateInput}. */
	max?: string;

	size?: "small" | "medium" | "large";

	textTransform?: "uppercase";

	/**
	 * Used for datalists. May be removed in the future. We still need to evaluate if this is a good way to do it.
	 */
	list?: string;

	/**
	 * @remarks For internal use only.
	 * @internal
	 * */
	ref?: Ref<HTMLInputElement>;
}

export default memo(TextBox);

const styles = stylex.create({
	box: {
		background: {
			default: controlColor.inputBackground,
			":focus": controlColor.inputActiveBackground,
			":disabled": controlColor.inputDisabledBackground,
		},

		borderWidth: "1px",
		borderStyle: "solid",
		borderColor: {
			default: controlColor.inputBorder,
			":focus": controlColor.inputActiveBorder,
			// https://web.dev/articles/user-valid-and-user-invalid-pseudo-classes
			":user-invalid": controlColor.inputInvalidBorder,
			":disabled": controlColor.inputDisabledBorder,
		},

		borderRadius: 0,
		color: {
			default: controlColor.inputColor,
			":focus": controlColor.inputActiveColor,
			":disabled": controlColor.inputDisabledColor,
		},

		width: "100%",
		fontFamily: font.main,

		"::placeholder": {
			color: controlColor.inputPlaceholderColor,
		},

		outline: {
			":focus": "none",
		},
		cursor: {
			":disabled": "not-allowed",
		},
	},
	upperCase: {
		textTransform: "uppercase",
	},
});

const variants = stylex.create({
	small: {
		padding: `${size.rem1} ${size.rem2}`,
		fontSize: size.rem4,
	},
	medium: {
		padding: `${size.px2} ${size.px3}`,
		fontSize: size.rem4,
		fontWeight: 400,
	},
	large: {
		padding: `${size.rem1} ${size.rem2}`,
		fontSize: size.rem6, // TODO: ASK-UX:So we have small/large text fields?
	},
});

function TextBox(props: TextBoxProps) {
	const onChange = useCallback(
		(e: ChangeEvent<HTMLInputElement>) => {
			props.onChange?.(e.currentTarget.value, e);
		},
		[props.onChange],
	);

	return (
		<input
			ref={props.ref}
			{...stylex.props(
				styles.box,
				variants[props.size ?? "small"],
				props.textTransform === "uppercase" && styles.upperCase,
			)}
			aria-label={props["aria-label"]}
			type={props.type ?? "text"}
			placeholder={props.placeholder}
			name={props.name}
			required={props.required}
			disabled={props.disabled}
			autoComplete={props.autoComplete}
			inputMode={props.inputMode}
			// biome-ignore lint/a11y/noAutofocus: We're just passing it down
			autoFocus={props.autoFocus}
			minLength={props.minLength}
			maxLength={props.maxLength}
			min={props.min}
			max={props.max}
			pattern={props.pattern}
			list={props.list}
			title={props.title}
			value={props.value}
			onChange={onChange}
			onKeyDown={props.onKeyDown}
			defaultValue={props.defaultValue}
			form={props.form}
		/>
	);
}

/**
 * See: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill
 * MDN link: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
 */
export type TextBoxAutoCompleteValue =
	| "off"
	| "name"
	| "email"
	| "honorific-prefix"
	| "given-name"
	| "additional-name"
	| "family-name"
	| "honorific-suffix"
	| "nickname"
	| "username"
	| "new-password"
	| "current-password"
	| "one-time-code"
	| "organization-title"
	| "organization"
	| "street-address"
	| "address-line1"
	| "address-line2"
	| "address-line3"
	| "address-level4"
	| "address-level3"
	| "address-level2"
	| "address-level1"
	| "country"
	| "country-name"
	| "postal-code"
	| "cc-name"
	| "cc-given-name"
	| "cc-additional-name"
	| "cc-family-name"
	| "cc-number"
	| "cc-exp"
	| "cc-exp-month"
	| "cc-exp-year"
	| "cc-csc"
	| "cc-type"
	| "transaction-currency"
	| "transaction-amount"
	| "language"
	| "bday"
	| "bday-day"
	| "bday-month"
	| "bday-year"
	| "sex"
	| "url"
	| "photo";
