/* eslint-disable jsx-a11y/no-autofocus */
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import { clsx } from 'clsx';
import { Component, createRef, ReactNode } from 'react';

import { Input } from '../../inputs/Input';
import { TypeaheadOption, TypeaheadProps } from '../Typeahead';

const DEFAULT_INPUT_MIN_WIDTH = 10;

export type TypeaheadInputProps<T> = {
  typeaheadId: string;
  value: string;
  selected: readonly TypeaheadOption<T>[];
  dropdownOpen?: boolean;
  autoComplete: string;
  onChange: React.ChangeEventHandler<HTMLInputElement>;
  onKeyDown: React.KeyboardEventHandler<HTMLInputElement>;
  onFocus: () => void;
  onPaste: React.ClipboardEventHandler<HTMLInputElement>;
  renderChip: (chip: TypeaheadOption<T>, index: number) => ReactNode;
  ariaActivedescendant?: string;
} & Pick<TypeaheadProps<T>, 'id' | 'name' | 'autoFocus' | 'multiple' | 'placeholder' | 'maxHeight'>;

type TypeaheadInputState = {
  inputWidth: number;
  keyboardFocusedOptionIndex: number | null;
};

export default class TypeaheadInput<T> extends Component<
  TypeaheadInputProps<T>,
  TypeaheadInputState
> {
  inputRef = createRef<HTMLInputElement>();
  sizerRef = createRef<HTMLDivElement>();

  constructor(props: TypeaheadInputProps<T>) {
    super(props);
    this.state = {
      inputWidth: DEFAULT_INPUT_MIN_WIDTH,
      keyboardFocusedOptionIndex: null,
    };
  }

  componentDidMount() {
    const { autoFocus } = this.props;
    if (autoFocus) {
      // Autofocus after context code executions (e.g open Modal) is completed
      setTimeout(() => {
        this.inputRef.current?.focus();
      });
    }
  }

  componentDidUpdate(previousProps: TypeaheadInputProps<T>) {
    if (previousProps.value !== this.props.value && this.props.multiple) {
      this.recalculateWidth();
    }
  }

  recalculateWidth = () => {
    requestAnimationFrame(() => {
      this.setState({
        inputWidth: Math.max(DEFAULT_INPUT_MIN_WIDTH, this.sizerRef.current?.scrollWidth ?? 0 + 10),
      });
    });
  };

  renderInput = () => {
    const {
      typeaheadId,
      autoFocus,
      multiple,
      name,
      dropdownOpen,
      placeholder,
      selected,
      value,
      onChange,
      onKeyDown,
      onFocus,
      onPaste,
      autoComplete,
      ariaActivedescendant, // Declare the ariaActivedescendant variable
    } = this.props;
    const { inputWidth } = this.state;

    const hasPlaceholder = !multiple || selected.length === 0;
    return (
      <Input
        ref={this.inputRef}
        className={clsx(multiple && 'typeahead__input')}
        name={name}
        id={`input-${typeaheadId}`}
        aria-activedescendant={
          ariaActivedescendant
            ? `menu-${typeaheadId} ${ariaActivedescendant}`
            : `menu-${typeaheadId}`
        }
        autoFocus={autoFocus}
        placeholder={hasPlaceholder ? placeholder : ''}
        aria-autocomplete="list"
        aria-expanded={dropdownOpen}
        aria-haspopup="listbox"
        aria-controls={dropdownOpen ? `menu-${typeaheadId}` : undefined}
        autoComplete={autoComplete}
        role="combobox"
        value={value}
        style={multiple && selected.length > 0 ? { width: inputWidth } : {}}
        onChange={onChange}
        onKeyDown={onKeyDown}
        onClick={onFocus}
        onFocus={onFocus}
        onPaste={onPaste}
      />
    );
  };

  render() {
    const { multiple, selected, value, maxHeight, renderChip } = this.props;

    return multiple ? (
      <div
        className="form-control typeahead__input-container"
        style={{ maxHeight }}
        onClick={() => {
          this.inputRef.current?.focus();
        }}
      >
        <div className="typeahead__input-wrapper">
          {selected && selected.map((chip, idx) => renderChip(chip, idx))}

          {this.renderInput()}
          <div className="typeahead__input-aligner" />
        </div>
        <div ref={this.sizerRef} className="sizer form-control typeahead__input">
          {value}
        </div>
      </div>
    ) : (
      this.renderInput()
    );
  }
}
