import type React from "react";
import { useEffect, useRef, useState } from "react";

import { Icon, IconAction } from "@ax/components";

import * as S from "./style";

const TextField = (props: ITextFieldProps): JSX.Element => {
	const {
		name,
		value,
		error,
		placeholder,
		autoComplete,
		readonly,
		icon,
		onClickIcon,
		className,
		onChange,
		onBlur,
		onKeyDown,
		inputType = "text",
		disabled,
		iconPosition = "out",
		prefix,
		handleValidation,
		validators,
		editorID,
		focus,
		inversed = false,
		noeditable = false,
		selectOnFocus = false,
	} = props;

	const [width, setWidth] = useState(0);
	const [state, setState] = useState<string>(value);
	const prefixRef = useRef<HTMLDivElement>(null);
	const timeOutRef = useRef<ReturnType<typeof setTimeout>>();

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		const width = prefixRef.current ? prefixRef.current.offsetWidth : 0;
		setWidth(width);
	}, [prefixRef]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		if (!autoComplete && state !== value) {
			timeOutRef.current && clearTimeout(timeOutRef.current);
			timeOutRef.current = setTimeout(() => {
				delayedChange(state);
			}, 300);
		}
	}, [state]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		!autoComplete && setState(value);
	}, [editorID, value]);

	const handleOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
		if (noeditable) return;
		!autoComplete ? setState(e.target.value) : delayedChange(e.target.value);
	};

	const delayedChange = (newValue: string) => {
		onChange?.(newValue);
		error && handleValidation && handleValidation(newValue, validators);
	};

	const handleOnFocus = (e: React.FocusEvent<HTMLInputElement>) => {
		if (!selectOnFocus) return;
		const target = e.target;
		requestAnimationFrame(() => target.select());
	};

	const handleOnBlur = (e: React.FocusEvent<HTMLInputElement>) => {
		const newValue = e.target.value;
		onBlur?.(newValue);
		handleValidation?.(newValue, validators);
	};

	const hasBackgroundIcon = !!icon && iconPosition === "in";
	const hasButton = !!icon && iconPosition === "out";

	const Prefix = () => (
		<S.Prefix data-testid="prefix" ref={prefixRef}>
			{prefix}
		</S.Prefix>
	);

	const inputValue = !autoComplete ? state : value || "";

	return (
		<S.FieldWrapper data-testid="text-field-container" error={error} className={className}>
			{prefix && <Prefix />}
			<S.Input
				data-testid="input-component"
				type={inputType}
				value={inputValue}
				name={name}
				onChange={handleOnChange}
				onFocus={handleOnFocus}
				onBlur={handleOnBlur}
				onKeyDown={onKeyDown}
				placeholder={placeholder}
				autoComplete={autoComplete}
				error={error}
				readOnly={readonly}
				button={hasButton}
				disabled={disabled}
				icon={hasBackgroundIcon}
				hasPrefix={!!prefix}
				prefixWidth={width}
				aria-label={name}
				autoFocus={focus}
				spellCheck={true}
			/>
			{hasBackgroundIcon && (
				<S.BackgroundIcon data-testid="background-icon-component" onClick={onClickIcon} inversed={inversed}>
					<Icon name={icon || ""} size="24" />
				</S.BackgroundIcon>
			)}
			{hasButton && (
				<S.IconWrapper data-testid="icon-wrapper-component" readonly={readonly} error={error}>
					<IconAction icon={icon || ""} onClick={onClickIcon} disabled={disabled} size="s" />
				</S.IconWrapper>
			)}
		</S.FieldWrapper>
	);
};

interface ITextFieldProps {
	value: string;
	onChange: (value: string) => void;
	autoComplete?: string;
	inputType?: string;
	name?: string;
	error?: boolean;
	placeholder?: string;
	readonly?: boolean;
	icon?: string;
	onClickIcon?: () => void;
	className?: string;
	onBlur?: (value: string) => void;
	onKeyDown?: () => void;
	disabled?: boolean;
	iconPosition?: string;
	prefix?: string;
	handleValidation?: (value: string, validators?: Record<string, unknown>) => void;
	validators?: Record<string, unknown>;
	editorID?: number;
	focus?: boolean;
	inversed?: boolean;
	noeditable?: boolean;
	selectOnFocus?: boolean;
}

export default TextField;
