var _excluded = ["className", "style"];

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

/* eslint-disable no-plusplus */
import React, { forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
import cls from 'clsx';
import { getScrollTop, pick } from '../utils';
import { calcDateNum, cloneDate, cloneDates, compareDay, compareMonth, getDayByOffset, getNextDay, getPrevDay, getToday } from './utils';
import useRefs from '../hooks/use-refs';
import { raf } from '../utils/raf';
import { getRect } from '../hooks/use-rect';
import Toast from '../toast';
import CalendarMonth from './CalendarMonth';
import Button from '../button';
import CalendarHeader from './CalendarHeader';
import Popup from '../popup';
import useSetState from '../hooks/use-set-state';
import useUpdateEffect from '../hooks/use-update-effect';
import ConfigProviderContext from '../config-provider/ConfigProviderContext';
var Calendar = /*#__PURE__*/forwardRef(function (_ref, ref) {
  var className = _ref.className,
      style = _ref.style,
      props = _objectWithoutPropertiesLoose(_ref, _excluded);

  var _useContext = useContext(ConfigProviderContext),
      prefixCls = _useContext.prefixCls,
      createNamespace = _useContext.createNamespace,
      locale = _useContext.locale;

  var _createNamespace = createNamespace('calendar', prefixCls),
      bem = _createNamespace[0];

  var limitDateRange = function limitDateRange(date, minDate, maxDate) {
    if (minDate === void 0) {
      minDate = props.minDate;
    }

    if (maxDate === void 0) {
      maxDate = props.maxDate;
    }

    if (compareDay(date, minDate) === -1) {
      return minDate;
    }

    if (compareDay(date, maxDate) === 1) {
      return maxDate;
    }

    return date;
  };

  var getInitialDate = function getInitialDate(defaultDate) {
    if (defaultDate === void 0) {
      defaultDate = props.defaultDate;
    }

    var type = props.type,
        minDate = props.minDate,
        maxDate = props.maxDate;

    if (defaultDate === null) {
      return defaultDate;
    }

    var now = getToday();

    if (type === 'range') {
      if (!Array.isArray(defaultDate)) {
        defaultDate = [];
      }

      var start = limitDateRange(defaultDate[0] || now, minDate, getPrevDay(maxDate));
      var end = limitDateRange(defaultDate[1] || now, getNextDay(minDate));
      return [start, end];
    }

    if (type === 'multiple') {
      if (Array.isArray(defaultDate)) {
        return defaultDate.map(function (date) {
          return limitDateRange(date);
        });
      }

      return [limitDateRange(now)];
    }

    if (!defaultDate || Array.isArray(defaultDate)) {
      defaultDate = now;
    }

    return limitDateRange(defaultDate);
  };

  var bodyRef = useRef();
  var bodyHeightRef = useRef(0);

  var _useSetState = useSetState({
    subtitle: '',
    currentDate: getInitialDate()
  }),
      state = _useSetState[0],
      updateState = _useSetState[1];

  var _useRefs = useRefs(),
      monthRefs = _useRefs[0],
      setMonthRefs = _useRefs[1];

  var dayOffset = useMemo(function () {
    return props.firstDayOfWeek ? +props.firstDayOfWeek % 7 : 0;
  }, [props.firstDayOfWeek, props.firstDayOfWeek]);
  var months = useMemo(function () {
    var internalMonths = [];
    var cursor = new Date(props.minDate);
    cursor.setDate(1);

    do {
      internalMonths.push(new Date(cursor));
      cursor.setMonth(cursor.getMonth() + 1);
    } while (compareMonth(cursor, props.maxDate) !== 1);

    return internalMonths;
  }, [props.minDate, props.maxDate]);
  var buttonDisabled = useMemo(function () {
    var currentDate = state.currentDate;

    if (currentDate) {
      if (props.type === 'range') {
        return !currentDate[0] || !currentDate[1];
      }

      if (props.type === 'multiple') {
        return !currentDate.length;
      }
    }

    return !currentDate;
  }, [props.type, state.currentDate]); // calculate the position of the elements
  // and find the elements that needs to be rendered

  var onScroll = function onScroll() {
    var top = getScrollTop(bodyRef.current);
    var bottom = top + bodyHeightRef.current;
    var heights = months.map(function (item, index) {
      return monthRefs[index].getHeight();
    });
    var heightSum = heights.reduce(function (a, b) {
      return a + b;
    }, 0); // iOS scroll bounce may exceed the range

    if (bottom > heightSum && top > 0) {
      return;
    }

    var height = 0;
    var currentMonth;
    var visibleRange = [-1, -1];

    for (var i = 0; i < months.length; i++) {
      var month = monthRefs[i];
      var visible = height <= bottom && height + heights[i] >= top;

      if (visible) {
        visibleRange[1] = i;

        if (!currentMonth) {
          currentMonth = month;
          visibleRange[0] = i;
        }

        if (!monthRefs[i].showed) {
          monthRefs[i].showed = true;
          props.onMonthShow == null ? void 0 : props.onMonthShow({
            date: month.date,
            title: month.getTitle()
          });
        }
      }

      height += heights[i];
    }

    months.forEach(function (_, index) {
      var visible = index >= visibleRange[0] - 1 && index <= visibleRange[1] + 1;
      monthRefs[index].setVisible(visible);
    });
    /* istanbul ignore else */

    if (currentMonth) {
      updateState({
        subtitle: currentMonth.getTitle()
      });
    }
  };

  var scrollToDate = function scrollToDate(targetDate) {
    raf(function () {
      months.some(function (month, index) {
        if (compareMonth(month, targetDate) === 0) {
          if (bodyRef.current) {
            monthRefs[index].scrollIntoView(bodyRef.current);
          }

          return true;
        }

        return false;
      });
      onScroll();
    });
  }; // scroll to current month


  var scrollIntoView = function scrollIntoView() {
    if (props.poppable && !props.visible) {
      return;
    }

    var currentDate = state.currentDate;

    if (currentDate) {
      var targetDate = props.type === 'single' ? currentDate : currentDate[0];
      scrollToDate(targetDate);
    } else {
      raf(onScroll);
    }
  };

  var init = function init() {
    if (props.poppable && !props.visible) {
      return;
    }

    raf(function () {
      // add Math.floor to avoid decimal height issues
      // https://github.com/youzan/vant/issues/5640
      bodyHeightRef.current = Math.floor(getRect(bodyRef.current).height);
      scrollIntoView();
    });
  };

  var reset = function reset(date) {
    if (date === void 0) {
      date = getInitialDate();
    }

    updateState({
      currentDate: date
    });
    scrollIntoView();
  };

  var checkRange = function checkRange(date) {
    var maxRange = props.maxRange,
        rangePrompt = props.rangePrompt,
        showRangePrompt = props.showRangePrompt;

    if (maxRange && calcDateNum(date) > maxRange) {
      if (showRangePrompt) {
        Toast.info(rangePrompt || locale.vanCalendar.rangePrompt(+maxRange));
      }

      props.onOverRange == null ? void 0 : props.onOverRange();
      return false;
    }

    return true;
  };

  var onConfirm = function onConfirm() {
    props.onConfirm == null ? void 0 : props.onConfirm(cloneDates(state.currentDate));
  };

  var select = function select(date, complete) {
    var setCurrentDate = function setCurrentDate(current) {
      state.currentDate = current;
      updateState({
        currentDate: current
      });
      props.onSelect == null ? void 0 : props.onSelect(cloneDates(state.currentDate));
    };

    if (complete && props.type === 'range') {
      var valid = checkRange(date);

      if (!valid) {
        // auto selected to max range if showConfirm
        if (props.showConfirm) {
          setCurrentDate([date[0], getDayByOffset(date[0], +props.maxRange - 1)]);
        } else {
          setCurrentDate(date);
        }

        return;
      }
    }

    setCurrentDate(date);

    if (complete && !props.showConfirm) {
      onConfirm();
    }
  };

  var onClickDay = function onClickDay(item) {
    if (props.readonly || !item.date) {
      return;
    }

    var date = item.date;
    var type = props.type;
    var currentDate = state.currentDate;

    if (type === 'range') {
      if (!currentDate) {
        select([date]);
        return;
      }

      var startDay = currentDate[0],
          endDay = currentDate[1];

      if (startDay && !endDay) {
        var compareToStart = compareDay(date, startDay);

        if (compareToStart === 1) {
          select([startDay, date], true);
        } else if (compareToStart === -1) {
          select([date]);
        } else if (props.allowSameDay) {
          select([date, date], true);
        }
      } else {
        select([date]);
      }
    } else if (type === 'multiple') {
      if (!currentDate) {
        select([date]);
        return;
      }

      var selectedIndex;
      var selected = state.currentDate.some(function (dateItem, index) {
        var equal = compareDay(dateItem, date) === 0;

        if (equal) {
          selectedIndex = index;
        }

        return equal;
      });

      if (selected) {
        var _currentDate$splice = currentDate.splice(selectedIndex, 1),
            unselectedDate = _currentDate$splice[0];

        props.onUnselect == null ? void 0 : props.onUnselect(cloneDate(unselectedDate));
        select([].concat(currentDate));
      } else if (props.maxRange && currentDate.length >= props.maxRange) {
        Toast(props.rangePrompt || "\u9009\u62E9\u5929\u6570\u4E0D\u80FD\u8D85\u8FC7 " + props.maxRange + " \u5929");
      } else {
        select([].concat(currentDate, [date]));
      }
    } else {
      select(date, true);
    }
  };

  var renderMonth = function renderMonth(date, index) {
    var showMonthTitle = index !== 0 || !props.showSubtitle;
    return /*#__PURE__*/React.createElement(CalendarMonth, _extends({
      key: index,
      ref: setMonthRefs(index),
      date: date,
      currentDate: state.currentDate,
      showMonthTitle: showMonthTitle,
      firstDayOfWeek: dayOffset
    }, pick(props, ['type', 'color', 'minDate', 'maxDate', 'showMark', 'formatter', 'rowHeight', 'showSubtitle', 'lazyRender', 'allowSameDay', 'topInfoRender', 'bottomInfoRender', 'formatMonthTitle']), {
      onClick: onClickDay
    }));
  };

  var renderFooterButton = function renderFooterButton() {
    if (props.footer) {
      return props.footer;
    }

    if (props.showConfirm) {
      var text = buttonDisabled ? props.confirmDisabledText : props.confirmText;
      return /*#__PURE__*/React.createElement(Button, {
        round: true,
        block: true,
        type: "danger",
        color: props.color,
        className: cls(bem('confirm')),
        disabled: buttonDisabled,
        nativeType: "button",
        onClick: onConfirm
      }, text || locale.vanCalendar.confirm);
    }

    return null;
  };

  var renderFooter = function renderFooter() {
    return /*#__PURE__*/React.createElement("div", {
      className: cls(bem('footer'), {
        'rv-safe-area-bottom': props.safeAreaInsetBottom
      })
    }, renderFooterButton());
  };

  var renderCalendar = function renderCalendar() {
    return /*#__PURE__*/React.createElement("div", {
      className: cls(className, bem()),
      style: style
    }, /*#__PURE__*/React.createElement(CalendarHeader, {
      weekdays: props.weekdays,
      title: props.title,
      subtitle: props.subtitle || state.subtitle,
      showTitle: props.showTitle,
      showSubtitle: props.showSubtitle,
      firstDayOfWeek: dayOffset,
      "onClick-subtitle": function onClickSubtitle(event) {
        props.onClickSubtitle == null ? void 0 : props.onClickSubtitle(event);
      }
    }), /*#__PURE__*/React.createElement("div", {
      ref: bodyRef,
      className: cls(bem('body')),
      onScroll: onScroll
    }, months.map(renderMonth)), renderFooter());
  };

  useEffect(function () {
    init();
  }, [props.visible]);
  useUpdateEffect(function () {
    reset(getInitialDate(state.currentDate));
  }, [props.type, props.minDate, props.maxDate]);
  useUpdateEffect(function () {
    updateState({
      currentDate: props.defaultDate
    });
    scrollIntoView();
  }, [props.defaultDate]);
  useImperativeHandle(ref, function () {
    return {
      reset: reset,
      scrollToDate: scrollToDate
    };
  });

  if (props.poppable) {
    return /*#__PURE__*/React.createElement(Popup, {
      visible: props.visible,
      className: cls(bem('popup')),
      round: props.round,
      position: props.position,
      closeable: props.showTitle || props.showSubtitle,
      closeOnPopstate: props.closeOnPopstate,
      closeOnClickOverlay: props.closeOnClickOverlay,
      onClose: props.onClose,
      onClosed: props.onClosed
    }, renderCalendar());
  }

  return renderCalendar();
});
Calendar.defaultProps = {
  round: true,
  poppable: true,
  showMark: true,
  showTitle: true,
  showConfirm: true,
  showSubtitle: true,
  lazyRender: true,
  closeOnPopstate: true,
  closeOnClickOverlay: true,
  safeAreaInsetBottom: true,
  type: 'single',
  position: 'bottom',
  maxRange: null,
  minDate: getToday(),
  maxDate: function () {
    var now = getToday();
    return new Date(now.getFullYear(), now.getMonth() + 6, now.getDate());
  }(),
  firstDayOfWeek: 0,
  showRangePrompt: true
};
export default Calendar;