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

import ClearAll from '../ClearAll';
import options, { buildCustomOption, buildOptionLabel } from './options';
import KdSlider from '../KdSlider';
import wrap from '../ItemContainerWrapper';
import { ADD_CHICKLET_SINGLE } from '../../constants';
import { numberHasErrors } from '../../utils/validations.js';
import { numberHandler } from '../../utils/number.js';

const defaultProps = {
  isDisabled: false,
  selectedValuesInitial: [],
  selectBehavior: ADD_CHICKLET_SINGLE,
  min: 0,
  max: 1000,
  mainSelector: {
    visible: true,
    selected: 'anyOption',
    optionValues: {
      anyOption: { visible: true },
      equalToOption: { visible: true },
      noEqualToOption: { visible: false },
      greaterThanOption: { visible: false },
      greaterThanOrEqualToOption: { visible: true },
      lessThanOption: { visible: false },
      lessThanOrEqualToOption: { visible: true },
      betweenOption: { visible: true },
      noneOption: { visible: false },
      moreThanZeroOption: { visible: false },
      customOptions: [],
    },
  },
  events: {},
  slider: {
    label: { visible: true },
  },
  clearAll: { visible: true },
};


const propTypes = {
  theme: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.func,
  ]).isRequired,
  isDisabled: PropTypes.bool,
  onOptionSelected: PropTypes.func,
  onItemAdded: PropTypes.func,
  onClearChicklets: PropTypes.func,
  onError: PropTypes.func,
  selectedValuesInitial: PropTypes.arrayOf(
    PropTypes.shape({
      operator: PropTypes.string,
      values: PropTypes.array,
      label: PropTypes.string,
    }),
  ),
  type: PropTypes.string,
  decimals: PropTypes.number,
  min: PropTypes.number,
  max: PropTypes.number,
  kdFilterModel: PropTypes.object.isRequired,
  emptyItems: PropTypes.bool,
  isChickletNone: PropTypes.bool,

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

    optionValues: PropTypes.shape({
      anyOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      equalToOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      noEqualToOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      greaterThanOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      greaterThanOrEqualToOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      lessThanOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      lessThanOrEqualToOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      betweenOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      noneOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      moreThanZeroOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      customOptions: PropTypes.arrayOf(
        PropTypes.shape({
          label: PropTypes.string,
          operator: PropTypes.string,
          values: PropTypes.arrayOf(
            PropTypes.number,
          ),
        }),
      ),
    }),
  }),

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

  slider: PropTypes.shape({
    label: PropTypes.shape({
      visible: PropTypes.bool,
      handler: PropTypes.func,
    }),
  }),

  clearAll: PropTypes.shape({
    visible: PropTypes.bool,
  }),
};

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

    const { type, decimals } = props;

    this.mainSelector = extend(true, {}, defaultProps.mainSelector, this.props.mainSelector);
    this.numberUtils = numberHandler({ type, decimals });
    this.options = this.propBasedOptions();

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

  componentWillMount() {
    const { onOptionSelected, onError, selectedValuesInitial } = this.props;
    onOptionSelected(this.state.selectedOption.id);

    if (this.state.selectedOption.operator !== 'any') {
      onError({ errors: numberHasErrors('') });
    }

    selectedValuesInitial.forEach(({ operator, values }) => {
      const relatedOption = this.options
        .find(option => option.operator === operator);


      if (relatedOption) {
        if (Array.isArray(values) && values.length === 1) {
          relatedOption.value = String(values[0]);
        } else {
          relatedOption.value = values;
        }
        this.addItem(this.buildItemToAdd(relatedOption));
        this.setState({
          ...this.state,
          selectedOption: relatedOption,
        });
      }
    });
  }

  propBasedOptions() {
    const { optionValues, visible } = this.mainSelector;
    if (!visible) {
      return [options.create('betweenOption')];
    }

    const selectOptionsBefore = Object.keys(optionValues)
      .filter(key => optionValues[key].visible)
      .map(key => options.create(key, optionValues[key].label))
      .filter(option => option.first);

    const selectOptionsAfter = Object.keys(optionValues)
      .filter(key => optionValues[key].visible)
      .map(key => options.create(key, optionValues[key].label))
      .filter(option => !option.first);

    const customOptions = optionValues.customOptions.map(customOption => (
      buildCustomOption({
        value: customOption.values,
        label: customOption.label,
        operator: customOption.operator,
      })
    ));

    const total = selectOptionsBefore
      .concat(customOptions)
      .concat(selectOptionsAfter);

    return total;
  }

  addItem = (item) => {
    const { onItemAdded, events: { onChange } } = this.props;
    const callback = () => (
      onChange && onChange({ data: item })
    );
    onItemAdded(item, callback);
  }

  onSelectChange = ({ target }) => {
    const selectedOption = this.options.find(option => option.id === target.value);
    const {
      isChickletNone,
      onOptionSelected,
      onError,
      events: { onSelect },
    } = this.props;

    if (isChickletNone || selectedOption.operator === 'any') {
      this.clearValues();
      this.props.clearErrors();
    }

    if (selectedOption.value) {
      this.addItem(this.buildItemToAdd(selectedOption));
    } else if (selectedOption.operator !== 'any') {
      onError({ errors: numberHasErrors('') });
    }

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

      const value = selectedOption.selected ? selectedOption.selected.value : null;
      const label = selectedOption.selected ? selectedOption.selected.chickletLabel : null;
      const data = { label, value };

      if (onSelect) {
        onSelect({ data });
      }
    });
  }

  onSelectedValueChange = (value) => {
    const { operator, id } = this.state.selectedOption;
    const selectedItem = { operator, id, value };

    this.setState({
      selectedOption: selectedItem,
    });

    this.addItem(
      this.buildItemToAdd(selectedItem),
    );
  }

  buildItemToAdd = (selectedItem) => {
    const { operator, value, id } = selectedItem;
    const { kdFilterModel, slider: { label: { handler } }, min, max } = this.props;

    const kdFilterOptions = {
      operator,
      model: kdFilterModel,
      itemHandler: this.numberUtils.handler,
    };

    let chickletLabel = buildOptionLabel({
      value: selectedItem.value,
      unit: this.numberUtils.unit,
      operator,
      handler,
    });

    if (selectedItem.custom) {
      chickletLabel = selectedItem.displayName;
    }

    return {
      option: id,
      errors: numberHasErrors(value, min, max),
      value,
      chickletLabel,
      kdFilterOptions,
    };
  }

  clearValues = () => {
    this.props.onClearChicklets();
    this.options
      .filter(option => option.custom !== true)
      .forEach((option) => { option.value = null; });
  }

  renderUnit() {
    const { unit } = this.numberUtils;
    const { theme } = this.props;
    if (unit) {
      return (
        <span
          className={classNames('c-kd-select-number__unit', theme['c-kd-select-number__unit'])}>
          {unit}
        </span>
      );
    }
  }

  optionHelper = () => {
    const { theme } = this.props;
    const { operator, custom, value } = this.state.selectedOption;
    if (custom) {
      return null;
    }

    const selectedValue = value || '';

    switch (operator) {
      case 'Between': {
        const { min, max, slider, onError, isChickletNone, events: { onChange } } = this.props;
        return (
          <KdSlider
            theme={theme}
            slider={slider}
            min={min}
            max={max}
            values={selectedValue}
            onError={onError}
            unit={this.numberUtils.unit}
            isChickletNone={isChickletNone}
            onChange={onChange}
            onSelectedValue={this.onSelectedValueChange}
          />
        );
      }
      case 'Eq':
      case 'NotEq':
      case 'Gt':
      case 'Gte':
      case 'Lt':
      case 'Lte':
        return (
          <div className={classNames('c-kd-select-number', theme['c-kd-select-number'])}>
            <input
              className={
                classNames(
                  'c-kd-select-number__input',
                  'u-1/1',
                  'c-kd__input',
                  theme['c-kd-select-number__input'],
                  theme['c-kd__input'],
                  theme['u-1/1'],
                )
              }
              ref={ref => this.input = ref}
              onChange={({ target }) => this.onSelectedValueChange(target.value)}
              value={selectedValue}
              type="text"
            />
            {this.renderUnit()}
          </div>
        );
      default:
        return null;
    }
  }

  render() {
    const { theme } = this.props;
    return (
      <div
        className={
          classNames('c-kd-select-number',
            theme['c-kd-select-number'],
            { 'u-disabled': this.props.isDisabled },
            { [theme['u-disabled']]: this.props.isDisabled },
          )
        }
      >
        {
          this.mainSelector.visible &&
            <div className={classNames('c-base-select', theme['c-base-select'])}>
              <select onChange={this.onSelectChange} value={this.state.selectedOption.id}>
                {
                  this.options.map(option => (
                    <option
                      value={option.id}
                      key={option.id}
                    >
                      {option.displayName}
                    </option>
                  ))
                }
              </select>
            </div>
        }
        {this.optionHelper()}
        {
          !this.props.emptyItems &&
            !this.props.isChickletNone &&
            this.props.chicklets.clearAll.visible &&
            <ClearAll theme={theme} onClick={this.clearValues} />
        }
      </div>
    );
  }
}

KdSelectNumber.propTypes = propTypes;
KdSelectNumber.defaultProps = defaultProps;

export default wrap(KdSelectNumber);
