import clsx from "clsx";
import {
  ChangeEvent,
  FC,
  FocusEvent,
  HTMLAttributes,
  HTMLInputTypeAttribute,
  useEffect,
  useState,
} from "react";
import { v4 as uuidv4 } from "uuid";

import styles from "./styles.module.css";

interface InputProps extends HTMLAttributes<HTMLInputElement> {
  min?: number;
  max?: number;
  autoComplete?: string;
}

export interface InputFieldProps extends HTMLAttributes<HTMLInputElement> {
  errorMessage?: string;
  initialValue?: string | number | null;
  label: string;
  maxLength?: number;
  placeholder?: string;
  type?: HTMLInputTypeAttribute;
  inputProps?: InputProps;
}

export const InputField: FC<InputFieldProps> = ({
  className,
  errorMessage,
  initialValue = "",
  inputProps,
  label,
  maxLength,
  onBlur,
  onChange,
  onFocus,
  placeholder,
  type = "text",
  ...props
}) => {
  // Set default error message if not provided
  errorMessage ||= `The maximum length is ${maxLength} characters.`;
  // Set up component state and a shared id
  const [value, setValue] = useState(initialValue);
  const [showError, setShowError] = useState(false);
  const id = props.id ?? `input-field-${uuidv4()}`;

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    const valueLength = e.target.value.length;
    if (maxLength) {
      // If the value length exceeds maxLength, show error
      if (valueLength > maxLength) {
        setShowError(true);
        //return; Prevent further processing if maxLength exceeded
        return;
      }

      // If the value length is equal to maxLength, show error
      if (valueLength === maxLength) {
        setShowError(true);
      }
      // If the value length is less than maxLength, hide error
      if (valueLength < maxLength) {
        setShowError(false);
      }
    }
    // Update component state
    setValue(e.target.value);
    // Call the provided onChange handler if it exists
    if (onChange) onChange(e);
    return;
  };

  const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
    const intValue = parseInt(e.target.value);

    // If entered value is below the minimum, reset to minimum
    if (
      !isNaN(intValue) &&
      inputProps?.min !== undefined &&
      intValue < inputProps.min
    ) {
      setValue(inputProps.min);
    }

    // If entered value is above the maximum and no initialValue exists, reset to maximum
    if (
      !isNaN(intValue) &&
      inputProps?.max !== undefined &&
      intValue > inputProps.max
    ) {
      setValue(inputProps.max);
    }

    // Call the provided onBlur handler if it exists
    if (onBlur) onBlur(e);

    // Reset error state on blur
    setShowError(false);
  };

  const handleFocus = (e: FocusEvent<HTMLInputElement>) => {
    // Call the provided onFocus handler if it exists
    if (onFocus) onFocus(e);
    // Check for maxLength error
    const valueLength = e.target.value.length;
    if (maxLength && valueLength > maxLength - 1) setShowError(true);
  };

  useEffect(() => {
    // Update the value when initialValue changes
    setValue(initialValue);
    // Reset error state when initial value changes
    setShowError(false);
  }, [initialValue]);

  const inputAttrs = { id, type, placeholder };

  return (
    <div className={clsx([styles.inputField, className])} {...props}>
      <label className={styles.label} htmlFor={id}>
        {label}
      </label>
      <input
        className={styles.input}
        value={value ?? ""}
        onBlur={handleBlur}
        onChange={handleChange}
        onFocus={handleFocus}
        {...inputAttrs}
        {...inputProps}
      />
      {showError && <span className={styles.error}>{errorMessage}</span>}
    </div>
  );
};
