import { ValidationError } from "class-validator";
import classNames from "classnames";
import React from "react";
import ReactSelect, { ActionMeta, MultiValue, Options, PropsValue } from "react-select";

import { IOptionOld } from "../Form/SelectFieldOld";
import Typography, { ETypo, ETypoColor } from "../Typography";
import classes from "./classes.module.scss";
import { styles } from "./styles";

type IProps = {
	options: IOptionOld[];
	label?: string | JSX.Element;
	placeholder?: string;
	onChange?: (newValue: MultiValue<IOptionOld>, actionMeta: ActionMeta<IOptionOld>) => void;
	defaultValue?: PropsValue<IOptionOld>;
	value?: PropsValue<IOptionOld>;
	isMulti?: boolean;
	shouldCloseMenuOnSelect: boolean;
	isOptionDisabled?: (option: IOptionOld, selectValue: Options<IOptionOld>) => boolean;
	validationError?: ValidationError;
};
type IState = {
	isFocused: boolean;
	selectedOptions: MultiValue<IOptionOld>;
	validationError: ValidationError | null;
};

export default class MultiSelect extends React.Component<IProps, IState> {
	public static defaultProps: Partial<IProps> = {
		placeholder: "Sélectionner une option...",
		shouldCloseMenuOnSelect: false,
	};

	constructor(props: IProps) {
		super(props);
		this.state = {
			isFocused: false,
			selectedOptions: [],
			validationError: this.props.validationError ?? null,
		};
		this.hasError = this.hasError.bind(this);
		this.onChange = this.onChange.bind(this);
		this.onEmptyResearch = this.onEmptyResearch.bind(this);
		this.onFocus = this.onFocus.bind(this);
		this.onBlur = this.onBlur.bind(this);
		this.renderErrors = this.renderErrors.bind(this);
	}
	public override render(): JSX.Element {
		return (
			<div className={classes["root"]}>
				<div className={classNames(classes["label-container"], this.state.selectedOptions.length >= 1 && classes["active"])}>
					{this.props.label && <div className={classes["label"]}>{this.props.label}</div>}
					{this.props.placeholder && (
						<div
							className={classes["placeholder"]}
							data-selected={(this.state.isFocused || this.state.selectedOptions.length >= 1).toString()}>
							<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_500}>
								{this.props.placeholder}
							</Typography>
						</div>
					)}
					<div className={classes["input-container"]}>
						<ReactSelect
							placeholder={""}
							options={this.props.options}
							styles={styles}
							onChange={this.onChange}
							value={this.props.defaultValue}
							defaultValue={this.state.selectedOptions}
							closeMenuOnSelect={this.props.shouldCloseMenuOnSelect}
							isMulti
							isOptionDisabled={this.props.isOptionDisabled}
							noOptionsMessage={this.onEmptyResearch}
							onFocus={this.onFocus}
							onBlur={this.onBlur}
							classNamePrefix="react-select"
						/>
					</div>
				</div>
				{this.hasError() && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
			</div>
		);
	}

	public override componentDidMount(): void {
		if (this.props.defaultValue) {
			// If default value contains multiple IOptions
			if (Array.isArray(this.props.defaultValue)) {
				this.setState({ selectedOptions: this.props.defaultValue });
			}

			// If default value is a single IOption
			if ("label" in this.props.defaultValue) {
				this.setState({ selectedOptions: [this.props.defaultValue] });
			}
		}
	}

	public override componentDidUpdate(prevProps: IProps): void {
		if (this.props.validationError !== prevProps.validationError) {
			this.setState({
				validationError: this.props.validationError ?? null,
			});
		}
		if (this.props.defaultValue && this.props.defaultValue !== prevProps.defaultValue) {
			// If default value contains multiple IOptions
			if (Array.isArray(this.props.defaultValue)) {
				this.setState({ selectedOptions: this.props.defaultValue });
			}

			// If default value is a single IOption
			if ("label" in this.props.defaultValue) {
				this.setState({ selectedOptions: [this.props.defaultValue] });
			}
		}
	}

	private onFocus() {
		this.setState({ isFocused: true });
	}

	private onBlur() {
		this.setState({ isFocused: false });
	}

	private onChange(newValue: MultiValue<IOptionOld>, actionMeta: ActionMeta<IOptionOld>) {
		this.props.onChange && this.props.onChange(newValue, actionMeta);
		this.setState({
			selectedOptions: newValue,
			validationError: null,
		});
	}

	private onEmptyResearch() {
		if (this.state.selectedOptions.length === this.props.options.length) {
			return null;
		}
		return "Aucune option trouvée";
	}

	protected hasError(): boolean {
		return this.state.validationError !== null;
	}

	protected renderErrors(): JSX.Element[] | null {
		if (!this.state.validationError || !this.state.validationError.constraints) return null;
		let errors: JSX.Element[] = [];
		Object.entries(this.state.validationError.constraints).forEach(([key, value]) => {
			errors.push(
				<Typography key={key} typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_ERROR_600}>
					{value}
				</Typography>,
			);
		});
		return errors;
	}
}
