import { Input } from '@/components/ui/input';
import { FormElement } from '@/types/form-types';
import { ChangeEvent, forwardRef } from 'react';

type ValidationClass =
  | 'allow_numeric'
  | 'allow_decimal'
  | 'allow_alphabets'
  | 'allow_alpha_numeric'
  | 'allow_alpha_numeric_space'
  | 'allow_alpha_special_symbols'
  | 'allow_alpha_numeric_special_symbols'
  | 'allow_numeric_special_symbols'
  | 'allow_decimal_special_symbols'
  | 'allow_alpha_decimal_special'
  | 'valid_date'
  | 'valid_time'
  | 'valid_datetime'
  | 'valid_mm_yyyy'
  | 'valid_email'
  | 'valid_future_date'
  | 'valid_past_date'
  | 'valid_present_date'
  | 'valid_phone_number'
  | 'check_max_length'
  | 'check_min_length'
  | 'allow_controller_name'
  | 'allow_controller_title';

interface FormInputProps {
  maxLength?: number;
  minLength?: number;
  element: FormElement;
  field: {
    value: string;
    onChange: (event: ChangeEvent<HTMLInputElement>) => void;
    onBlur: () => void;
    name: string;
    ref: (instance: HTMLInputElement | null) => void;
  };
  className?: string;
  defaultValue?: unknown;
}

const baseInputStyles =
  'rounded-full border-gray-600 shadow-none focus-within:shadow-none hover:shadow-none focus:shadow-none';

const validationPatterns = {
  allow_numeric: /^[0-9]*$/,
  allow_decimal: /^\d*\.?\d*$/,
  allow_alphabets: /^[a-zA-Z]*$/,
  allow_alpha_numeric: /^[a-zA-Z0-9]*$/,
  allow_alpha_numeric_space: /^[a-zA-Z0-9 ]+(?:\s[a-zA-Z0-9]+)*$/,
  allow_alpha_special_symbols: /^[a-zA-Z!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]*$/,
  allow_alpha_numeric_special_symbols:
    /^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]*$/,
  allow_numeric_special_symbols: /^[0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]*$/,
  allow_decimal_special_symbols:
    /^[\d!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?\\.]*$/,
  allow_alpha_decimal_special:
    /^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?\\.]*$/,

  valid_date: /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/,
  valid_time: /^([01]\d|2[0-3]):([0-5]\d)$/,
  valid_datetime:
    /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):([0-5]\d)$/,
  valid_mm_yyyy: /^(0[1-9]|1[0-2])\/\d{4}$/,

  valid_email: /^[^@\s]*@?[^@\s]*\.?[^@\s]*$/,

  valid_phone_number: /^\+?[1-9]\d{1,14}$/,
  allow_controller_name: /^[a-zA-Z0-9_]*$/,
  allow_controller_title: /^[a-zA-Z0-9\- ]+$/,
  allow_custom: /^[a-zA-Z0-9\-_.(), ': ]*$/,
};

const customValidations = {
  valid_future_date: (value: string) => {
    const date = new Date(value);
    return date > new Date();
  },
  valid_past_date: (value: string) => {
    const date = new Date(value);
    return date < new Date();
  },
  valid_present_date: (value: string) => {
    const date = new Date(value);
    const today = new Date();
    return date.toDateString() === today.toDateString();
  },
  check_max_length: (value: string, maxLength?: number) => {
    return maxLength ? value.length <= maxLength : true;
  },
  check_min_length: (value: string, minLength?: number) => {
    return minLength ? value.length >= minLength : true;
  },
};

export const FormInput = forwardRef<HTMLInputElement, FormInputProps>(
  ({ element, field, className = '', maxLength, minLength }, ref) => {
    const sanitizationClasses = element?.attributes?.classes
      ? `${baseInputStyles} ${element.attributes.classes} ${className}`.trim()
      : `${baseInputStyles} ${className}`.trim();
    const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
      let value = event.target.value;
      const hasSpaceValidation =
        sanitizationClasses.includes('allow_alpha_numeric_space') ||
        sanitizationClasses.includes('allow_controller_title') ||
        sanitizationClasses.includes('allow_custom');

      if (hasSpaceValidation) {
        if (value.includes('  ')) {
          value = value.replace(/\s+/g, ' ');
          event.target.value = value;
          return;
        }
        if (value.startsWith(' ')) {
          return;
        }
      } else {
        value = value.replace(/\s+/g, ' ').trim();
        event.target.value = value;
      }

      const classes = sanitizationClasses.split(' ');

      const validationClasses = classes.filter((cls) =>
        [
          ...Object.keys(validationPatterns),
          ...Object.keys(customValidations),
        ].includes(cls),
      ) as ValidationClass[];

      if (validationClasses.length > 0 && value) {
        for (const validationClass of validationClasses) {
          const pattern =
            validationPatterns[
              validationClass as keyof typeof validationPatterns
            ];

          if (pattern) {
            if (validationClass === 'valid_email') {
              if (pattern.test(value)) {
                continue;
              }
              return;
            }

            if (!pattern.test(value)) {
              return;
            }
          }

          const customValidation =
            customValidations[
              validationClass as keyof typeof customValidations
            ];
          if (typeof customValidation === 'function') {
            const validationValue =
              validationClass === 'check_max_length' ? maxLength : minLength;
            if (!customValidation(value, validationValue)) {
              return;
            }
          }
        }
      }

      field.onChange(event);
    };

    return (
      <Input
        {...field}
        type={element.type || 'text'}
        className={`${sanitizationClasses}`}
        placeholder={element.title || 'Enter value...'}
        value={field.value ?? ''}
        onChange={handleInputChange}
        onBlur={field.onBlur}
        name={field.name}
        aria-label={element.title}
        ref={(instance) => {
          if (typeof ref === 'function') ref(instance);
          field.ref(instance);
        }}
        autoComplete="off"
        data-validation={className
          .split(' ')
          .find((cls) => Object.keys(validationPatterns).includes(cls))}
      />
    );
  },
);
FormInput.displayName = 'FormInput';
