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,
  showMonthDropdown: true,
  showYearDropdown: true,
  initialOption: 'Between',
  mainSelector: {
    visible: true,
    optionValues: {
      anyOption: {
        visible: true,
      },
      onOption: {
        visible: true,
      },
      afterOption: {
        visible: true,
      },
      beforeOption: {
        visible: true,
      },
      betweenOption: {
        visible: true,
      },
      customOptions: [],
    },
  },
  events: {},
  clearAll: { visible: true },
};

const propTypes = {
  theme: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.func,
  ]).isRequired,
  min: PropTypes.object,
  max: PropTypes.object,
  onOptionSelected: PropTypes.func.isRequired,
  onItemAdded: PropTypes.func.isRequired,
  onSetChicklets: PropTypes.func.isRequired,
  labelHandler: PropTypes.func,
  selectedValuesInitial: PropTypes.arrayOf(PropTypes.object),
  showMinTime: PropTypes.bool,
  showMaxTime: PropTypes.bool,
  showMonthDropdown: PropTypes.bool,
  showYearDropdown: PropTypes.bool,
  initialOption: PropTypes.oneOf(['Eq', 'Gt', 'Lt', 'Between']),
  mainSelector: PropTypes.shape({
    visible: PropTypes.bool,
    optionValues: PropTypes.shape({
      anyOption: PropTypes.shape({
        visible: PropTypes.bool,
        label: PropTypes.string,
      }),
      onOption: 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,
      }),
      customOptions: PropTypes.arrayOf(
        PropTypes.shape({
          label: PropTypes.string,
          values: PropTypes.arrayOf(PropTypes.object),
          operator: 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 { kdFilterModel } = this.props;
    this.kdFilterOptions = {
      model: kdFilterModel,
      operator: operatorSelector(kdFilterModel.getDataType()),
      itemHandler: this.props.itemHandler || itemHandlerCreator(kdFilterModel.getDataType()),
    };

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

  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)
          || this.options.find(opt => opt.operator === chicklet.operator && `custom${[chicklet.value]}` === opt.id);

        let label = chicklet.chickletLabel;
        if (option) {
          option.selected = chicklet.value;
          selectedOption = option;
          if (option.displayName) label = option.displayName;
        }

        this.addItem(Object.assign({}, chicklet, { chickletLabel: label }));
      });

      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,
      clearErrors
    } = this.props;

    if (selectedOption.id === 'any') {
      onClearChicklets();
      clearErrors();
    } 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 } = this.props.events;
      const label = selectedOption.displayName;
      const value = selectedOption.id;
      const data = { label, value };
      if (onSelect) {
        onSelect({ 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);
  }

  buildOptions = () => {
    const { showMinTime, showMaxTime } = this.props;
    const opts = 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);
      });

    const firstOpts = opts.filter(opt => opt.first);
    const lastOpts = opts.filter(opt => opt.first !== true);
    const customOptions = this.mainSelector.optionValues.customOptions.map(options.createCustom);

    return [...firstOpts, ...customOptions, ...lastOpts];
  }

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

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

  selectedOptionHelper = () => {
    const { selectedOption } = this.state;
    const { min, max, theme } = this.props;
    let selectedValue = null;
    if (!dateHasErrors(selectedOption.selectedValue, min, max)) {
      selectedValue = selectedOption.selectedValue;
    }

    const dateFormat = this.props.showYearDropdown ? 'MM/DD/YYYY' : 'MM/DD';

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

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

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

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

    return false;
  }

  getChickletLabel = (selectedItem) => {
    const { labelHandler } = this.props;
    if (labelHandler) {
      return labelHandler(selectedItem);
    }

    return selectedItem.chickletLabel;
  }

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

    const callback = () => {
      onChange && onChange({ data: { option, value, ...kdFilterModel.getFilterItemValue() } });
    };
    onItemAdded({
      ...selectedItem,
      kdFilterOptions,
      chickletLabel: this.getChickletLabel({ ...selectedItem, operator: option }),
      errors: dateHasErrors(value, min, max),
      option: selectedItem.custom ? selectedItem.id : selectedItem.option,
    }, callback);
  }

  render() {
    const { theme } = this.props;
    return (
      <div className={classNames('c-kd-select-date', theme['c-kd-select-date'])}>
        {
          (() => {
            if (this.mainSelector.visible) {
              return (
                <BaseSelect
                  theme={theme}
                  inline options={this.options}
                  onChange={this.onSelectChange}
                  defaultValue={this.state.selectedOption.id}
                />
              );
            }

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

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

export default wrap(KdSelectDate);
