import classNames from 'classnames';
import extend from 'extend';
import React, { PropTypes } from 'react';

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.isRequired,
    key: PropTypes.string.isRequired,
  }),

  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,
};

class KdSelectDropDown extends React.Component {

  constructor(props) {
    super(props);

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

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

    this.options = this.propBasedOptions();

    this.onSelectChange = this.onSelectChange.bind(this);
    this.addItem = this.addItem.bind(this);
    this.isInputVisible = this.isInputVisible.bind(this);

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

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

  componentDidMount() {
    this.props.onOptionSelected(this.state.selectedOption.id);

    this.props.selectedValuesInitial.forEach(({ value, label }) => {
      const relatedOption = this.options.find(option => option.key === value);

      if (relatedOption) {
        relatedOption.selected = { value, label, option: value };

        if (this.props.isChickletNone) {
          this.setState({
            selectedOption: relatedOption,
            selectedValue: relatedOption.selectedLabel,
          }, () => this.props.onOptionSelected(this.state.selectedOption.id));
        }
      }
    });

    const keys = this.options.map(opt => opt.key);
    const remove = this.props.isChickletNone
      && this.inputString
      && this.inputString.setInputValue.bind(this.inputString);

    const { model, itemHandler } = this.kdFilterOptions;
    const initialOpts = this.props.selectedValuesInitial
      .filter(opt => keys.includes(opt.value))
      .map(opt => ({
        remove,
        value: opt.value,
        chickletLabel: opt.value,
        option: opt.value,
        category: this.category,
        inputVisible: this.isInputVisible(),
        autocompleteVisible: this.isInputVisible(),
        isChickletNone: this.props.isChickletNone,
        hasInitialValues: this.mainSelector.visible,
        kdFilterOptions: { operator: 'Eq', model, itemHandler },
      }));

    this.props.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 })
              )
            )
        )
    );
  }

  isInputVisible() {
    return (this.state.selectedOption.id === USER_SPECIFIED_OPTION);
  }

  onSelectChange({ target }) {
    const { events: { onChange, onSelect }, onOptionSelected } = this.props;
    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 });
      onChange && onChange({ data });
    }

    if (selectedOption.id === USER_SPECIFIED_OPTION) {
      const data = { value: 'userSpecifiedOption', label: 'userSpecifiedOption' };
      onSelect && onSelect({ data });
      onChange && onChange({ data });
    }

    if (selectedOption.loadDefaultValue()) {
      this.addItem(selectedOption.selected);
      onSelect && onSelect({
        data: {
          value: selectedOption.selected.value,
          label: selectedOption.selectedLabel,
        },
      });
    }

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

  addItem({ value, category, customOperator }) {
    const { events } = this.props;
    const remove = this.props.isChickletNone
      && this.inputString
      && this.inputString.setInputValue.bind(this.inputString);
    const { model, itemHandler } = this.kdFilterOptions;
    let { operator } = this.kdFilterOptions;

    operator = customOperator ? customOperator : operator;

    this.props.onItemAdded({
      value,
      chickletLabel: value,
      option: value,
      remove,
      category,
      kdFilterOptions: { model, operator, itemHandler },
      inputVisible: this.isInputVisible(),
    });
    events.onChange && events.onChange({ data: { value, label: value } });
  }

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

  render() {
    const { theme } = this.props;

    return (
      <div className={
        classNames(
          { 'u-disabled': this.props.isDisabled },
          { [theme['u-disabled']]: this.props.isDisabled },
        )}
      >
        {this.props.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);
