import React, {
  ChangeEvent,
  FocusEvent,
  MouseEvent,
  ReactElement,
  Ref,
  forwardRef,
  InputHTMLAttributes,
} from 'react';
import css from '../../utils/css';

import StyledInput, { InputWrapper } from './StyledInput';
import Affix from './Affix';
import { getThemeState } from './utils';
import { IconName } from '../Icon';

import { CommonProps } from '../common';

export interface InputProps
  extends Omit<CommonProps, 'id'>,
    Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'> {
  /**
   * Specify the [automated assistance](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) in filling out form field values by the browser.
   */
  autoComplete?: string;
  /**
   * Specify the input element should automatically get focus when it is mounted.
   */
  autoFocus?: boolean;
  /**
   * Whether the input is disabled.
   */
  disabled?: boolean;
  /**
   * Id of <input> element.
   */
  id?: string;
  /**
   * Whether the input is invalid
   */
  invalid?: boolean;
  /**
   * Name of <input> element, is used to refer to the form data for submission.
   */
  name?: string;
  /**
   * Blur event handler.
   */
  onBlur?: (e: FocusEvent<HTMLInputElement>) => void;
  /**
   * Change event handler. Use `event.target.value` for new value.
   */
  onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
  /**
   * Click event handler.
   */
  onClick?: (e: MouseEvent<HTMLInputElement>) => void;
  /**
   * Focus event handler.
   */
  onFocus?: (e: FocusEvent<HTMLInputElement>) => void;
  /**
   * Placeholder text in the absence of any value.
   */
  placeholder?: string;
  /**
   * Name of Icon or an Icon element to render on the left side of the input, before the user's cursor.
   */
  prefix?: IconName | ReactElement;
  /**
   * Whether or not Input's value is read only.
   */
  readonly?: boolean;
  /**
   * Input ref passing to input element
   */
  ref?: Ref<HTMLInputElement>;
  /**
   * The size of the input box.
   */
  size?: 'small' | 'medium' | 'large';
  /**
   * Name of Icon or an Icon element to render on the right side of the input.
   */
  suffix?: IconName | ReactElement;
  /**
   * [Type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types) of input.
   */
  type?: string;
  /**
   * The input's content value.
   */
  value?: string;
}

export const INPUT_DISPLAY_NAME = 'Input';

const Input = forwardRef<HTMLInputElement, InputProps>(
  (
    {
      autoFocus,
      type,
      autoComplete,
      disabled,
      invalid,
      name,
      onBlur,
      onChange,
      onFocus,
      onClick,
      placeholder,
      prefix,
      readonly,
      size = 'medium',
      suffix,
      value,
      id,
      className,
      'data-test-id': dataTestId,
      style,
      sx = {},
      ...inputAttrs
    }: InputProps,
    forwardedRef
  ): ReactElement => {
    return (
      <InputWrapper
        themeSize={size}
        themeState={getThemeState({ disabled, invalid })}
        className={className}
        style={{ ...style, ...css(sx) }}
        data-test-id={dataTestId}
      >
        <Affix affix={prefix} themeSize={size} />
        <StyledInput
          // XXX: Intentionally having spreading props to accept a11y props from Select component
          {...inputAttrs}
          type={type}
          autoFocus={autoFocus}
          autoComplete={autoComplete}
          value={value}
          onChange={onChange}
          onBlur={onBlur}
          onFocus={onFocus}
          onClick={onClick}
          disabled={disabled}
          placeholder={placeholder}
          readOnly={readonly}
          ref={forwardedRef}
          name={name}
          id={id}
          themePaddingLeft={prefix === undefined ? size : 'none'}
          themePaddingRight={suffix === undefined ? size : 'none'}
        />
        <Affix affix={suffix} themeSize={size} themeMarginLeft="auto" />
      </InputWrapper>
    );
  }
);

Input.displayName = INPUT_DISPLAY_NAME;

export default Input;
