import React, { memo, useMemo, useRef, useEffect, useCallback } from 'react';
import {
  Input as ChakraInput,
  FormControl,
  FormLabel,
  FormErrorMessage,
  FormHelperText,
  InputGroup,
  InputLeftElement,
  InputRightElement
} from '@chakra-ui/react';


export interface InputProps {
  /**
   * Label for the input
   */
  label?: string;
  /**
   * Error message to display
   */
  error?: string;
  /**
   * Helper text to display below the input
   */
  helperText?: string;
  /**
   * Size of the input
   */
  size?: 'sm' | 'md' | 'lg';
  /**
   * Whether the input is required
   */
  isRequired?: boolean;
  /**
   * ID for the input
   */
  id?: string;
  /**
   * Name for the input
   */
  name?: string;
  /**
   * Value of the input
   */
  value?: string;
  /**
   * Placeholder text
   */
  placeholder?: string;
  /**
   * Change handler for the input
   */
  onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
  /**
   * Type of the input
   */
  type?: string;
  /**
   * Whether the input is disabled
   */
  isDisabled?: boolean;
  /**
   * Whether the input is read-only
   */
  isReadOnly?: boolean;
  /**
   * Autocomplete attribute
   */
  autoComplete?: string;
  /**
   * Pattern for input validation
   */
  pattern?: string;
  /**
   * Minimum value for number inputs
   */
  min?: string | number;
  /**
   * Maximum value for number inputs
   */
  max?: string | number;
  /**
   * Step value for number inputs
   */
  step?: string | number;
  /**
   * Maximum length for text inputs
   */
  maxLength?: number;
  /**
   * Minimum length for text inputs
   */
  minLength?: number;
  /**
   * ARIA label
   */
  'aria-label'?: string;
  /**
   * ARIA described by
   */
  'aria-describedby'?: string;
  /**
   * ARIA invalid
   */
  'aria-invalid'?: boolean | 'true' | 'false';
  /**
   * Element to display on the left side of the input
   */
  leftElement?: React.ReactNode;
  /**
   * Element to display on the right side of the input
   */
  rightElement?: React.ReactNode;
  /**
   * Whether to focus the input on mount
   */
  autoFocus?: boolean;
  /**
   * Callback when input is focused
   */
  onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
  /**
   * Callback when input loses focus
   */
  onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
  /**
   * Data test ID for testing
   */
  'data-testid'?: string;
}

/**
 * Input component using Chakra UI's Input with BFUI theme customizations
 * Includes enhanced accessibility and performance optimizations
 */
const InputComponent: React.FC<InputProps> = ({
  label,
  error,
  helperText,
  size = 'md',
  isRequired = false,
  id,
  name,
  value,
  placeholder,
  onChange,
  type = 'text',
  isDisabled = false,
  isReadOnly = false,
  autoComplete,
  pattern,
  min,
  max,
  step,
  maxLength,
  minLength,
  'aria-label': ariaLabel,
  'aria-describedby': ariaDescribedBy,
  'aria-invalid': ariaInvalid,
  leftElement,
  rightElement,
  autoFocus = false,
  onFocus,
  onBlur,
  'data-testid': dataTestId,
  ...rest
}) => {
  const inputRef = useRef<HTMLInputElement>(null);
  
  // Generate IDs for accessibility
  const inputId = useMemo(() => id || label?.toLowerCase().replace(/\s+/g, '-'), [id, label]);
  const errorId = useMemo(() => error ? `${inputId}-error` : undefined, [inputId, error]);
  const helperId = useMemo(() => helperText ? `${inputId}-helper` : undefined, [inputId, helperText]);
  
  // Compute aria-describedby
  const computedAriaDescribedBy = useMemo(() => {
    const parts = [];
    if (ariaDescribedBy) parts.push(ariaDescribedBy);
    if (errorId) parts.push(errorId);
    if (helperId) parts.push(helperId);
    return parts.length > 0 ? parts.join(' ') : undefined;
  }, [ariaDescribedBy, errorId, helperId]);
  
  // Auto-focus handling
  useEffect(() => {
    if (autoFocus && inputRef.current) {
      inputRef.current.focus();
    }
  }, [autoFocus]);
  
  // Memoize change handler
  const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
    if (!isDisabled && !isReadOnly && onChange) {
      onChange(event);
    }
  }, [isDisabled, isReadOnly, onChange]);

  // Render input with or without input group based on elements
  const inputElement = useMemo(() => {
    const inputProps = {
      ref: inputRef,
      id: inputId,
      name: name || inputId,
      size,
      value,
      placeholder,
      onChange: handleChange,
      type,
      isDisabled,
      isReadOnly,
      autoComplete,
      pattern,
      min,
      max,
      step,
      maxLength,
      minLength,
      onFocus,
      onBlur,
      'aria-label': ariaLabel || label,
      'aria-describedby': computedAriaDescribedBy,
      'aria-invalid': ariaInvalid ?? (error ? 'true' : 'false'),
      'aria-required': isRequired,
      'data-testid': dataTestId,
      ...rest
    };
    
    if (leftElement || rightElement) {
      return (
        <InputGroup size={size}>
          {leftElement && <InputLeftElement>{leftElement}</InputLeftElement>}
          <ChakraInput {...inputProps} />
          {rightElement && <InputRightElement>{rightElement}</InputRightElement>}
        </InputGroup>
      );
    }
    
    return <ChakraInput {...inputProps} />;
  }, [inputId, name, size, value, placeholder, handleChange, type, isDisabled, isReadOnly, autoComplete, pattern, min, max, step, maxLength, minLength, onFocus, onBlur, ariaLabel, label, computedAriaDescribedBy, ariaInvalid, error, isRequired, dataTestId, leftElement, rightElement, rest]);

  return (
    <FormControl isInvalid={!!error} isRequired={isRequired}>
      {label && (
        <FormLabel htmlFor={inputId} id={`${inputId}-label`}>
          {label}
        </FormLabel>
      )}
      {inputElement}
      {error ? (
        <FormErrorMessage id={errorId} role="alert">
          {error}
        </FormErrorMessage>
      ) : helperText ? (
        <FormHelperText id={helperId}>
          {helperText}
        </FormHelperText>
      ) : null}
    </FormControl>
  );
};

// Memoize the component for better performance
export const Input = memo(InputComponent);

Input.displayName = 'Input';

export default Input;
