import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Autosuggest from 'react-autosuggest';
import { Manager, Popper, Reference } from 'react-popper';
import classNames from 'classnames';
import Portal from 'Components/Portal';
import styles from './AutoSuggestInput.css';

class AutoSuggestInput extends Component {

  //
  // Lifecycle

  constructor(props, context) {
    super(props, context);

    this._scheduleUpdate = null;
  }

  componentDidUpdate(prevProps) {
    if (
      this._scheduleUpdate &&
      prevProps.suggestions !== this.props.suggestions
    ) {
      this._scheduleUpdate();
    }
  }

  //
  // Control

  renderInputComponent = (inputProps) => {
    const { renderInputComponent } = this.props;

    return (
      <Reference>
        {({ ref }) => {
          if (renderInputComponent) {
            return renderInputComponent(inputProps, ref);
          }

          return (
            <div ref={ref}>
              <input
                {...inputProps}
              />
            </div>
          );
        }}
      </Reference>
    );
  }

  renderSuggestionsContainer = ({ containerProps, children }) => {
    return (
      <Portal>
        <Popper
          placement='bottom-start'
          modifiers={{
            computeMaxHeight: {
              order: 851,
              enabled: true,
              fn: this.onComputeMaxHeight
            },
            flip: {
              padding: this.props.minHeight
            }
          }}
        >
          {({ ref: popperRef, style, scheduleUpdate }) => {
            this._scheduleUpdate = scheduleUpdate;

            return (
              <div
                ref={popperRef}
                style={style}
                className={children ? styles.suggestionsContainerOpen : undefined}
              >
                <div
                  {...containerProps}
                  style={{
                    maxHeight: style.maxHeight
                  }}
                >
                  {children}
                </div>
              </div>
            );
          }}
        </Popper>
      </Portal>
    );
  }

  //
  // Listeners

  onComputeMaxHeight = (data) => {
    const {
      top,
      bottom,
      width
    } = data.offsets.reference;

    const windowHeight = window.innerHeight;

    if ((/^botton/).test(data.placement)) {
      data.styles.maxHeight = windowHeight - bottom;
    } else {
      data.styles.maxHeight = top;
    }

    data.styles.width = width;

    return data;
  }

  onInputChange = (event, { newValue }) => {
    this.props.onChange({
      name: this.props.name,
      value: newValue
    });
  }

  onInputKeyDown = (event) => {
    const {
      name,
      value,
      suggestions,
      onChange
    } = this.props;

    if (
      event.key === 'Tab' &&
      suggestions.length &&
      suggestions[0] !== this.props.value
    ) {
      event.preventDefault();

      if (value) {
        onChange({
          name,
          value: suggestions[0]
        });
      }
    }
  }

  //
  // Render

  render() {
    const {
      forwardedRef,
      className,
      inputContainerClassName,
      name,
      value,
      placeholder,
      suggestions,
      hasError,
      hasWarning,
      getSuggestionValue,
      renderSuggestion,
      onInputChange,
      onInputKeyDown,
      onInputFocus,
      onInputBlur,
      onSuggestionsFetchRequested,
      onSuggestionsClearRequested,
      onSuggestionSelected,
      ...otherProps
    } = this.props;

    const inputProps = {
      className: classNames(
        className,
        hasError && styles.hasError,
        hasWarning && styles.hasWarning
      ),
      name,
      value,
      placeholder,
      autoComplete: 'off',
      spellCheck: false,
      onChange: onInputChange || this.onInputChange,
      onKeyDown: onInputKeyDown || this.onInputKeyDown,
      onFocus: onInputFocus,
      onBlur: onInputBlur
    };

    const theme = {
      container: inputContainerClassName,
      containerOpen: styles.suggestionsContainerOpen,
      suggestionsContainer: styles.suggestionsContainer,
      suggestionsList: styles.suggestionsList,
      suggestion: styles.suggestion,
      suggestionHighlighted: styles.suggestionHighlighted
    };

    return (
      <Manager>
        <Autosuggest
          {...otherProps}
          ref={forwardedRef}
          id={name}
          inputProps={inputProps}
          theme={theme}
          suggestions={suggestions}
          getSuggestionValue={getSuggestionValue}
          renderInputComponent={this.renderInputComponent}
          renderSuggestionsContainer={this.renderSuggestionsContainer}
          renderSuggestion={renderSuggestion}
          onSuggestionSelected={onSuggestionSelected}
          onSuggestionsFetchRequested={onSuggestionsFetchRequested}
          onSuggestionsClearRequested={onSuggestionsClearRequested}
        />
      </Manager>
    );
  }
}

AutoSuggestInput.propTypes = {
  forwardedRef: PropTypes.func,
  className: PropTypes.string.isRequired,
  inputContainerClassName: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
  placeholder: PropTypes.string,
  suggestions: PropTypes.array.isRequired,
  hasError: PropTypes.bool,
  hasWarning: PropTypes.bool,
  enforceMaxHeight: PropTypes.bool.isRequired,
  minHeight: PropTypes.number.isRequired,
  maxHeight: PropTypes.number.isRequired,
  getSuggestionValue: PropTypes.func.isRequired,
  renderInputComponent: PropTypes.elementType,
  renderSuggestion: PropTypes.func.isRequired,
  onInputChange: PropTypes.func,
  onInputKeyDown: PropTypes.func,
  onInputFocus: PropTypes.func,
  onInputBlur: PropTypes.func.isRequired,
  onSuggestionsFetchRequested: PropTypes.func.isRequired,
  onSuggestionsClearRequested: PropTypes.func.isRequired,
  onSuggestionSelected: PropTypes.func,
  onChange: PropTypes.func.isRequired
};

AutoSuggestInput.defaultProps = {
  className: styles.input,
  inputContainerClassName: styles.inputContainer,
  enforceMaxHeight: true,
  minHeight: 50,
  maxHeight: 200
};

export default AutoSuggestInput;
