/* eslint-disable react-hooks/exhaustive-deps */
import { forwardRef, startTransition, useCallback, useMemo, useRef } from 'react';
import { useFormContext, UseFormRegister } from 'react-hook-form';

export type TextFieldProps = {
  placeholder?: string;
  inputMode?: 'text' | 'search' | 'email' | 'tel' | 'url' | 'none' | 'numeric' | 'decimal';
  readOnly?: boolean;
  selectLabel?: string;
  onInputFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
  onInputBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
  onInputChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
  isFocused: boolean;
  setIsFocused: React.Dispatch<React.SetStateAction<boolean>>;
  suffix?: string;
  prefix?: string;
  textFieldId: string;
} & ReturnType<UseFormRegister<any>>;

export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(function TextField(
  {
    name,
    placeholder,
    inputMode = 'text',
    onBlur,
    disabled,
    readOnly,
    onInputFocus,
    onInputBlur,
    selectLabel,
    onChange,
    onInputChange,
    maxLength,
    isFocused,
    setIsFocused,
    suffix,
    prefix,
    textFieldId,
  },
  ref,
) {
  const textRef = useRef<HTMLInputElement>(null);

  const {
    setValue,
    formState: { errors },
    watch,
    trigger,
  } = useFormContext();

  const value = watch(name);
  const hasValue = useMemo(() => value?.length > 0, [value]);
  const isFocusedAndNoValue = useMemo(() => isFocused && !hasValue, [isFocused, hasValue]);

  const handleBlur = useCallback(
    (event: React.FocusEvent<HTMLInputElement>) => {
      if (readOnly) return;
      startTransition(() => {
        setIsFocused(false);
        onBlur(event);
        onInputBlur?.(event);
        if (prefix || suffix) {
          trigger(name);
        }
      });
    },
    [setIsFocused, onBlur, onInputBlur],
  );

  const handleFocus = useCallback(
    (event: React.FocusEvent<HTMLInputElement>) => {
      if (readOnly) return;
      setIsFocused(true);
      onInputFocus?.(event);
    },
    [setIsFocused, onInputFocus],
  );

  const handleChange = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      startTransition(() => {
        // if maxLength is set, do not allow input more than maxLength
        if (maxLength && event.target.value?.length > maxLength) {
          setValue(name, event.target.value.substring(0, maxLength));
          return;
        }
        if (prefix || suffix) {
          const inputValue = event.target.value;
          const numberPart = inputValue.replace(/[^\d]/g, '');
          const formattedNumber = Number(numberPart).toLocaleString('ko-KR');
          setValue(name, numberPart);

          setTimeout(() => {
            const cursorPosition = (prefix ? prefix.length : 0) + formattedNumber.length;
            textRef.current?.setSelectionRange(cursorPosition, cursorPosition);
          }, 0);
        }

        onChange(event);
        onInputChange?.(event);
      });
    },
    [maxLength, setValue, name, onChange, onInputChange],
  );

  if (selectLabel) {
    return (
      <>
        <input type="hidden" name={name} ref={ref} id={textFieldId} />
        <input
          type="text"
          className="h-[28px] w-full rounded-none pr-[19px] text-xl leading-7 tracking-[0]"
          value={selectLabel}
          inputMode={inputMode}
          readOnly={readOnly}
          onFocus={handleFocus}
          onBlur={handleBlur}
          placeholder={isFocusedAndNoValue ? placeholder : ''}
          aria-invalid={errors[name] ? 'true' : 'false'}
        />
      </>
    );
  }

  const isNotNullValue = value !== null && value !== undefined && value !== '';

  if (prefix || suffix) {
    return (
      <>
        <input type="hidden" name={name} ref={ref} id={textFieldId} />
        <input
          type="text"
          value={`${isNotNullValue ? (prefix ?? '') + Math.abs(value).toLocaleString('ko-KR') + suffix : ''}`}
          ref={textRef}
          className="h-[28px] w-full rounded-none pr-[19px] text-xl leading-7 tracking-[0]"
          onChange={handleChange}
          onFocus={handleFocus}
          onBlur={handleBlur}
          inputMode={inputMode}
          disabled={disabled}
          readOnly={readOnly}
          placeholder={isFocusedAndNoValue ? placeholder : ''}
          aria-invalid={errors[name] ? 'true' : 'false'}
        />
      </>
    );
  }

  return (
    <input
      type="text"
      name={name}
      ref={ref}
      id={textFieldId}
      className="h-[28px] w-full rounded-none pr-[19px] text-xl leading-7 tracking-[0]"
      onChange={handleChange}
      onFocus={handleFocus}
      onBlur={handleBlur}
      inputMode={inputMode}
      disabled={disabled}
      readOnly={readOnly}
      placeholder={isFocusedAndNoValue ? placeholder : ''}
      aria-invalid={errors[name] ? 'true' : 'false'}
    />
  );
});
