import classNames from 'classnames';
import extend from 'extend';
import React, { PropTypes } from 'react';
import isequal from 'lodash.isequal';
import get from 'lodash.get';
import BaseSelect from '../BaseSelect';
import InputString from '../InputString';
import wrap from '../ItemContainerWrapper';
import options, { buildCustomOption } from './options';
import { itemHandlerCreator, operatorSelector } from '../../utils';
import { ADD_CHICKLET, CHICKLET_ORANGE, USER_SPECIFIED_OPTION, ANY } from '../../constants';


const defaultProps = {
  selectedValuesInitial: [],
  selectBehavior: ADD_CHICKLET,
  isDisabled: false,
  mainSelector: {
    visible: false,
    selected: '',
    optionValues: {
      anyOption: {
        visible: true,
      },
      customOptions: [],
      userSpecifiedOption: {
        visible: true,
      },
    },
  },
  events: {},
  autoComplete: {
    clearTextboxOnSelection: true,
    renderClearTextboxButton: true,
  },
  multipleCategories: true,
};

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

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

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

  mainSelector: PropTypes.shape({
    visible: PropTypes.bool,
    selected: PropTypes.string,

    optionValues: PropTypes.shape({
      selectOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      anyOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      customOptions: PropTypes.array,
      userSpecifiedOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
    }),
  }),

  autoComplete: PropTypes.shape({
    onSuccess: PropTypes.func,
    onRender: PropTypes.func,
    onSelect: PropTypes.func,
    onFail: PropTypes.func,
    onDone: PropTypes.func,
    placeholderText: PropTypes.string,
    clearTextboxOnSelection: PropTypes.bool,
    renderClearTextboxButton: PropTypes.bool,
  }),
  selectedValuesInitial: PropTypes.array,
  // Private Props
  multipleCategories: PropTypes.bool,
  onOptionSelected: PropTypes.func.isRequired,
  onSetChicklets: PropTypes.func.isRequired,
  onClearChicklets: PropTypes.func.isRequired,
  isChickletNone: PropTypes.bool.isRequired,
  hasInitialValues: PropTypes.func.isRequired,
};

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

    this.mainSelector = extend(true, {}, defaultProps.mainSelector, this.props.mainSelector);

    if (props.multipleCategories) {
      this.category = CHICKLET_ORANGE;
    }

    this.options = this.propBasedOptions();

    this.kdFilterOptions = {
      model: this.props.kdFilterModel,
      operator: operatorSelector(props.kdFilterModel.getDataType()),
      itemHandler: itemHandlerCreator(props.kdFilterModel.getDataType()),
    };

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

  getCategory = (operator) => {
    if (operator === 'Eq') {
      return CHICKLET_ORANGE;
    }
  }

  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,
      isChickletNone,
      onSetChicklets,
    } = this.props;
    let optionSelected = true;
    // set first option selected by default
    onOptionSelected(this.state.selectedOption.id);

    selectedValuesInitial.forEach(({ value, label }) => {
      const relatedOption = this.options.find(option => option.key === value);
      if (relatedOption) {
        optionSelected = false;
        relatedOption.selected = { value, label, option: value };
        this.setState({
          selectedOption: relatedOption,
          selectedValue: relatedOption.selectedLabel,
        }, () => onOptionSelected(this.state.selectedOption.id));
      }
    });
    // set selected the user custom if has values but they are not included in options
    if (optionSelected && selectedValuesInitial.length) {
      const option = this.options.find(o => o.id === USER_SPECIFIED_OPTION);
      if (option) {
        this.setState({
          selectedOption: option,
          selectedValue: '',
        }, () => onOptionSelected(option.id));
      }
    }

    const { model, itemHandler } = this.kdFilterOptions;
    const initialOpts = selectedValuesInitial
      .map(opt => ({
        isChickletNone,
        remove: this.remove,
        value: opt.value,
        label: get(this.options.find(o => o.id === opt.value), 'displayName', opt.label),
        chickletLabel: get(this.options.find(o => o.id === opt.value), 'displayName', opt.label),
        option: opt.value,
        category: this.getCategory(opt.operator),
        inputVisible: this.isInputVisible(),
        autocompleteVisible: this.isInputVisible(),
        hasInitialValues: this.mainSelector.visible,
        kdFilterOptions: { operator: opt.operator || 'Eq', model, itemHandler },
      }));
    if (initialOpts) {
      onSetChicklets(initialOpts);
    }
  }

  propBasedOptions = () => {
    const { anyOption, userSpecifiedOption } = this.mainSelector.optionValues;
    const defaultOptions = [];

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

    if (anyOption.visible) {
      defaultOptions.push(ANY);
      if (anyOption.label) {
        options.optionsMap[ANY].displayName = anyOption.label;
      }
    }

    if (userSpecifiedOption.visible) {
      defaultOptions.push(USER_SPECIFIED_OPTION);
      if (userSpecifiedOption.label) {
        options.optionsMap[USER_SPECIFIED_OPTION].displayName = userSpecifiedOption.label;
      }
    }

    return (
      defaultOptions
        .map(type => options.create(type))
        .concat(
          this.mainSelector.optionValues.customOptions
            .map(
              ({ value, label, position }) => (
                buildCustomOption({ value, label, category: this.category, position })
              )
            )
        )
    );
  }

  componentDidMount() {
    this.setSelectedValues(this.props.selectedValuesInitial);
  }

  componentWillReceiveProps(nextProps) {
    const { selectedValuesInitial, onClearChicklets } = this.props;

    if (selectedValuesInitial && !isequal(selectedValuesInitial, nextProps.selectedValuesInitial)) {
      onClearChicklets(false);
      this.setSelectedValues(nextProps.selectedValuesInitial);
    }
  }

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


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

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

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

  addItem = ({ value, label, category, customOperator }) => {
    const { events, isChickletNone } = this.props;
    const { model, itemHandler } = this.kdFilterOptions;
    let { operator } = this.kdFilterOptions;
    const callback = () => {
      events &&
      events.onChange &&
        events.onChange({
          data: {
            value,
            label: label || value,
            model,
            ...model.getFilterItemValue(),
          },
        });
    };

    operator = customOperator || operator;

    this.props.onItemAdded({
      value,
      chickletLabel: label || value,
      option: value,
      remove: this.remove,
      category,
      kdFilterOptions: { model, operator, itemHandler },
      inputVisible: this.isInputVisible(),
    }, 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 } = 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()}
      </div>
    );
  }

}

KdSelectDropDown.defaultProps = defaultProps;
KdSelectDropDown.propTypes = propTypes;

export const UnwrappedKdSelectDropDown = KdSelectDropDown;

export default wrap(KdSelectDropDown);
