import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
import _extends from "@babel/runtime/helpers/esm/extends";
import React, { useCallback, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import omit from 'lodash/omit';
import pick from 'lodash/pick';
import isUndefined from 'lodash/isUndefined';
import { addMonths, compareAsc, isSameDay, isSameMonth } from '../utils/dateUtils';
import * as disabledDateUtils from './disabledDateUtils';
import { FormattedDate } from '../CustomProvider';
import Toolbar from '../DatePicker/Toolbar';
import Calendar from './Calendar';
import { getCalendarDate, getMonthHoverRange, getWeekHoverRange, setTimingMargin } from './utils';
import { createChainedFunction, DATERANGE_DISABLED_TARGET, DateUtils, mergeRefs, useClassNames, useControlled, useCustom } from '../utils';
import { PickerOverlay, pickerDefaultProps, pickerPropTypes, usePublicMethods, useToggleKeyDownEvent, PickerToggle, PickerToggleTrigger, pickTriggerPropKeys, omitTriggerPropKeys, usePickerClassName } from '../Picker';
import partial from 'lodash/partial';
import IconCalendar from '@rsuite/icons/legacy/Calendar';

var defaultProps = _extends({}, pickerDefaultProps, {
  as: 'div',
  classPrefix: 'picker',
  cleanable: true,
  placement: 'bottomStart',
  appearance: 'default',
  format: 'yyyy-MM-dd',
  limitEndYear: 1000,
  placeholder: '',
  showOneCalendar: false,
  character: ' ~ '
});

var DateRangePicker = /*#__PURE__*/React.forwardRef(function (props, ref) {
  var _ref, _merge;

  var Component = props.as,
      classPrefix = props.classPrefix,
      className = props.className,
      cleanable = props.cleanable,
      character = props.character,
      defaultCalendarValue = props.defaultCalendarValue,
      defaultValue = props.defaultValue,
      disabled = props.disabled,
      disabledDateProp = props.disabledDate,
      formatStr = props.format,
      hoverRange = props.hoverRange,
      isoWeek = props.isoWeek,
      limitEndYear = props.limitEndYear,
      overrideLocale = props.locale,
      menuClassName = props.menuClassName,
      menuStyle = props.menuStyle,
      oneTap = props.oneTap,
      placeholder = props.placeholder,
      placement = props.placement,
      ranges = props.ranges,
      renderValue = props.renderValue,
      showOneCalendar = props.showOneCalendar,
      showWeekNumbers = props.showWeekNumbers,
      style = props.style,
      toggleAs = props.toggleAs,
      valueProp = props.value,
      onChange = props.onChange,
      onClean = props.onClean,
      onClose = props.onClose,
      onEnter = props.onEnter,
      onEntered = props.onEntered,
      onExited = props.onExited,
      onOk = props.onOk,
      onOpen = props.onOpen,
      onSelect = props.onSelect,
      rest = _objectWithoutPropertiesLoose(props, ["as", "classPrefix", "className", "cleanable", "character", "defaultCalendarValue", "defaultValue", "disabled", "disabledDate", "format", "hoverRange", "isoWeek", "limitEndYear", "locale", "menuClassName", "menuStyle", "oneTap", "placeholder", "placement", "ranges", "renderValue", "showOneCalendar", "showWeekNumbers", "style", "toggleAs", "value", "onChange", "onClean", "onClose", "onEnter", "onEntered", "onExited", "onOk", "onOpen", "onSelect"]);

  var _useClassNames = useClassNames(classPrefix),
      merge = _useClassNames.merge,
      prefix = _useClassNames.prefix;

  var _useCustom = useCustom('DateRangePicker', overrideLocale),
      locale = _useCustom.locale,
      formatDate = _useCustom.formatDate;

  var rangeFormatStr = "" + formatStr + character + formatStr;

  var _useControlled = useControlled(valueProp, defaultValue !== null && defaultValue !== void 0 ? defaultValue : []),
      value = _useControlled[0],
      setValue = _useControlled[1];
  /**
   * Whether to complete the selection.
   * Everytime selection will change this value. If the value is false, it means that the selection has not been completed.
   *
   * In `oneTap` mode, select action will not change this value, its value should be true always.
   */


  var hasDoneSelect = useRef(true);
  /**
   * The currently selected date range.
   *
   * The time range is selected by two clicks. After the first click,
   * the cursor will store a temporary event date in the process until
   * the second click to determine the end date of the date range.
   *
   */

  var _useState = useState((_ref = valueProp !== null && valueProp !== void 0 ? valueProp : defaultValue) !== null && _ref !== void 0 ? _ref : []),
      selectValue = _useState[0],
      setSelectValue = _useState[1]; // The date of the current hover, used to reduce the calculation of `handleMouseMove`


  var _useState2 = useState([]),
      hoverValue = _useState2[0],
      setHoverValue = _useState2[1]; // The displayed calendar panel is rendered based on this value.


  var _useState3 = useState(getCalendarDate({
    value: valueProp !== null && valueProp !== void 0 ? valueProp : defaultCalendarValue
  })),
      calendarDate = _useState3[0],
      setCalendarDate = _useState3[1];

  var _useState4 = useState(),
      inputState = _useState4[0],
      setInputState = _useState4[1];
  /**
   * When hoverRange is set, `selectValue` will be updated during the hover process,
   * which will cause the `selectValue` to be updated after the first click,
   * so declare a Ref to temporarily store the `selectValue` of the first click.
   */


  var selectRangeValueRef = useRef(null);
  /**
   * Call this function to update the calendar panel rendering benchmark value.
   * If params `value` is not passed, it defaults to [new Date(), addMonth(new Date(), 1)].
   */

  var updateCalendarDate = useCallback(function (value) {
    setCalendarDate(getCalendarDate({
      value: value
    }));
  }, []); // if valueProp changed then update selectValue/hoverValue

  useEffect(function () {
    setSelectValue(valueProp !== null && valueProp !== void 0 ? valueProp : []);
    setHoverValue(valueProp !== null && valueProp !== void 0 ? valueProp : []);
  }, [valueProp]);

  var _useState5 = useState(false),
      isPickerToggleActive = _useState5[0],
      setPickerToggleActive = _useState5[1];

  var rootRef = useRef();
  var overlayRef = useRef();
  var targetRef = useRef();
  var triggerRef = useRef();
  var handleCloseDropdown = useCallback(function () {
    var _triggerRef$current, _triggerRef$current$c;

    (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 ? void 0 : (_triggerRef$current$c = _triggerRef$current.close) === null || _triggerRef$current$c === void 0 ? void 0 : _triggerRef$current$c.call(_triggerRef$current);
  }, []);
  usePublicMethods(ref, {
    triggerRef: triggerRef,
    overlayRef: overlayRef,
    targetRef: targetRef,
    rootRef: rootRef
  });
  var getDisplayString = useCallback(function (nextValue, isPlaintext) {
    var _nextValue, _nextValue2;

    if (nextValue === void 0) {
      nextValue = value;
    }

    var startDate = (_nextValue = nextValue) === null || _nextValue === void 0 ? void 0 : _nextValue[0];
    var endDate = (_nextValue2 = nextValue) === null || _nextValue2 === void 0 ? void 0 : _nextValue2[1];

    if (startDate && endDate) {
      var displayValue = [startDate, endDate].sort(compareAsc);

      if (isPlaintext) {
        return formatDate(displayValue[0], formatStr) + character + formatDate(displayValue[1], formatStr);
      }

      return renderValue ? renderValue(displayValue, formatStr) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FormattedDate, {
        date: displayValue[0],
        formatStr: formatStr
      }), character, /*#__PURE__*/React.createElement(FormattedDate, {
        date: displayValue[1],
        formatStr: formatStr
      }));
    }

    return isPlaintext ? '' : placeholder || rangeFormatStr;
  }, [character, formatDate, formatStr, placeholder, rangeFormatStr, renderValue, value]);
  /**
   * preset hover range
   */

  var getHoverRangeValue = useCallback(function (date) {
    if (!hoverRange) {
      return [];
    }

    var hoverRangeFunc = hoverRange;

    if (hoverRange === 'week') {
      hoverRangeFunc = partial(getWeekHoverRange, isoWeek);
    }

    if (hoverRangeFunc === 'month') {
      hoverRangeFunc = getMonthHoverRange;
    }

    if (typeof hoverRangeFunc !== 'function') {
      return [];
    }

    var hoverValues = hoverRangeFunc(date);
    var isHoverRangeValid = hoverValues instanceof Array && hoverValues.length === 2;

    if (!isHoverRangeValid) {
      return [];
    }

    if (DateUtils.isAfter(hoverValues[0], hoverValues[1])) {
      hoverValues.reverse();
    }

    return hoverValues;
  }, [hoverRange, isoWeek]);
  var handleValueUpdate = useCallback(function (event, nextSelectValue, closeOverlay) {
    if (closeOverlay === void 0) {
      closeOverlay = true;
    }

    var nextValue = !isUndefined(nextSelectValue) ? nextSelectValue : selectValue;
    setSelectValue(nextValue || []);

    if (!isSameDay(nextValue[0], value[0]) || !isSameDay(nextValue[1], value[1])) {
      setValue(nextValue);
      onChange === null || onChange === void 0 ? void 0 : onChange(nextValue, event);
    } // `closeOverlay` default value is `true`


    if (closeOverlay !== false) {
      handleCloseDropdown();
    }
  }, [handleCloseDropdown, onChange, selectValue, setSelectValue, setValue, value]);
  /**
   * Select the date range. If oneTap is not set, you need to click twice to select the start time and end time.
   * The MouseMove event is called between the first click and the second click to update the selection state.
   */

  var handleMouseMove = useCallback(function (date) {
    var hoverRangeValue = getHoverRangeValue(date); // After the first click

    if (!hasDoneSelect.current) {
      // If hoverRange is set, you need to change the value of hoverValue according to the rules
      if (hoverRange) {
        var nextHoverValue = [selectRangeValueRef.current[0], hoverRangeValue[1]];

        if (DateUtils.isBefore(hoverRangeValue[0], selectRangeValueRef.current[0])) {
          nextHoverValue[0] = hoverRangeValue[0];
          nextHoverValue[1] = selectRangeValueRef.current[1];
        }

        setSelectValue(nextHoverValue);
      } else {
        setHoverValue(function (prevHoverValue) {
          return [prevHoverValue[0], date];
        });
      } // Before the first click, if hoverRangeValue has a value, hoverValue needs to be updated

    } else if (hoverRange && hoverRangeValue.length) {
      setHoverValue(hoverRangeValue);
    }
  }, [getHoverRangeValue, hoverRange]);
  var handleSelectValueChange = useCallback(function (date, event) {
    var nextSelectValue = Array.from(hoverValue);
    var hoverRangeValue = getHoverRangeValue(date);
    var noHoverRangeValid = hoverRangeValue.length !== 2; // in `oneTap` mode

    if (hasDoneSelect.current && oneTap) {
      handleValueUpdate(event, noHoverRangeValid ? [setTimingMargin(date), setTimingMargin(date, 'right')] : hoverRangeValue);
      hasDoneSelect.current = false;
      return;
    } // no preset hover range can use


    if (noHoverRangeValid) {
      // start select
      if (hasDoneSelect.current) {
        nextSelectValue = [date];
      } else {
        // finish select
        nextSelectValue[1] = date;
      }
    } else {
      if (!hasDoneSelect.current) {
        nextSelectValue = selectValue;
        selectRangeValueRef.current = null;
      } else {
        nextSelectValue = hoverRangeValue;
        selectRangeValueRef.current = hoverRangeValue;
      }
    } // If user have completed the selection, then sort


    if (nextSelectValue.length === 2 && DateUtils.isAfter(nextSelectValue[0], nextSelectValue[1])) {
      nextSelectValue.reverse();
    }

    setHoverValue(nextSelectValue);
    setSelectValue(nextSelectValue);
    updateCalendarDate(nextSelectValue);
    onSelect === null || onSelect === void 0 ? void 0 : onSelect(date, event);
    hasDoneSelect.current = !hasDoneSelect.current;
  }, [getHoverRangeValue, handleValueUpdate, hoverValue, onSelect, oneTap, selectValue, updateCalendarDate]);
  /**
   * If `selectValue` changed, there will be the following effects.
   * 1. Check if the selection is completed.
   * 2. if the selection is completed, set the temporary `hoverValue` empty.
   */

  useEffect(function () {
    var _selectValue$length;

    var selectValueLength = (_selectValue$length = selectValue === null || selectValue === void 0 ? void 0 : selectValue.length) !== null && _selectValue$length !== void 0 ? _selectValue$length : 0;
    var doneSelected = selectValueLength === 0 || selectValueLength === 2;
    doneSelected && setHoverValue([]);
  }, [selectValue]);
  var handleChangeCalendarDate = useCallback(function (index, date) {
    var nextCalendarDate = Array.from(calendarDate);
    nextCalendarDate[index] = date;
    updateCalendarDate(nextCalendarDate);
  }, [calendarDate, updateCalendarDate]);
  /**
   * Toolbar operation callback function
   */

  var handleShortcutPageDate = useCallback(function (value, closeOverlay, event) {
    handleValueUpdate(event, value, closeOverlay);
  }, [handleValueUpdate]);
  var handleOK = useCallback(function (event) {
    handleValueUpdate(event);
    onOk === null || onOk === void 0 ? void 0 : onOk(selectValue, event);
  }, [handleValueUpdate, onOk, selectValue]);
  var handleClean = useCallback(function (event) {
    updateCalendarDate();
    handleValueUpdate(event, []);
  }, [handleValueUpdate, updateCalendarDate]);
  /**
   * Callback after the input box value is changed.
   */

  var handleInputChange = useCallback(function (value) {
    setInputState('Typing');
    var rangeValue = value.split(character); // isMatch('01/11/2020', 'MM/dd/yyyy') ==> true
    // isMatch('2020-11-01', 'MM/dd/yyyy') ==> false

    if (!DateUtils.isMatch(rangeValue[0], formatStr) || !DateUtils.isMatch(rangeValue[1], formatStr)) {
      setInputState('Error');
      return;
    }

    var startDate = new Date(rangeValue[0]);
    var endDate = new Date(rangeValue[1]);
    var selectValue = [startDate, endDate];

    if (!DateUtils.isValid(startDate) || !DateUtils.isValid(endDate)) {
      setInputState('Error');
      return;
    }

    if (disabledDate(startDate, selectValue, true, DATERANGE_DISABLED_TARGET.CALENDAR)) {
      setInputState('Error');
      return;
    }

    setHoverValue(selectValue);
    setSelectValue(selectValue);
    updateCalendarDate(selectValue);
  }, // eslint-disable-next-line react-hooks/exhaustive-deps
  [character, rangeFormatStr, updateCalendarDate]);
  /**
   * The callback after the enter key is triggered on the input
   */

  var handleInputBlur = useCallback(function (event) {
    if (inputState === 'Typing') {
      handleValueUpdate(event, selectValue);
    }

    setInputState('Initial');
  }, [handleValueUpdate, selectValue, inputState]);
  var handleEnter = useCallback(function () {
    var nextCalendarDate;

    if (value && value.length) {
      var _startDate = value[0],
          endData = value[1];
      nextCalendarDate = [_startDate, isSameMonth(_startDate, endData) ? addMonths(endData, 1) : endData];
    } else {
      nextCalendarDate = getCalendarDate({
        value: defaultCalendarValue
      });
    }

    setSelectValue(value);
    updateCalendarDate(nextCalendarDate);
  }, [defaultCalendarValue, updateCalendarDate, setSelectValue, value]);
  var handleEntered = useCallback(function () {
    onOpen === null || onOpen === void 0 ? void 0 : onOpen();
    setPickerToggleActive(true);
  }, [onOpen]);
  var handleExited = useCallback(function () {
    setPickerToggleActive(false);
    hasDoneSelect.current = true;
    onClose === null || onClose === void 0 ? void 0 : onClose();
  }, [onClose]);
  var disabledDate = useCallback(function (date, selectDate, selectedDone, target) {
    return disabledDateProp === null || disabledDateProp === void 0 ? void 0 : disabledDateProp(date, selectDate, selectedDone, target);
  }, [disabledDateProp]);
  var disabledByBetween = useCallback(function (start, end, type) {
    var selectStartDate = selectValue[0],
        selectEndDate = selectValue[1];
    var nextSelectValue = [selectStartDate, selectEndDate]; // If the date is between the start and the end
    // the button is disabled

    while (DateUtils.isBefore(start, end) || DateUtils.isSameDay(start, end)) {
      if (disabledDate(start, nextSelectValue, hasDoneSelect.current, type)) {
        return true;
      }

      start = DateUtils.addDays(start, 1);
    }

    return false;
  }, [disabledDate, selectValue]);
  var disabledOkButton = useCallback(function () {
    var start = selectValue[0],
        end = selectValue[1];

    if (!start || !end || !hasDoneSelect.current) {
      return true;
    }

    return disabledByBetween(start, end, DATERANGE_DISABLED_TARGET.TOOLBAR_BUTTON_OK);
  }, [disabledByBetween, selectValue]);
  var disabledShortcutButton = useCallback(function (value) {
    if (value === void 0) {
      value = [];
    }

    var _value = value,
        start = _value[0],
        end = _value[1];

    if (!start || !end) {
      return true;
    }

    return disabledByBetween(start, end, DATERANGE_DISABLED_TARGET.TOOLBAR_SHORTCUT);
  }, [disabledByBetween]);
  var handleDisabledDate = useCallback(function (date, values, type) {
    return disabledDate(date, values, hasDoneSelect.current, type);
  }, [disabledDate]);
  var onPickerKeyDown = useToggleKeyDownEvent(_extends({
    triggerRef: triggerRef,
    targetRef: targetRef,
    active: isPickerToggleActive,
    onExit: handleClean
  }, rest));

  var renderDropdownMenu = function renderDropdownMenu(positionProps, speakerRef) {
    var left = positionProps.left,
        top = positionProps.top,
        className = positionProps.className;
    var classes = merge(className, menuClassName, prefix('daterange-menu'));
    var panelClasses = prefix('daterange-panel', {
      'daterange-panel-show-one-calendar': showOneCalendar
    });

    var styles = _extends({}, menuStyle, {
      left: left,
      top: top
    });

    var panelProps = {
      calendarDate: calendarDate,
      disabledDate: handleDisabledDate,
      format: formatStr,
      hoverRangeValue: hoverValue,
      isoWeek: isoWeek,
      limitEndYear: limitEndYear,
      locale: locale,
      showOneCalendar: showOneCalendar,
      showWeekNumbers: showWeekNumbers,
      value: selectValue,
      onChangeCalendarDate: handleChangeCalendarDate,
      onMouseMove: handleMouseMove,
      onSelect: handleSelectValueChange
    };
    return /*#__PURE__*/React.createElement(PickerOverlay, {
      className: classes,
      ref: mergeRefs(overlayRef, speakerRef),
      target: triggerRef,
      style: styles
    }, /*#__PURE__*/React.createElement("div", {
      className: panelClasses
    }, /*#__PURE__*/React.createElement("div", {
      className: prefix('daterange-content')
    }, /*#__PURE__*/React.createElement("div", {
      className: prefix('daterange-header')
    }, getDisplayString(selectValue)), /*#__PURE__*/React.createElement("div", {
      className: prefix("daterange-calendar-" + (showOneCalendar ? 'single' : 'group'))
    }, /*#__PURE__*/React.createElement(Calendar, _extends({
      index: 0
    }, panelProps)), !showOneCalendar && /*#__PURE__*/React.createElement(Calendar, _extends({
      index: 1
    }, panelProps)))), /*#__PURE__*/React.createElement(Toolbar, {
      locale: locale,
      calendarDate: selectValue,
      disabledOkBtn: disabledOkButton,
      disabledShortcut: disabledShortcutButton,
      hideOkBtn: oneTap,
      onOk: handleOK,
      onClickShortcut: handleShortcutPageDate,
      ranges: ranges
    })));
  };

  var hasValue = value && value.length > 1;

  var _usePickerClassName = usePickerClassName(_extends({}, props, {
    name: 'daterange',
    hasValue: hasValue
  })),
      classes = _usePickerClassName[0],
      usedClassNamePropKeys = _usePickerClassName[1];

  return /*#__PURE__*/React.createElement(PickerToggleTrigger, {
    pickerProps: pick(props, pickTriggerPropKeys),
    ref: triggerRef,
    placement: placement,
    onEnter: createChainedFunction(handleEnter, onEnter),
    onEntered: createChainedFunction(handleEntered, onEntered),
    onExited: createChainedFunction(handleExited, onExited),
    speaker: renderDropdownMenu
  }, /*#__PURE__*/React.createElement(Component, {
    ref: rootRef,
    className: merge(className, classes, (_merge = {}, _merge[prefix('error')] = inputState === 'Error', _merge)),
    style: style
  }, /*#__PURE__*/React.createElement(PickerToggle, _extends({}, omit(rest, [].concat(omitTriggerPropKeys, usedClassNamePropKeys, DateUtils.calendarOnlyProps)), {
    as: toggleAs,
    ref: targetRef,
    input: true,
    inputMask: DateUtils.getDateMask(rangeFormatStr),
    inputValue: value ? getDisplayString(value, true) : '',
    inputPlaceholder: typeof placeholder === 'string' && placeholder ? placeholder : rangeFormatStr,
    onInputChange: handleInputChange,
    onInputBlur: handleInputBlur,
    onKeyDown: onPickerKeyDown,
    onClean: createChainedFunction(handleClean, onClean),
    cleanable: cleanable && !disabled,
    hasValue: hasValue,
    active: isPickerToggleActive,
    placement: placement,
    caretComponent: IconCalendar,
    disabled: disabled
  }), getDisplayString())));
});
DateRangePicker.after = disabledDateUtils.after;
DateRangePicker.afterToday = disabledDateUtils.afterToday;
DateRangePicker.allowedDays = disabledDateUtils.allowedDays;
DateRangePicker.allowedMaxDays = disabledDateUtils.allowedMaxDays;
DateRangePicker.allowedRange = disabledDateUtils.allowedRange;
DateRangePicker.before = disabledDateUtils.before;
DateRangePicker.beforeToday = disabledDateUtils.beforeToday;
DateRangePicker.combine = disabledDateUtils.combine;
DateRangePicker.displayName = 'DateRangePicker';
DateRangePicker.defaultProps = defaultProps;
DateRangePicker.propTypes = _extends({}, pickerPropTypes, {
  ranges: PropTypes.array,
  value: PropTypes.arrayOf(PropTypes.instanceOf(Date)),
  defaultValue: PropTypes.arrayOf(PropTypes.instanceOf(Date)),
  defaultCalendarValue: PropTypes.arrayOf(PropTypes.instanceOf(Date)),
  hoverRange: PropTypes.oneOfType([PropTypes.oneOf(['week', 'month']), PropTypes.func]),
  format: PropTypes.string,
  isoWeek: PropTypes.bool,
  oneTap: PropTypes.bool,
  limitEndYear: PropTypes.number,
  showWeekNumbers: PropTypes.bool,
  onChange: PropTypes.func,
  onOk: PropTypes.func,
  disabledDate: PropTypes.func,
  onSelect: PropTypes.func,
  showOneCalendar: PropTypes.bool
});
export default DateRangePicker;