import React from 'react';
import PropTypes from 'prop-types';
import ReactAutosuggest from 'react-autosuggest';
import Textfield from './Textfield';
import autosuggestStyles from './Autosuggest.styl';
import newId from '../utils/newId';
import Spaced from './Spaced';

const always = () => true;

export default class Autosuggest extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      typedText: this.textForValue(props.value),
      suggestions: []
    };

    this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind(this);
    this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind(this);
    this.getSuggestionValue = this.getSuggestionValue.bind(this);
    this.onSuggestionSelected = this.onSuggestionSelected.bind(this);
    this.renderInputComponent = this.renderInputComponent.bind(this);
    this.onChange = this.onChange.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.textForValue = this.textForValue.bind(this);
    this.defaultRenderOption = this.defaultRenderOption.bind(this);
  }


  componentWillMount() {
    this.id = newId();
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      typedText: (this.props.allowManualInput) ? nextProps.value : this.textForValue(nextProps.value)
    });
  }

  onSuggestionsFetchRequested({ value: typedText }) {
    const { options, filter } = this.props;
    if (!this.textMatchesCurrentValue()) {
      const suggestions = options.filter((suggestion) => filter(suggestion, typedText));
      this.setState({ suggestions });
    } else {
      this.setState({ suggestions: options });
    }
  }

  onSuggestionsClearRequested() {
    this.setState({ suggestions: [] });
  }

  onChange(event, { newValue: typedText }) {
    this.setState({ typedText });
    if (this.props.onChange && this.props.allowManualInput) this.props.onChange(typedText);
  }

  onBlur() {
    // If the user just entered some text without selecting an actual option, clear the input.
    if (!this.textMatchesCurrentValue() && !this.props.allowManualInput) {
      this.setState({ typedText: '' });
      if (this.props.onChange) this.props.onChange('');
    }
    if (this.props.onBlur) this.props.onBlur();
  }

  onSuggestionSelected(event, { suggestion }) {
    const value = this.props.getOptionValue(suggestion);
    if (this.props.onChange) {
      this.props.onChange(value);
    }
  }

  getSuggestionValue(suggestion) {
    return this.props.getOptionText(suggestion);
  }

  textMatchesCurrentValue() {
    return this.state.typedText === this.textForValue(this.props.value);
  }

  textForValue(value) {
    const { options, getOptionValue, getOptionText } = this.props;
    const option = options.find((option_) => getOptionValue(option_) === value);
    return option ? getOptionText(option) : '';
  }

  defaultRenderOption(option) {
    const { getOptionText } = this.props;
    return (
      <div style={{ padding: 10 }}>
        {getOptionText(option)}
      </div>
    );
  }

  renderInputComponent(inputProps) {
    const { label, error, success, warning, info, placeholder, selectedElement, options, value, getOptionValue } = this.props;
    const selectedValue = value && options && options.find((option) => getOptionValue(option) === value);
    return (
      <Spaced>
        <Textfield
          preliminary={!this.textMatchesCurrentValue() && !this.props.allowManualInput}
          {...{ label, error, success, warning, info, placeholder }}
          {...inputProps}
        />
        { selectedElement && selectedValue && (
          selectedElement(selectedValue)
        )}
      </Spaced>
    );
  }

  render() {
    const { renderOption = this.defaultRenderOption } = this.props;
    const { typedText, suggestions } = this.state;
    const { onChange, onBlur } = this;
    // We need a unique id for the link between input and label, even if the user doesn't provide one.
    const id = this.props.id || this.id;

    return (
      <ReactAutosuggest
        id={id}
        focusFirstSuggestion
        shouldRenderSuggestions={always}
        suggestions={suggestions}
        onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
        onSuggestionsClearRequested={this.onSuggestionsClearRequested}
        getSuggestionValue={this.getSuggestionValue}
        renderSuggestion={renderOption}
        renderInputComponent={this.renderInputComponent}
        onSuggestionSelected={this.onSuggestionSelected}
        inputProps={{ onChange, onBlur, value: typedText, id, type: 'search' }}
        theme={autosuggestStyles}
      />
    );
  }

}

Autosuggest.propTypes = {
  options: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
  getOptionValue: PropTypes.func.isRequired,
  getOptionText: PropTypes.func.isRequired,
  renderOption: PropTypes.func,
  filter: PropTypes.func.isRequired,
  onChange: PropTypes.func,
  value: PropTypes.string
};

Autosuggest.defaultProps = {
  renderOption: undefined,
  onChange: undefined,
  value: undefined
};
