import React from 'react';
import PropTypes from 'prop-types';
import { withStateHandlers, withProps, lifecycle, compose } from 'recompose';
import moment from 'moment';

import styles from './styles.scss';
import { handleDateRange, isDateValid, rangeSwap, toUtc } from './helpers';
import ToggleButtons from './ToggleButtons';
import RangeInputs from './RangeInputs';
import MonthNavigation from './MonthNavigation';
import Calendar from './Calendar';
import getProcoreLocale from '../_shared/utils/getProcoreLocale';

/* moment setup */
let dateFormat;
let weekdayLabels;
function setLocale(locale) {
  moment.locale(getProcoreLocale(locale));
  dateFormat = moment.localeData().longDateFormat('L');
  weekdayLabels = moment.weekdaysMin(true).map(name => name.substr(0, 1));
}

const initialState = ({
  committedDates = [{}, {}],
  currentMonth = moment(),
  keyboardTempValues = ['', ''],
  isRange = false
}) => ({
  committedDates,
  currentMonth,
  hoverDate: {},
  isRange,
  isTyping: false,
  keyboardTempValues,
  step: 0
});

const actions = {
  clearDate: (state, props) => (step) => {
    const newDates = state.committedDates.slice();
    newDates[step] = {};
    const newKeyboardTempValues = state.keyboardTempValues.slice();
    newKeyboardTempValues[step] = '';

    return {
      ...state,
      committedDates: newDates,
      keyboardTempValues: newKeyboardTempValues
    };
  },
  setSingleDate: (state, props) => ({ date }) => {
    const newDates = [date, {}];

    return {
      ...state,
      committedDates: newDates,
      step: 0
    };
  },
  setRangeDates: (state, props) => ({ date }) => {
    const { step } = state;
    const dates = state.committedDates.slice();
    const { newDates, nextStep } = handleDateRange(date, dates, step);
    const isValidStartDate = isDateValid(newDates, 0);
    const isValidEndDate = isDateValid(newDates, 1);
    const newStartKeyboardDate = isValidStartDate ? newDates[0].format(dateFormat) : '';
    const newEndKeyboardDate = isValidEndDate ? newDates[1].format(dateFormat) : '';

    return {
      ...state,
      committedDates: newDates,
      step: nextStep,
      keyboardTempValues: [newStartKeyboardDate, newEndKeyboardDate]
    };
  },
  goToPreviousMonth: state => () => ({
    ...state,
    currentMonth: moment(state.currentMonth).subtract(1, 'months')
  }),
  goToNextMonth: state => () => ({
    ...state,
    currentMonth: moment(state.currentMonth).add(1, 'months')
  }),
  onInputBlur: (state, props) => () => {
    const isValidStartDate = isDateValid(state.committedDates, 0);
    const isValidEndDate = isDateValid(state.committedDates, 1);
    let newDates = [];
    let newKeyboardTempValues = [];

    if (isValidStartDate && isValidEndDate) {
      newDates = rangeSwap(state.committedDates[0], state.committedDates[1]);
      newKeyboardTempValues = [newDates[0].format(dateFormat), newDates[1].format(dateFormat)];
    } else {
      newDates = state.committedDates;
      newKeyboardTempValues = state.keyboardTempValues;
    }

    return {
      ...state,
      committedDates: newDates,
      keyboardTempValues: newKeyboardTempValues
    };
  },
  setKeyboardSingleDate: (state, props) => ({ value }) => {
    const isValid = moment(value, dateFormat, true).isValid();
    const firstDate = isValid ? moment(value, dateFormat, true) : {};
    const currentMonth = isValid ? moment(value, dateFormat, true) : state.currentMonth;

    return {
      ...state,
      keyboardTempValues: [value, ''],
      committedDates: [firstDate, {}],
      currentMonth
    };
  },
  setStartDate: (state, props) => ({ value }) => {
    const isValid = moment(value, dateFormat, true).isValid();
    const newStartDate = isValid ? moment(value, dateFormat, true) : {};
    const currentMonth = isValid ? moment(newStartDate, dateFormat, true) : state.currentMonth;
    const newCommittedDates = [newStartDate, state.committedDates[1]];

    return {
      ...state,
      keyboardTempValues: [value, state.keyboardTempValues[1]],
      committedDates: newCommittedDates,
      currentMonth
    };
  },
  setEndDate: (state, props) => ({ value }) => {
    const isValid = moment(value, dateFormat, true).isValid();
    const newEndDate = isValid ? moment(value, dateFormat, true) : {};
    const currentMonth = isValid ? moment(newEndDate, dateFormat, true) : state.currentMonth;
    const newCommittedDates = [state.committedDates[0], newEndDate];

    return {
      ...state,
      keyboardTempValues: [state.keyboardTempValues[0], value],
      committedDates: newCommittedDates,
      currentMonth
    };
  },
  setHoverDate: state => payload => ({ ...state, hoverDate: payload }),
  setIsRange: (state, props) => (isRange) => {
    props.onRangeChange();
    return {
      ...state,
      isRange,
      committedDates: [{}, {}],
      keyboardTempValues: ['', '']
    };
  },
  setIsTyping: state => payload => ({ ...state, isTyping: payload }),
  setStep: state => payload => ({ ...state, step: payload })
};

const CalendarParticle = ({
  clearDate,
  committedDates,
  currentMonth,
  goToNextMonth,
  goToPreviousMonth,
  hoverDate,
  isRange,
  isRangeToggleVisible,
  isTyping,
  keyboardTempValues,
  onInputBlur,
  onSelect,
  setEndDate,
  setHoverDate,
  setIsRange,
  setIsTyping,
  setKeyboardSingleDate,
  setRangeDates,
  setSingleDate,
  setStartDate,
  setStep,
  step,
  locale
}) => (
  <div className={styles['date-picker']}>
    <div className={styles['date-picker__container']}>
      {isRangeToggleVisible && (
        <ToggleButtons
          {...{
            isRange,
            setIsRange
          }}
        />
      )}
      <RangeInputs
        {...{
          clearDate,
          committedDates,
          currentMonth,
          dateFormat,
          isRange,
          isTyping,
          keyboardTempValues,
          onInputBlur,
          setIsTyping,
          setEndDate,
          setKeyboardSingleDate,
          setStartDate,
          setStep,
          step,
          locale
        }}
      />
      <MonthNavigation
        {...{
          currentMonth,
          goToPreviousMonth,
          goToNextMonth,
          locale
        }}
      />
      <Calendar
        {...{
          committedDates,
          currentMonth,
          hoverDate,
          isRange,
          setHoverDate,
          setRangeDates,
          setSingleDate,
          step,
          weekdayLabels
        }}
      />
    </div>
  </div>
);

CalendarParticle.propTypes = {
  clearDate: PropTypes.func,
  committedDates: PropTypes.arrayOf(PropTypes.object),
  currentMonth: PropTypes.shape(),
  goToNextMonth: PropTypes.func,
  goToPreviousMonth: PropTypes.func,
  hoverDate: PropTypes.shape({}),
  isRange: PropTypes.bool,
  isRangeToggleVisible: PropTypes.bool,
  isTyping: PropTypes.bool,
  keyboardTempValues: PropTypes.arrayOf(PropTypes.string),
  onInputBlur: PropTypes.func,
  setEndDate: PropTypes.func,
  setHoverDate: PropTypes.func,
  setIsRange: PropTypes.func,
  setIsTyping: PropTypes.func,
  setKeyboardSingleDate: PropTypes.func,
  setRangeDates: PropTypes.func,
  setSingleDate: PropTypes.func,
  setStartDate: PropTypes.func,
  setStep: PropTypes.func,
  step: PropTypes.number,
  onRangeChange: PropTypes.func,
  updateCommittedDates: PropTypes.func,
  locale: PropTypes.string
};

export default compose(
  withProps(({ locale }) => setLocale(locale || 'en')),
  withStateHandlers(initialState, actions),
  lifecycle({
    componentDidUpdate(prevProps) {
      const previousDates = toUtc(prevProps.committedDates).join();
      const nextDates = toUtc(this.props.committedDates).join();

      if (previousDates !== nextDates) {
        const { isRange, committedDates, updateCommittedDates } = this.props;
        let nextDates;

        if (!isRange) {
          // send 1 date for single day
          const isValidStartDate = isDateValid(committedDates, 0);
          nextDates = isValidStartDate ? [committedDates[0]] : [{}];
        } else {
          nextDates = committedDates;
        }

        updateCommittedDates(toUtc(nextDates));
      }
    }
  })
)(CalendarParticle);
