import classNames from 'classnames';
import Datepicker from 'react-datepicker';
import extend from 'extend';
import moment from 'moment';
import React, { PropTypes } from 'react';

import BaseSelect from '../BaseSelect';
import BetweenDatepicker from './BetweenDatepicker';
import ClearAll from '../ClearAll';
import HourSelect from './HourSelect';
import options, { defaultOptions, buildChicklets } from './options';
import wrap from '../ItemContainerWrapper';
import { itemHandlerCreator, operatorSelector } from '../../utils';
import { dateHasErrors } from '../../utils/validations.js';
import { HOUR_FORMAT } from './hourOptions';

const defaultProps = {
  min: moment().subtract(3, 'months'),
  max: moment().add(1, 'years'),
  selectedValuesInitial: [],
  showMinTime: false,
  showMaxTime: false,
  mainSelector: {
    visible: true,
    optionValues: {
      anyOption: {
        visible: true,
      },
      afterOption: {
        visible: true,
      },
      beforeOption: {
        visible: true,
      },
      betweenOption: {
        visible: true,
      },
      todayOption: {
        visible: true,
      },
    },
  },
  events: {},
  clearAll: { visible: true },
};

const propTypes = {
  min: PropTypes.object,
  max: PropTypes.object,
  onOptionSelected: PropTypes.func.isRequired,
  onItemAdded: PropTypes.func.isRequired,
  onSetChicklets: PropTypes.func.isRequired,
  selectedValuesInitial: PropTypes.arrayOf(PropTypes.object),
  showMinTime: PropTypes.bool,
  showMaxTime: PropTypes.bool,
  mainSelector: PropTypes.shape({
    visible: PropTypes.bool,
    optionValues: PropTypes.shape({
      anyOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      afterOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      beforeOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      betweenOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      todayOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
    }),
  }),
  events: PropTypes.shape({
    onSelect: PropTypes.func,
    onChange: PropTypes.func,
  }),
};

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

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

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

    this.options = defaultOptions
      .filter(
        option => {
         const mainOption = this.mainSelector.optionValues[`${option.toLowerCase()}Option`];
         return mainOption && mainOption.visible;
        }
      )
      .map(option => {
          const mainOption = this.mainSelector.optionValues[`${option.toLowerCase()}Option`];
          const displayName = mainOption && mainOption.label;
          return options.create(option, showMinTime, showMaxTime, displayName)
        }
      );

    if (this.mainSelector.visible) {
      this.state = { selectedOption: this.options[0] };
    } else {
      const betweenOption = this.options.find(opt => opt.id === 'between');
      this.state = { selectedOption: betweenOption };
    }

    this.handleChange = this.handleChange.bind(this);
    this.onSelectChange = this.onSelectChange.bind(this);
    this.handleHourChange = this.handleHourChange.bind(this);
    this.getSelectedHour = this.getSelectedHour.bind(this);
    this.renderTime = this.renderTime.bind(this);
    this.fireOnChange = this.fireOnChange.bind(this);
    this.addItem = this.addItem.bind(this);
  }

  componentWillMount() {
    const {
      selectedValuesInitial,
      showMinTime,
      showMaxTime,
      onOptionSelected,
      onError
    } = this.props;

    if (selectedValuesInitial.length > 0) {
      const chicklets = buildChicklets(selectedValuesInitial, showMinTime, showMaxTime);

      let selectedOption;
      chicklets.forEach((chicklet) => {
        const option = this.options.find(opt => opt.id === chicklet.operator);

        if (option) {
          option.selected = chicklet.value;
          selectedOption = option;
        }
        this.addItem(chicklet);
      });

      this.setState({ selectedOption }, () => {
        onOptionSelected(selectedOption.id);
      });
    } else {
      const { selectedOption } = this.state;
      if (selectedOption.id !== 'any') {
        onError({ errors: dateHasErrors() });
      }
      onOptionSelected(selectedOption.id);
    }
  }


  onSelectChange({ target }) {
    const selectedOption = this.options.find(option => option.id === target.value);
    const { emptyItems, onClearChicklets, onError, min, max, isChickletNone } = this.props;
    if (selectedOption.id === 'any') {
      onClearChicklets();
    } else {
      if (selectedOption.loadDefaultValue()) {
        this.addItem(selectedOption.selected);
      } else {
        if (isChickletNone) {
          onClearChicklets();
          onError({ errors: dateHasErrors(selectedOption.value, min, max) });
        }
        if (emptyItems) {
          onError({ errors: dateHasErrors(selectedOption.value, min, max) });
        }
      }
    }

    this.props.onOptionSelected(selectedOption.id);

    this.setState({ selectedOption }, () => {
      const { onSelect, onChange } = this.props.events;
      const label = selectedOption.displayName;
      const value = selectedOption.id;
      const data = { label, value };
      if (onSelect) {
        onSelect({ data });
      }
      if (onChange) {
        onChange({ data });
      }
    });
  }

  getSelectedHour() {
    const { selectedOption } = this.state;

    if (selectedOption && selectedOption.selected) {
      return selectedOption.selected.value.format(HOUR_FORMAT);
    }

    return moment().hour(0).minutes(0).format(HOUR_FORMAT);
  }

  handleHourChange(hourString) {
    const hour = moment(hourString, HOUR_FORMAT).hour();

    const { selectedOption } = this.state;
    const date = selectedOption.selected;

    if (date && date.value) {
      date.value.hour(hour);
      selectedOption.selected = date.value;
      this.addItem(selectedOption.selected);
      this.fireOnChange();
    }
  }

  handleChange(date) {
    const { selectedOption } = this.state;
    if (!selectedOption.selected || !selectedOption.selected.value) {
      const process = d => d && d.hour(0).minutes(0);

      if (Array.isArray(date)) {
        selectedOption.selected = date.map(process);
      } else {
        selectedOption.selected = process(date);
      }
    } else {
      selectedOption.selected = date;
    }

    this.addItem(selectedOption.selected);
    this.fireOnChange();
  }

  fireOnChange() {
    const { onChange } = this.props.events;
    if (onChange) {
      const { selectedOption } = this.state;

      onChange({
        data: {
          value: selectedOption.id,
          label: (selectedOption.selected ? selectedOption.selected.label : null),
        },
      });
    }
  }


  selectedOptionHelper() {
    const { selectedOption } = this.state;
    const { min, max } = this.props;
    let selectedValue = null;
    if (!dateHasErrors(selectedOption.selectedValue, min, max)) {
      selectedValue = selectedOption.selectedValue;
    }
    switch (selectedOption.id) {
      case 'Gt':
      case 'Lt': {
        return (
          <div className="o-layout">
            <div
              className={
                classNames(
                  'o-layout__item',
                  {
                    'u-3/4': this.renderTime(selectedOption.id),
                    'u-1/1': !this.renderTime(selectedOption.id),
                  },
                )
              }
            >
              <Datepicker
                className="c-kd__input"
                selected={selectedValue}
                onChange={this.handleChange}
                showMonthDropdown
                dropdownMode="select"
                showYearDropdown
                scrollableYearDropdown
                minDate={min}
                maxDate={max}
                placeholderText={'MM/DD/YYYY'}
              />
            </div>
            {
              (() => {
                if (this.renderTime(selectedOption.id)) {
                  return (
                    <div className="u-1/4 o-layout__item">
                      <HourSelect
                        onChange={this.handleHourChange}
                        selected={this.getSelectedHour()}
                      />
                    </div>
                  );
                }

                return null;
              })()
            }
          </div>
        );
      }
      case 'Between': {
        const selectedValue = selectedOption.selectedValue;
        const from = selectedValue ? selectedValue[0] : null;
        const to = selectedValue ? selectedValue[1] : null;
        return (
          <BetweenDatepicker
            onChange={this.handleChange}
            min={min}
            max={max}
            from={from}
            to={to}
            showMinTime={this.props.showMinTime}
            showMaxTime={this.props.showMaxTime}
          />
        );
      }
      default:
        return null;
    }
  }

  renderTime(option) {
    if (option === 'Gt' && this.props.showMinTime) {
      return true;
    }

    if (option === 'Lt' && this.props.showMaxTime) {
      return true;
    }

    return false;
  }

  addItem(selectedItem) {
    const { option, value } = selectedItem;
    const { min, max, onItemAdded, kdFilterModel } = this.props;
    const kdFilterOptions = {
      model: kdFilterModel,
      operator: option,
      itemHandler: itemHandlerCreator(kdFilterModel.getDataType()),
    };

    onItemAdded({
      ...selectedItem,
      kdFilterOptions,
      errors: dateHasErrors(value, min, max),
    });
  }

  render() {
    return (
      <div className="c-kd-select-date">
        {
          (() => {
            if (this.mainSelector.visible) {
              return (
                <BaseSelect
                  inline options={this.options}
                  onChange={this.onSelectChange}
                  defaultValue={this.state.selectedOption.id}
                />
              );
            }

            return null;
          })()
        }
        {this.selectedOptionHelper()}
        {
          !this.props.emptyItems &&
            !this.props.isChickletNone &&
            <ClearAll onClick={this.props.onClearChicklets} />
        }
      </div>
    );
  }
}

KdSelectDate.defaultProps = defaultProps;
KdSelectDate.propTypes = propTypes;

export default wrap(KdSelectDate);
