import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
import _extends from "@babel/runtime/helpers/esm/extends";
import React, { useCallback, useMemo, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import mapValues from 'lodash/mapValues';
import pick from 'lodash/pick';
import omit from 'lodash/omit';
import IconCalendar from '@rsuite/icons/legacy/Calendar';
import IconClockO from '@rsuite/icons/legacy/ClockO';
import { Calendar, CalendarState } from '../Calendar';
import Toolbar from './Toolbar';
import { composeFunctions, createChainedFunction, DateUtils, mergeRefs, TimeZone, useClassNames, useControlled, useCustom } from '../utils';
import { PickerOverlay, pickerDefaultProps, pickerPropTypes, PickerToggle, PickerToggleTrigger, pickTriggerPropKeys, omitTriggerPropKeys, usePickerClassName, usePublicMethods, useToggleKeyDownEvent } from '../Picker';
import { useCalendarState } from './utils';

var defaultProps = _extends({}, pickerDefaultProps, {
  as: 'div',
  classPrefix: 'picker',
  format: 'yyyy-MM-dd',
  limitEndYear: 1000,
  placeholder: ''
});

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

  var Component = props.as,
      className = props.className,
      classPrefix = props.classPrefix,
      calendarDefaultDate = props.calendarDefaultDate,
      cleanable = props.cleanable,
      defaultValue = props.defaultValue,
      disabled = props.disabled,
      formatStr = props.format,
      inline = props.inline,
      isoWeek = props.isoWeek,
      limitEndYear = props.limitEndYear,
      overrideLocale = props.locale,
      menuClassName = props.menuClassName,
      placement = props.placement,
      oneTap = props.oneTap,
      placeholder = props.placeholder,
      ranges = props.ranges,
      valueProp = props.value,
      showMeridian = props.showMeridian,
      showWeekNumbers = props.showWeekNumbers,
      style = props.style,
      timeZone = props.timeZone,
      toggleAs = props.toggleAs,
      disabledDateProp = props.disabledDate,
      renderValue = props.renderValue,
      onChange = props.onChange,
      onChangeCalendarDate = props.onChangeCalendarDate,
      onClean = props.onClean,
      onClose = props.onClose,
      onEntered = props.onEntered,
      onExited = props.onExited,
      onNextMonth = props.onNextMonth,
      onOk = props.onOk,
      onOpen = props.onOpen,
      onPrevMonth = props.onPrevMonth,
      onSelect = props.onSelect,
      onToggleMonthDropdown = props.onToggleMonthDropdown,
      onToggleTimeDropdown = props.onToggleTimeDropdown,
      rest = _objectWithoutPropertiesLoose(props, ["as", "className", "classPrefix", "calendarDefaultDate", "cleanable", "defaultValue", "disabled", "format", "inline", "isoWeek", "limitEndYear", "locale", "menuClassName", "placement", "oneTap", "placeholder", "ranges", "value", "showMeridian", "showWeekNumbers", "style", "timeZone", "toggleAs", "disabledDate", "renderValue", "onChange", "onChangeCalendarDate", "onClean", "onClose", "onEntered", "onExited", "onNextMonth", "onOk", "onOpen", "onPrevMonth", "onSelect", "onToggleMonthDropdown", "onToggleTimeDropdown"]);

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

  var _useClassNames = useClassNames(classPrefix),
      merge = _useClassNames.merge,
      prefix = _useClassNames.prefix,
      withClassPrefix = _useClassNames.withClassPrefix; // Format the value according to the time zone.


  var formatValue = useCallback(function (v) {
    return TimeZone.toTimeZone(v, timeZone);
  }, [timeZone]);

  var _useControlled = useControlled(valueProp, defaultValue, formatValue),
      value = _useControlled[0],
      setValue = _useControlled[1];

  var _useState = useState(TimeZone.toTimeZone((_ref = value !== null && value !== void 0 ? value : calendarDefaultDate) !== null && _ref !== void 0 ? _ref : new Date(), timeZone)),
      pageDate = _useState[0],
      setPageDate = _useState[1];

  var _useState2 = useState(),
      inputState = _useState2[0],
      setInputState = _useState2[1];

  var _useCalendarState = useCalendarState(),
      calendarState = _useCalendarState.calendarState,
      reset = _useCalendarState.reset,
      openMonth = _useCalendarState.openMonth,
      openTime = _useCalendarState.openTime;

  var _useState3 = useState(false),
      active = _useState3[0],
      setActive = _useState3[1];

  var triggerRef = useRef();
  var rootRef = useRef();
  var targetRef = useRef();
  var overlayRef = useRef();
  usePublicMethods(ref, {
    rootRef: rootRef,
    triggerRef: triggerRef,
    overlayRef: overlayRef,
    targetRef: targetRef
  });
  var getLocalPageDate = useCallback(function (date) {
    if (date === void 0) {
      date = pageDate;
    }

    return TimeZone.toLocalTimeZone(date, timeZone);
  }, [pageDate, timeZone]);
  /**
   * Switch to the callback triggered after the next month.
   */

  var handleMoveForward = useCallback(function (nextPageDate) {
    setPageDate(nextPageDate);
    nextPageDate = getLocalPageDate(nextPageDate);
    onNextMonth === null || onNextMonth === void 0 ? void 0 : onNextMonth(nextPageDate);
    onChangeCalendarDate === null || onChangeCalendarDate === void 0 ? void 0 : onChangeCalendarDate(nextPageDate);
  }, [getLocalPageDate, onChangeCalendarDate, onNextMonth]);
  /**
   * Switch to the callback triggered after the previous month.
   */

  var handleMoveBackward = useCallback(function (nextPageDate) {
    setPageDate(nextPageDate);
    nextPageDate = getLocalPageDate(nextPageDate);
    onPrevMonth === null || onPrevMonth === void 0 ? void 0 : onPrevMonth(nextPageDate);
    onChangeCalendarDate === null || onChangeCalendarDate === void 0 ? void 0 : onChangeCalendarDate(nextPageDate);
  }, [getLocalPageDate, onChangeCalendarDate, onPrevMonth]);
  /**
   * The callback triggered when the date changes.
   */

  var handleDateChange = useCallback(function (nextValue, event) {
    nextValue = TimeZone.toLocalTimeZone(nextValue, timeZone);
    onSelect === null || onSelect === void 0 ? void 0 : onSelect(nextValue, event);
    onChangeCalendarDate === null || onChangeCalendarDate === void 0 ? void 0 : onChangeCalendarDate(nextValue, event);
  }, [onChangeCalendarDate, onSelect, timeZone]);
  /**
   *  A callback triggered when the date on the calendar changes.
   */

  var handleChangePageDate = useCallback(function (nextPageDate) {
    setPageDate(nextPageDate);
    reset();
    handleDateChange(nextPageDate);
  }, [handleDateChange, reset]);
  /**
   *  A callback triggered when the time on the calendar changes.
   */

  var handleChangePageTime = useCallback(function (nextPageTime) {
    setPageDate(nextPageTime);
    handleDateChange(nextPageTime);
  }, [handleDateChange]);
  var handleClose = 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);
  }, []);
  /**
   * The callback triggered when PM/AM is switched.
   */

  var handleToggleMeridian = useCallback(function () {
    var hours = DateUtils.getHours(pageDate);
    var nextHours = hours >= 12 ? hours - 12 : hours + 12;
    var nextDate = DateUtils.setHours(pageDate, nextHours);
    setPageDate(nextDate);
  }, [pageDate]);
  var updateValue = useCallback(function (event, nextPageDate, closeOverlay) {
    if (closeOverlay === void 0) {
      closeOverlay = true;
    }

    var nextValue = typeof nextPageDate !== 'undefined' ? nextPageDate : pageDate;
    setPageDate(nextValue || new Date());
    setValue(nextValue);

    if (nextValue !== value || !DateUtils.isSameDay(nextValue, value)) {
      onChange === null || onChange === void 0 ? void 0 : onChange(getLocalPageDate(nextValue), event);
    } // `closeOverlay` default value is `true`


    if (closeOverlay !== false) {
      handleClose();
    }
  }, [getLocalPageDate, handleClose, onChange, pageDate, setValue, value]);
  /**
   * The callback triggered after the date in the shortcut area is clicked.
   */

  var handleShortcutPageDate = useCallback(function (value, closeOverlay, event) {
    updateValue(event, value, closeOverlay);
    handleDateChange(value, event);
  }, [handleDateChange, updateValue]);
  /**
   * The callback triggered after clicking the OK button.
   */

  var handleOK = useCallback(function (event) {
    updateValue(event);
    onOk === null || onOk === void 0 ? void 0 : onOk(getLocalPageDate(), event);
  }, [getLocalPageDate, updateValue, onOk]);
  /**
   * Toggle month selection panel
   */

  var handleMonthDropdown = useCallback(function () {
    if (calendarState === CalendarState.DROP_MONTH) {
      reset();
    } else {
      openMonth();
    }

    onToggleMonthDropdown === null || onToggleMonthDropdown === void 0 ? void 0 : onToggleMonthDropdown(calendarState !== CalendarState.DROP_MONTH);
  }, [calendarState, onToggleMonthDropdown, openMonth, reset]);
  /**
   * Switch time selection panel
   */

  var handleTimeDropdown = useCallback(function () {
    if (calendarState === CalendarState.DROP_TIME) {
      reset();
    } else {
      openTime();
    }

    onToggleTimeDropdown === null || onToggleTimeDropdown === void 0 ? void 0 : onToggleTimeDropdown(calendarState !== CalendarState.DROP_TIME);
  }, [calendarState, onToggleTimeDropdown, openTime, reset]);
  /**
   * Callback after clicking the clear button.
   */

  var handleClean = useCallback(function (event) {
    setPageDate(TimeZone.toTimeZone(new Date(), timeZone));
    updateValue(event, null);
  }, [updateValue, timeZone]);
  /**
   * Handle keyboard events.
   */

  var onPickerKeyDown = useToggleKeyDownEvent(_extends({
    triggerRef: triggerRef,
    targetRef: targetRef,
    active: active,
    onExit: handleClean
  }, rest));
  /**
   * Callback after the date is selected.
   */

  var handleSelect = useCallback(function (nextValue, event, updatableValue) {
    if (updatableValue === void 0) {
      updatableValue = true;
    }

    setPageDate( // Determine whether the current value contains time, if not, use pageDate.
    DateUtils.shouldTime(formatStr) ? nextValue : composeFunctions(function (d) {
      return DateUtils.setHours(d, DateUtils.getHours(pageDate));
    }, function (d) {
      return DateUtils.setMinutes(d, DateUtils.getMinutes(pageDate));
    }, function (d) {
      return DateUtils.setSeconds(d, DateUtils.getSeconds(pageDate));
    })(nextValue));
    handleDateChange(nextValue);

    if (oneTap && updatableValue) {
      updateValue(event, nextValue);
    }
  }, [formatStr, handleDateChange, oneTap, pageDate, updateValue]);
  var disabledDate = useCallback(function (date) {
    return disabledDateProp === null || disabledDateProp === void 0 ? void 0 : disabledDateProp(TimeZone.toLocalTimeZone(date, timeZone));
  }, [disabledDateProp, timeZone]);
  /**
   * Callback after the input box value is changed.
   */

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

    if (!DateUtils.isMatch(value, formatStr, {
      locale: locale.dateLocale
    })) {
      setInputState('Error');
      return;
    }

    var date = parseDate(value, formatStr); // If only the time is included in the characters, it will default to today.

    if (DateUtils.shouldOnlyTime(formatStr)) {
      date = new Date(DateUtils.format(new Date(), 'yyyy-MM-dd') + " " + value);
    }

    if (!DateUtils.isValid(date)) {
      setInputState('Error');
      return;
    }

    if (disabledDate(date)) {
      setInputState('Error');
      return;
    }

    handleSelect(date, event, false);
  }, [formatStr, locale, parseDate, disabledDate, handleSelect]);
  /**
   * The callback after the enter key is triggered on the input
   */

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

    setInputState('Initial');
  }, [inputState, pageDate, updateValue]);
  var handleEntered = useCallback(function () {
    onOpen === null || onOpen === void 0 ? void 0 : onOpen();
    setActive(true);
  }, [onOpen]);
  var handleExited = useCallback(function () {
    onClose === null || onClose === void 0 ? void 0 : onClose();
    reset();
    setActive(false);
  }, [onClose, reset]); // Check whether the time is within the time range of the shortcut option in the toolbar.

  var disabledToolbarHandle = useCallback(function (date) {
    var _disabledDateProp;

    var localTimeZoneDate = TimeZone.toLocalTimeZone(date, timeZone);
    var allowDate = (_disabledDateProp = disabledDateProp === null || disabledDateProp === void 0 ? void 0 : disabledDateProp(localTimeZoneDate)) !== null && _disabledDateProp !== void 0 ? _disabledDateProp : false;
    var allowTime = DateUtils.disabledTime(props, localTimeZoneDate);
    return allowDate || allowTime;
  }, [disabledDateProp, props, timeZone]);
  var calendarProps = useMemo(function () {
    return mapValues(pick(props, DateUtils.calendarOnlyProps), function (disabledOrHiddenTimeFunc) {
      return function (next, date) {
        return disabledOrHiddenTimeFunc(next, TimeZone.toLocalTimeZone(date, timeZone));
      };
    });
  }, [props, timeZone]);
  var calendar = /*#__PURE__*/React.createElement(Calendar, _extends({}, calendarProps, {
    locale: locale,
    showWeekNumbers: showWeekNumbers,
    showMeridian: showMeridian,
    disabledDate: disabledDate,
    limitEndYear: limitEndYear,
    format: formatStr,
    timeZone: timeZone,
    isoWeek: isoWeek,
    calendarState: calendarState,
    pageDate: pageDate,
    onMoveForward: handleMoveForward,
    onMoveBackward: handleMoveBackward,
    onSelect: handleSelect,
    onToggleMonthDropdown: handleMonthDropdown,
    onToggleTimeDropdown: handleTimeDropdown,
    onChangePageDate: handleChangePageDate,
    onChangePageTime: handleChangePageTime,
    onToggleMeridian: handleToggleMeridian
  }));

  var renderDropdownMenu = function renderDropdownMenu(positionProps, speakerRef) {
    var left = positionProps.left,
        top = positionProps.top,
        className = positionProps.className;
    var classes = merge(menuClassName, className, prefix('date-menu'));
    var styles = {
      left: left,
      top: top
    };
    return /*#__PURE__*/React.createElement(PickerOverlay, {
      className: classes,
      ref: mergeRefs(overlayRef, speakerRef),
      style: styles,
      target: triggerRef
    }, calendar, /*#__PURE__*/React.createElement(Toolbar, {
      locale: locale,
      timeZone: timeZone,
      ranges: ranges,
      pageDate: pageDate,
      disabledOkBtn: disabledToolbarHandle,
      disabledShortcut: disabledToolbarHandle,
      onClickShortcut: handleShortcutPageDate,
      onOk: handleOK,
      hideOkBtn: oneTap
    }));
  };

  var hasValue = !!value;

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

  var renderDate = useCallback(function () {
    var _renderValue;

    if (!value) {
      return placeholder || formatStr;
    }

    return (_renderValue = renderValue === null || renderValue === void 0 ? void 0 : renderValue(value, formatStr)) !== null && _renderValue !== void 0 ? _renderValue : formatDate(value, formatStr);
  }, [formatStr, formatDate, placeholder, renderValue, value]);
  var caretComponent = useMemo(function () {
    return DateUtils.shouldOnlyTime(formatStr) ? IconClockO : IconCalendar;
  }, [formatStr]);

  if (inline) {
    return /*#__PURE__*/React.createElement(Component, {
      ref: rootRef,
      className: merge(className, withClassPrefix('date-inline'))
    }, calendar);
  }

  return /*#__PURE__*/React.createElement(PickerToggleTrigger, {
    trigger: "active",
    pickerProps: pick(props, pickTriggerPropKeys),
    ref: triggerRef,
    placement: placement,
    onEntered: createChainedFunction(handleEntered, onEntered),
    onExited: createChainedFunction(handleExited, onExited),
    speaker: renderDropdownMenu
  }, /*#__PURE__*/React.createElement(Component, {
    className: merge(className, classes),
    style: style,
    ref: rootRef
  }, /*#__PURE__*/React.createElement(PickerToggle, _extends({}, omit(rest, [].concat(omitTriggerPropKeys, usedClassNamePropKeys, DateUtils.calendarOnlyProps)), {
    className: prefix({
      error: inputState === 'Error'
    }),
    as: toggleAs,
    ref: targetRef,
    input: true,
    inputValue: value ? formatDate(value, formatStr) : '',
    inputPlaceholder: typeof placeholder === 'string' && placeholder ? placeholder : formatStr,
    inputMask: DateUtils.getDateMask(formatStr),
    onInputChange: handleInputChange,
    onInputBlur: handleInputBlur,
    onKeyDown: onPickerKeyDown,
    onClean: createChainedFunction(handleClean, onClean),
    cleanable: cleanable && !disabled,
    hasValue: hasValue,
    active: active,
    placement: placement,
    disabled: disabled,
    caretComponent: caretComponent
  }), renderDate())));
});
DatePicker.displayName = 'DatePicker';
DatePicker.defaultProps = defaultProps;
DatePicker.propTypes = _extends({}, pickerPropTypes, {
  calendarDefaultDate: PropTypes.instanceOf(Date),
  defaultValue: PropTypes.instanceOf(Date),
  disabledDate: PropTypes.func,
  disabledHours: PropTypes.func,
  disabledMinutes: PropTypes.func,
  disabledSeconds: PropTypes.func,
  format: PropTypes.string,
  hideHours: PropTypes.func,
  hideMinutes: PropTypes.func,
  hideSeconds: PropTypes.func,
  inline: PropTypes.bool,
  isoWeek: PropTypes.bool,
  limitEndYear: PropTypes.number,
  onChange: PropTypes.func,
  onChangeCalendarDate: PropTypes.func,
  onNextMonth: PropTypes.func,
  onOk: PropTypes.func,
  onPrevMonth: PropTypes.func,
  onSelect: PropTypes.func,
  onToggleMonthDropdown: PropTypes.func,
  onToggleTimeDropdown: PropTypes.func,
  oneTap: PropTypes.bool,
  panelContainerRef: PropTypes.any,
  ranges: PropTypes.array,
  showMeridian: PropTypes.bool,
  showWeekNumbers: PropTypes.bool,
  timeZone: PropTypes.string,
  value: PropTypes.instanceOf(Date)
});
export default DatePicker;