import classNames from 'classnames';
import flatten from 'lodash/flatten';
import isEqual from 'lodash/isEqual';
import uniqWith from 'lodash/uniqWith';
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';

import wrap from '../ItemContainerWrapper';
import MainButton from './MainButton';
import OptionsList from './OptionsList';
import ClearAll from '../ClearAll';
import { itemHandlerCreator } from '../../utils';
import { ADD_CHICKLET, NON_SELECTED, ARROW_UP, ARROW_DOWN, ENTER, ESCAPE } from '../../constants';

const defaultProps = {
  mainSelector: {
    visible: false,
    placeholderText: 'Select option...',
    placeholderHandler:
      (selecteds, placeholderText) => `${selecteds.length} ${placeholderText}(s) selected`,
  },
  chicklets: { container: { visible: false } },
  selectBehavior: ADD_CHICKLET,
  events: {},
};

const propTypes = {
  theme: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.func,
  ]).isRequired,
  mainSelector: PropTypes.shape({
    placeholderText: PropTypes.string,
    placeholderHandler: PropTypes.func,
    optionGroups: PropTypes.arrayOf(
      PropTypes.shape({
        label: PropTypes.string.isRequired,
        options: PropTypes.arrayOf(
          PropTypes.shape({
            values: PropTypes.arrayOf(
              PropTypes.oneOfType([
                PropTypes.object,
                PropTypes.string,
              ]),
            ).isRequired,
            selected: PropTypes.bool,
          }),
        ),
      }),
    ),
  }).isRequired,
  kdFilterModel: PropTypes.object.isRequired,
  // private props
  wrapperState: PropTypes.object,
  onClearChicklets: PropTypes.func.isRequired,
  onSetChicklets: PropTypes.func.isRequired,
  selectedValuesInitial: PropTypes.array,
  isChickletNone: PropTypes.bool,
  emptyItems: PropTypes.bool,
};

class KdSelectStringMultiple extends React.Component {
  constructor(props) {
    super(props);
    const { Operator, DataType } = props.kdFilterModel.getFilterItemValue();
    this.kdFilterOptions = {
      model: props.kdFilterModel,
      operator: Operator,
      itemHandler: itemHandlerCreator(DataType),
    };
    this.allOptions = flatten(props.mainSelector.optionGroups.map(group => group.options));
    this.focusedOption = this.allOptions[0];
    this.state = {
      optionsListVisible: false,
      mainButtonLabel: props.mainSelector.placeHolderText,
      activeValue: this.allOptions[0],
      activeOption: 0,
    };
  }

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

  componentDidMount() {
    const { selectedValuesInitial } = this.props;
    if (selectedValuesInitial.length) {
      this.setSelectedValues(this.props.selectedValuesInitial);
    } else {
      const selected = this.getDefaultSelected();
      if (selected && selected.values.length) {
        this.changeOption(selected, true, true);
      }
    }
  }

  componentWillMount() {
    document.addEventListener('click', this.handleOutsideClick, false);
  }

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

  getDefaultSelected = () => {
    const selected = this.allOptions.filter(o => o.selected)[0];
    return selected;
  }


  // componentWillReceiveProps(nextProps) {
  //   const { selectedValuesInitial, onClearChicklets } = this.props;
  //   if (selectedValuesInitial && !isEqual(selectedValuesInitial, nextProps.selectedValuesInitial)) {
  //     const { Operator, DataType } = nextProps.kdFilterModel.getFilterItemValue();
  //     this.kdFilterOptions = {
  //       model: nextProps.kdFilterModel,
  //       operator: Operator,
  //       itemHandler: itemHandlerCreator(DataType),
  //     };
  //     onClearChicklets();
  //     this.setSelectedValues(nextProps.selectedValuesInitial);
  //   }
  // }

  setSelectedValues = (values) => {
    const options = this.allOptions
      .filter(option => values.some(o => isEqual(o.values, option.values)));

    this.changeGroup({ options }, true);
  }

  changeOption = (option, checked, silent = false) => {
    const { itemsState } = this.props.wrapperState;
    const stateOption = this.buildItem(option);
    let newSelecteds;
    if (checked) {
      newSelecteds = uniqWith([...itemsState, stateOption], (a, b) => isEqual(a.values, b.values));
    } else {
      newSelecteds =
        itemsState.filter(selectedOption => !isEqual(selectedOption.values, stateOption.values));
    }
    this.addItems(newSelecteds, silent);
  }

  addItems = (selecteds, silent = false) => {
    const { onSetChicklets, onClearChicklets, events: { onChange } } = this.props;
    if (selecteds && selecteds.length) {
      const { model } = this.kdFilterOptions;
      const callback = () => {
        onChange && onChange({ data: { model, ...model.getFilterItemValue() } });
      };
      onSetChicklets({ chicklets: selecteds, model, callback: !silent && callback });
    } else {
      onClearChicklets(true);
    }
  }

  changeGroup = (group, checked) => {
    const { itemsState } = this.props.wrapperState;
    let newSelecteds;
    if (checked) {
      newSelecteds = [...itemsState, ...group.options.map(this.buildItem)];
    } else {
      newSelecteds = itemsState
        .filter(selected => !group.options.find(o => isEqual(o.values, selected.values)));
    }
    this.addItems(uniqWith(newSelecteds, (x, y) => isEqual(x.values, y.values)));
  }

  buildItem = ({ values, label }) => {
    return ({
      values,
      label,
      key: values.join(),
      chickletLabel: label,
      kdFilterOptions: this.kdFilterOptions,
    });
  }

  checkAll = () => {
    this.addItems(
      this.allOptions.map(this.buildItem),
    );
  }

  uncheckAll = () => {
    this.props.onClearChicklets();
  }

  toggleOptionsList = () => {
    this.setState(
      { optionsListVisible: !this.state.optionsListVisible },
    );
  }

  mainButtonLabel = () => {
    const { itemsState } = this.props.wrapperState;
    const { placeholderText, placeholderHandler } = this.props.mainSelector;

    if (itemsState && itemsState.length === 0) {
      return `${placeholderText}` || 'Search...';
    }

    if (placeholderHandler) {
      return placeholderHandler(itemsState, placeholderText);
    }

    return `${itemsState.length} Selected`;
  }

  resetDisplayed = () => {
    this.setState({
      optionsListVisible: false,
      activeValue: this.allOptions[0],
      activeOption: 0,
    });
  }

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

  setActiveOption = (values) => {
    if (values) {
      const option = this.allOptions.findIndex(o => isEqual(o.values, values));
      this.setState({ activeOption: option, activeValue: this.allOptions[option] });
    }
  }

  resetActiveOption = () => {
    this.setState({ activeOption: 0 });
  }

  handleKeyDown = (e) => {
    const { allOptions } = this;
    const { activeOption } = this.state;
    const allOptionsLength = allOptions.length;
    e.preventDefault();
    if (allOptionsLength) {
      switch (e.keyCode) {
        case ARROW_DOWN: {
          const next = (activeOption + 1) % allOptionsLength;
          this.setState({ activeOption: next, activeValue: this.allOptions[next] });
          break;
        }
        case ARROW_UP: {
          const prev = (activeOption > 0)
            ? (activeOption - 1) % allOptionsLength
            : allOptionsLength - 1;
          this.setState({ activeOption: prev, activeValue: this.allOptions[prev] });
          break;
        }
        case ENTER: {
          const { activeValue } = this.state;
          const { itemsState } = this.props.wrapperState;
          const checked = !itemsState.find(o => isEqual(o.values, activeValue.values));
          this.changeOption(activeValue, checked);
          break;
        }
        case ESCAPE: {
          this.resetDisplayed();
          break;
        }
        default:
          break;
      }
    }
  }

  renderOptionsList = () => {
    const { itemsState } = this.props.wrapperState;
    if (this.state.optionsListVisible) {
      return (
        <OptionsList
          theme={this.props.theme}
          checkAll={this.checkAll}
          uncheckAll={this.uncheckAll}
          optionGroups={this.props.mainSelector.optionGroups}
          changeOption={this.changeOption}
          changeGroup={this.changeGroup}
          toggleOptionsList={this.toggleOptionsList}
          selecteds={itemsState}
          activeValue={this.state.activeValue && this.state.activeValue.values}
          handleKeyDown={this.handleKeyDown}
          resetActiveOption={this.resetActiveOption}
          setActiveOption={this.setActiveOption}
        />
      );
    }
    return null;
  }

  render() {
    const { theme, chicklets, emptyItems, isChickletNone, onClearChicklets } = this.props;

    return (
      <div className={classNames('c-kd-multiple-select', theme['c-kd-multiple-select'])}>
        <MainButton
          theme={theme}
          label={this.mainButtonLabel()}
          active={this.state.optionsListVisible}
          onClick={this.toggleOptionsList}
        />
        {this.renderOptionsList()}
        {
          !emptyItems &&
            !isChickletNone &&
              chicklets.clearAll.visible &&
            <ClearAll theme={theme} onClick={onClearChicklets} />
        }
      </div>
    );
  }
}

KdSelectStringMultiple.propTypes = propTypes;
KdSelectStringMultiple.defaultProps = defaultProps;

export default wrap(KdSelectStringMultiple);
