import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import debounce from 'lodash.debounce';
import KdAutocompleteOption from '../KdAutocompleteOption';
import { dispatchCustomEvent } from '../../utils';
import { NON_SELECTED, ARROW_UP, ARROW_DOWN, ENTER, ESCAPE } from '../../constants';

const defaultProps = {
  isChickletNone: false,
  hasInitialValues: false,
  autoComplete: {},
}

const propTypes = {
  kdFilterModel: PropTypes.object,

  autoComplete: PropTypes.shape({
    onSuccess: PropTypes.func,
    onRender: PropTypes.func,
    onSelect: PropTypes.func,
    onFail: PropTypes.func,
    onDone: PropTypes.func,
    placeholderText: PropTypes.string,
    clearTextboxOnSelection: PropTypes.bool,
  }),

  api: PropTypes.shape({
    url: PropTypes.string.isRequired,
    key: PropTypes.string.isRequired,
  }),

  setSelectedItems: PropTypes.func,
  isChickletNone: PropTypes.bool,
  hasInitialValues: PropTypes.bool,
  lastInitialValue: PropTypes.object,
  setKdSelectEntityLoading: PropTypes.func,
  handleRemove: PropTypes.func

}

class KdAutocomplete extends React.Component {

  constructor(props) {
    super(props);

    this.isOptionSelected = this.isOptionSelected.bind(this);
    this.handleOutsideClick = this.handleOutsideClick.bind(this);
    this.fetchResult = this.fetchResult.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleSelect = this.handleSelect.bind(this);
    this.handleRemove = this.handleRemove.bind(this);
    this.renderItem = this.renderItem.bind(this);
    this.renderOptions = this.renderOptions.bind(this);
    this.setActiveOption = this.setActiveOption.bind(this);
    this.removeActiveOption = this.removeActiveOption.bind(this);
    this.resetDisplayed = this.resetDisplayed.bind(this);
    this.resetActiveOption = this.resetActiveOption.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.renderInput = this.renderInput.bind(this);
    this.addItem = this.addItem.bind(this);

    this.state = { displayedItems: [],
      isLoading: false,
      activeOption: NON_SELECTED
    }
  }

  isOptionSelected(){
    return (this.state.activeOption !== NON_SELECTED);
  }

  initialInputValue(){
    if (this.props.isChickletNone && this.props.hasInitialValues) {
      return this.props.lastInitialValue.label;
    }
  }

  handleOutsideClick(e) {
    if (ReactDOM.findDOMNode(this).contains(e.target)) {
      return;
    } else {
      this.resetDisplayed();
    }
  }

  componentWillMount() {
    document.addEventListener('click', this.handleOutsideClick, false);
    this.delayedFetchResult = debounce(this.fetchResult, 800);
  }

  componentWillUnmount() {
    document.removeEventListener('click', this.handleOutsideClick, false);
  }

  fetchResult(event){
    const value = event.target.value || '';
    if (value.length) {
      const {
        autoComplete,
        setKdSelectEntityLoading,
        api,
        kdFilterModel
      } = this.props;
      const parseResult = (elem) => {
        let item = {
          value: elem.KdId,
          label: elem.KdLabel,
          ...elem
        };
        if (autoComplete.onSuccess) {
          item = autoComplete.onSuccess(item);
        }
        return item;
      };
      const onRequestDone = (data) => {
        this.setState({
          isLoading: false,
          displayedItems: data.KdResult.Result.map((elem) => (parseResult(elem)))
        });
        setKdSelectEntityLoading(false);
        dispatchCustomEvent(this.rootElement, 'kd:ui:SelectEntity:onDone', { data: this.state.displayedItems });
      };
      let request = kd.Request(kdFilterModel.getEntityType() , 'Search');
      request.setUrlBase(api.url);
      request.setApiKey(api.key);
      request.setAjaxDone(onRequestDone);
      request.setAjaxFail(autoComplete.onFail);
      request.setQ(value);
      request.requestData();
    } else {
      this.resetDisplayed();
    }
  }

  handleChange(event){
    event.persist();
    if (event.target.value.length){
      this.setState({ isLoading: true });
      this.props.setKdSelectEntityLoading(true);
      this.delayedFetchResult(event);
    } else {
      this.resetDisplayed();
    }
  }

  handleSelect(e){
    const value = e.currentTarget.getAttribute('data-value');
    this.addItem(value);
    this.resetActiveOption();

  }

  addItem(value) {
    const { autoComplete, setSelectedItems, isChickletNone } = this.props;
    const item = this.state.displayedItems.find((elem) => (elem.value === value));
    setSelectedItems && setSelectedItems(item);
    autoComplete.onSelect && autoComplete.onSelect({ data : {...item} });
    if (isChickletNone) {
      this.setInputValue(item.label);
    } else {
      autoComplete.clearTextboxOnSelection && this.setInputValue();
    }
    this.resetDisplayed();
  }

  handleRemove(elem){
    if (this.props.handleRemove) {
      this.props.handleRemove(elem);
    }
    this.setInputValue();
  }

  renderItem(item){
    const { autoComplete } = this.props;

    if (autoComplete.onRender) {
      return <span className="o-options__option-text" dangerouslySetInnerHTML={ {__html: autoComplete.onRender(item)} }></span>;
    } else {
      return <KdAutocompleteOption item={ item } />;
    }
  }

  renderInfoBar() {
    return (
      <li className="o-options__info-option">
        <div className="o-pack o-pack--middle">
          <div className="o-pack__item u-padding-small">
            <b>UPCOMING APPOINTMENTS</b>
          </div>
          <div className="o-pack__item u-padding-small">
            <b>&uarr; Up</b>
            <b>&darr; Down</b> to navigate
            <b className="u-margin-left-small">&crarr; Enter </b>
            <span> to view</span>
          </div>
        </div>
      </li>
    );
  }

  renderOptions(){
    const displayedItems = this.state.displayedItems.map(
      (item, index) => (
        <li ref={ref => this[`option_${index}`] = ref}
            className={
              classNames(
                'o-options__option',
                {'o-options__option-active': index === this.state.activeOption }
              )
            }
            key={ item.value }
            data-label={ item.label }
            data-value={ item.value }
            data-index={ index }
            onClick={this.handleSelect}
            onMouseEnter={this.setActiveOption}
            onMouseLeave={this.removeActiveOption}
        >
          {this.renderItem(item)}
        </li>
      )
    );
    if (displayedItems.length) {
      return (
        <ul className="o-options">
          {this.renderInfoBar()}
          { displayedItems }
        </ul>
      );
    }
  }

  setActiveOption(event){
    const index = event.currentTarget.getAttribute('data-index');
    this.setState({ activeOption: parseInt(index) });
  }

  removeActiveOption(){
    this.resetActiveOption();
  }

  setInputValue(value){
    if (this.kdAutocompleteInput != null ){
      this.kdAutocompleteInput.value = value || "";
    }
  }

  resetDisplayed() {
    this.setState({
      displayedItems: [],
      activeOption: NON_SELECTED,
      isLoading: false });
    this.props.setKdSelectEntityLoading(false);
  }

  resetActiveOption(){
    this.setState({ activeOption: NON_SELECTED });
  }

  handleKeyDown(e){
    const itemsLength = this.state.displayedItems.length;
    const { setSelectedItems, autoComplete, isChickletNone } = this.props;
    if (itemsLength){
      switch (e.keyCode){
        case ARROW_DOWN:
          const next = this.isOptionSelected() ? (this.state.activeOption + 1) % itemsLength : 0 ;
          this.setState({ activeOption: next });
          break;
        case ARROW_UP:
          const prev = this.isOptionSelected() ?  (this.state.activeOption - 1) % itemsLength : itemsLength - 1;
          this.setState({ activeOption: prev });
          break;
        case ENTER:
          const elem = this[`option_${this.state.activeOption}`];
          const value = elem.getAttribute('data-value');
          this.addItem(value);
          break;
        case ESCAPE:
          this.resetDisplayed();
          break;
        default:
          break;
      }
    }
  }

  renderInput(){
    const { autoComplete, isChickletNone, kdFilterModel } = this.props;
    const placeholder = autoComplete.placeholderText || `${kdFilterModel.getEntityType()} Search...`;
    let inputClass =
      classNames('o-kd-autocomplete-input', 'o-search-icon',
        {
          'o-search-icon-with-loading': this.state.isLoading,
          'u-input-chicklet-none': isChickletNone
        }
      );
    return (
      <input
        className={inputClass}
        onKeyDown={this.handleKeyDown}
        type="text"
        ref={ref => this.kdAutocompleteInput = ref}
        placeholder={ placeholder }
        onChange={this.handleChange}
        defaultValue={this.initialInputValue()}
      />
    );
  }

  render() {
    return (
      <div ref={ref => this.rootElement = ref} className="c-kd-autocomplete">
        {this.renderInput()}
        {this.renderOptions()}
      </div>
    );
  }
};

KdAutocomplete.defaultProps = defaultProps;

KdAutocomplete.propTypes = propTypes;

export default KdAutocomplete;