import { ChangeEvent, useState } from 'react';
import {
  useFormContext,
  Controller,
  UseControllerProps,
  Noop,
  FieldError,
} from 'react-hook-form';

import TextField, { TextFieldProps } from '@material-hu/mui/TextField';
import { sanitizeInput } from '@material-hu/utils/validations';

import { useLokaliseTranslation } from 'src/utils/i18n';
import {
  validateRequiredStringRule,
  validateMinStringRule,
  validateMaxStringRule,
  validateMaxRule,
  validateMinRule,
  validateDecimalRule,
  validatePositiveRule,
} from 'src/utils/validation';

export type FormTextFieldProps = Omit<TextFieldProps, 'onBlur' | 'onFocus'> &
  UseControllerProps & {
    resetOnEmptyBlur?: boolean;
    blurOnEnter?: boolean;
    onBlur?: (newValue: string) => void;
    onFocus?: (
      event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>,
      newValue: string,
    ) => void;
    valueAsPlaceholder?: boolean;
    required?: boolean;
    minLength?: number;
    maxLength?: number;
    minLengthError?: string;
    maxLengthError?: string;
    min?: number;
    max?: number;
    minError?: string;
    maxError?: string;
    positive?: boolean;
    decimal?: boolean;
    showCounter?: boolean;
    allowJustNumbers?: boolean;
    sanitize?: boolean;
  };

function FormTextField({
  name,
  rules,
  defaultValue,
  resetOnEmptyBlur,
  blurOnEnter,
  onBlur: onBlurProp,
  onFocus: onFocusProp,
  onChange: onChangeProp,
  onKeyDown = () => null,
  valueAsPlaceholder = false,
  placeholder,
  required = false,
  minLength,
  maxLength,
  minLengthError,
  maxLengthError,
  min,
  max,
  minError,
  maxError,
  decimal = false,
  positive = false,
  showCounter = false,
  allowJustNumbers = false,
  sanitize = true,
  error: errorProp,
  ...others
}: FormTextFieldProps) {
  const [previousState, setPreviousState] = useState('');

  const { t } = useLokaliseTranslation('backoffice_only');
  const { control, setValue, watch } = useFormContext();

  const value = watch(name);
  const length = value?.length || 0;

  const isValidNumberValue = (newValue: string) => {
    if (newValue === '') return true;
    if (!newValue || newValue === ' ') return false;
    return !Number.isNaN(Number(newValue));
  };

  const handleChange =
    (onChange: Function) =>
    (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
      if (allowJustNumbers) {
        const inputValue = event.target.value;
        const newValue = isValidNumberValue(inputValue) ? inputValue : value;

        onChange(newValue ?? '');
      } else {
        onChange(event);
      }

      if (onChangeProp) onChangeProp(event);
    };

  const handleBlur =
    (field: { value: string; name: string }, onBlur: Noop) => () => {
      onBlur();
      if (onBlurProp) onBlurProp(field.value);
      if (resetOnEmptyBlur && field.value === '')
        setValue(field.name, previousState);
    };

  const handleFocus =
    (field: { value: string; name: string }) =>
    (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => {
      if (onFocusProp) onFocusProp(event, field.value);
      if (resetOnEmptyBlur) {
        setPreviousState(field.value);
      }
    };

  const handleKeyDown =
    (field: { value: string; name: string }, onBlur: Noop) =>
    (event: React.KeyboardEvent<HTMLDivElement>) => {
      onKeyDown(event);
      if (blurOnEnter && event.key === 'Enter') {
        event.preventDefault();
        handleBlur(field, onBlur)();
      }
    };

  const getHelperText = (error?: FieldError) => {
    if (error?.message) return error.message;
    if (showCounter)
      return t('backoffice_only:form_text_field.characters_counter', {
        count: length,
        max: maxLength,
      });
    return others?.helperText;
  };

  return (
    <Controller
      render={({
        field: { ref, onBlur, onChange, ...field },
        fieldState: { error },
      }) => (
        <TextField
          {...field}
          onFocus={handleFocus(field)}
          onBlur={handleBlur(field, onBlur)}
          onChange={handleChange(onChange)}
          onKeyDown={handleKeyDown(field, onBlur)}
          inputRef={ref}
          fullWidth
          placeholder={valueAsPlaceholder ? field.value : placeholder}
          value={valueAsPlaceholder ? '' : field.value}
          error={errorProp || !!error}
          {...others}
          helperText={getHelperText(error)}
          inputProps={{
            maxLength,
            minLength,
            max,
            min,
            ...(decimal && { inputMode: 'decimal' }),
            ...(positive && { min: 1 }),
            ...(allowJustNumbers && { inputMode: 'numeric' }),
            ...others.inputProps,
          }}
        />
      )}
      name={name}
      rules={{
        ...rules,
        validate: {
          ...(sanitize && {
            sanitize: input =>
              sanitizeInput(
                input,
                t('backoffice_only:form_text_field.invalid_characters'),
              ),
          }),
          ...(required && {
            required: validateRequiredStringRule,
          }),
          ...(maxLength !== undefined && {
            maxLength: validateMaxStringRule(maxLength, maxLengthError),
          }),
          ...(minLength !== undefined && {
            minLength: validateMinStringRule(minLength, minLengthError),
          }),
          ...(decimal && {
            decimal: validateDecimalRule(),
          }),
          ...(min !== undefined && {
            min: validateMinRule(min, minError),
          }),
          ...(max !== undefined && {
            max: validateMaxRule(max, maxError),
          }),
          ...(positive && {
            positive: validatePositiveRule(),
          }),
          // In some Controller we pass validate as a fn and in others as an object
          ...(typeof rules?.validate === 'function'
            ? { custom: rules?.validate }
            : rules?.validate),
        },
      }}
      control={control}
      defaultValue={defaultValue}
    />
  );
}

export default FormTextField;
