import classNames from 'classnames';
import extend from 'extend';
import React from 'react';
import PropTypes from 'prop-types';
import isequal from 'lodash/isEqual';
import BaseSelect from '../BaseSelect';
import ClearAll from '../ClearAll';
import InputString from '../InputString';
import wrap from '../ItemContainerWrapper';
import optionsFactory from './options';
import { itemHandlerCreator, operatorSelector } from '../../utils';
import { CHICKLET_ORANGE, USER_SPECIFIED_OPTION, ANY } from '../../constants';

const defaultProps = {
  isDisabled: false,
  mainSelector: {
    visible: true,
    multipleSelection: false,
    notSelectedOption: { label: 'Select...' },
    userSpecifiedOption: { label: 'Search...' },
    optionGroups: [],
  },
  events: {},
  chicklets: { clearAll: { visible: false } },
};

const propTypes = {
  theme: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
  selectBehavior: PropTypes.string,
  kdFilterModel: PropTypes.object.isRequired,

  events: PropTypes.shape({
    onSelect: PropTypes.func,
    onChange: PropTypes.func,
  }),

  mainSelector: PropTypes.shape({
    visible: PropTypes.bool,
    multipleSelection: PropTypes.bool,
    notSelectedOption: PropTypes.shape({
      label: PropTypes.string,
      selected: PropTypes.bool,
    }),
    userSpecifiedOption: PropTypes.shape({
      label: PropTypes.string,
      selected: PropTypes.bool,
    }),
    optionGroups: PropTypes.arrayOf(
      PropTypes.shape({
        label: PropTypes.string,
        options: PropTypes.arrayOf(
          PropTypes.shape({
            values: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.object, PropTypes.string])),
            showTime: PropTypes.bool,
            selected: PropTypes.bool,
          })
        ),
      })
    ),
  }),

  // Private Props
  selectedValuesInitial: PropTypes.array,
  onOptionSelected: PropTypes.func.isRequired,
  onSetChicklets: PropTypes.func.isRequired,
  onClearChicklets: PropTypes.func.isRequired,
  isChickletNone: PropTypes.bool.isRequired,
  hasInitialValues: PropTypes.func.isRequired,
};

class KdSelectString extends React.Component {
  constructor(props) {
    super(props);

    this.mainSelector = extend(
      true,
      {},
      { visible: true, optionGroups: [], multipleSelection: false },
      props.mainSelector
    );
    this.options = this.propBasedOptions();
    this.kdFilterOptions = {
      model: props.kdFilterModel,
      operator: operatorSelector(props.kdFilterModel.getDataType()),
      itemHandler: itemHandlerCreator(props.kdFilterModel.getDataType()),
    };

    this.state = {
      selectedOption: this.options[0],
      selectedValue: '',
    };
  }

  getDefaultSelected = () => this.options.filter(o => o.isSelected);

  selectDefaultOption = () => {
    this.setState(
      {
        selectedOption: this.options[0],
        selectedValue: '',
      },
      () => this.props.onOptionSelected(this.state.selectedOption.id)
    );
  };

  remove = (items) => {
    const { inputString, selectDefaultOption, props: { isChickletNone } } = this;
    isChickletNone && inputString && inputString.setInputValue.bind(inputString);
    if (!items.length) {
      selectDefaultOption();
    }
  };

  setSelectedValues = (selectedValuesInitial) => {
    const { onOptionSelected, onSetChicklets } = this.props;
    if (selectedValuesInitial.length) {
      const option = this.options.find(o => o.id === USER_SPECIFIED_OPTION);
      if (option) {
        this.setState(
          {
            selectedOption: option,
            selectedValue: '',
          },
          () => onOptionSelected(option.id)
        );
      }
    }

    const chicklets = selectedValuesInitial.map(opt => ({
      ...this.buildItem({
        value: opt.values[0],
        label: opt.label,
      }).data,
      values: opt.values,
    }));
    onSetChicklets({ chicklets, model: this.kdFilterOptions.model });
  };

  propBasedOptions = () => {
    const { notSelectedOption, userSpecifiedOption, optionGroups } = this.mainSelector;
    let customOptions = [];

    if (!this.mainSelector.visible) {
      return [optionsFactory.create(USER_SPECIFIED_OPTION)];
    }

    if (optionGroups.length) {
      customOptions = optionGroups[0].options.map(
        option => option.values && optionsFactory.createCustom(option)
      );
    }

    if (notSelectedOption) {
      optionsFactory.setAnyDisplayName(notSelectedOption.label);
      customOptions = [optionsFactory.create(ANY, notSelectedOption.selected), ...customOptions];
    }

    if (userSpecifiedOption) {
      optionsFactory.setUserSpecifiedDisplayName(userSpecifiedOption.label);
      customOptions = [
        ...customOptions,
        optionsFactory.create(USER_SPECIFIED_OPTION, userSpecifiedOption.selected),
      ];
    }

    return customOptions;
  };

  componentDidMount() {
    const { selectedValuesInitial } = this.props;
    if (selectedValuesInitial.length) {
      this.setSelectedValues(this.props.selectedValuesInitial);
    } else {
      const selectedOptions = this.getDefaultSelected();
      if (selectedOptions) {
        selectedOptions.forEach((selectedOption) => {
          this.onSelectChange({ target: { value: selectedOption.id } }, true);
        });
      }
    }
  }

  // componentWillReceiveProps(nextProps) {
  //   const { selectedValuesInitial, onClearChicklets } = this.props;
  //   if (selectedValuesInitial && !isequal(selectedValuesInitial, nextProps.selectedValuesInitial)) {
  //     this.kdFilterOptions = {
  //       model: nextProps.kdFilterModel,
  //       operator: operatorSelector(nextProps.kdFilterModel.getDataType()),
  //       itemHandler: itemHandlerCreator(nextProps.kdFilterModel.getDataType()),
  //     };
  //     onClearChicklets();
  //     this.setSelectedValues(nextProps.selectedValuesInitial);
  //   }
  // }

  isInputVisible = () => this.state.selectedOption.id === USER_SPECIFIED_OPTION;

  onSelectChange = ({ target }, silent = false) => {
    const { events: { onSelect }, onOptionSelected, onClearChicklets } = this.props;
    const { model } = this.kdFilterOptions;
    const selectedOption = this.options.find(option => option.id === String(target.value));
    if (!this.mainSelector.multipleSelection) {
      onClearChicklets();
    }
    if (selectedOption.id === ANY) {
      const data = { value: ANY, label: ANY };
      !silent && onSelect && onSelect({ data });
    }

    if (selectedOption.id === USER_SPECIFIED_OPTION) {
      const data = { value: 'userSpecifiedOption', label: 'userSpecifiedOption' };
      !silent && onSelect && onSelect({ data });
    }
    if (selectedOption.loadDefaultValue()) {
      this.addItem(
        selectedOption.selected,
        !silent &&
          (() =>
            onSelect &&
            onSelect({
              data: {
                value: selectedOption.selected.value,
                label: selectedOption.selectedLabel,
                model,
                ...model.getFilterItemValue(),
              },
            }))
      );
    }

    this.setState(
      {
        selectedOption,
        selectedValue: selectedOption.selectedLabel,
      },
      () => {
        onOptionSelected(this.state.selectedOption.id);
        this.inputString && this.inputString.focus();
      }
    );
  };

  buildItem = ({ value, label, customOperator }) => {
    const { events: { onChange } } = this.props;
    const { model, itemHandler } = this.kdFilterOptions;
    let { operator } = this.kdFilterOptions;
    const callback = () => {
      onChange &&
        onChange({
          data: {
            value,
            model,
            label,
            ...model.getFilterItemValue(),
          },
        });
    };
    operator = customOperator || operator;
    const data = {
      value,
      chickletLabel: label,
      option: value,
      remove: this.remove,
      kdFilterOptions: { model, operator, itemHandler },
      inputVisible: this.isInputVisible(),
    };
    return { data, callback };
  };

  addItem = ({ values, label, customOperator, silent = false }) => {
    values.forEach((value) => {
      const customLabel = values.length > 1 ? value : label;
      const item = this.buildItem({ value, label: customLabel, customOperator });
      this.props.onItemAdded(item.data, !silent && item.callback);
    });
  };

  optionHelper = () => {
    const {
      theme,
      isChickletNone,
      onClearChicklets,
      lastInitialValue,
      hasInitialValues,
    } = this.props;
    switch (this.state.selectedOption.id) {
      case USER_SPECIFIED_OPTION:
        return (
          <InputString
            theme={theme}
            ref={ref => (this.inputString = ref)}
            addItem={this.addItem}
            isChickletNone={isChickletNone}
            onClearChicklets={onClearChicklets}
            lastInitialValue={lastInitialValue()}
            hasInitialValues={hasInitialValues()}
          />
        );
      default:
        return null;
    }
  };

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

    return (
      <div
        className={classNames({ 'u-disabled': isDisabled }, { [theme['u-disabled']]: isDisabled })}
      >
        {this.mainSelector.visible && (
          <BaseSelect
            theme={theme}
            options={this.options}
            onChange={this.onSelectChange}
            defaultValue={this.state.selectedOption.id}
          />
        )}
        {this.optionHelper()}
        {!emptyItems &&
          !isChickletNone &&
          chicklets.clearAll.visible && (
            <ClearAll theme={theme} onClick={this.props.onClearChicklets} />
          )}
      </div>
    );
  }
}

KdSelectString.defaultProps = defaultProps;
KdSelectString.propTypes = propTypes;

export const UnwrappedKdSelectString = KdSelectString;

export default wrap(KdSelectString);
