import _extends from "@babel/runtime/helpers/esm/extends";
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
import _inheritsLoose from "@babel/runtime/helpers/esm/inheritsLoose";
var _class, _Calendar;
import _regeneratorRuntime from "@babel/runtime/regenerator";
import React from 'react';
import normalizeWheel from 'normalize-wheel';
import throttle from 'lodash.throttle';
import shallowEqual from 'shallowequal';
import { globalObject } from '@skbkontur/global-object';
import { isInstanceOf } from "../../../lib/isInstanceOf";
import { InternalDate } from "../../../lib/date/InternalDate";
import { rootNode } from "../../../lib/rootNode";
import { cx } from "../../../lib/theming/Emotion";
import { CommonWrapper } from "../../../internal/CommonWrapper";
import { MAX_DATE, MAX_MONTH, MAX_YEAR, MIN_DATE, MIN_MONTH, MIN_YEAR } from "../../../lib/date/constants";
import { ThemeContext } from "../../../lib/theming/ThemeContext";
import { animation } from "../../../lib/animation";
import { isMobile } from "../../../lib/client";
import { createPropsGetter } from "../../../lib/createPropsGetter";
import { InternalDateTransformer } from "../../../lib/date/InternalDateTransformer";
import { themeConfig } from "../config";
import { MonthViewModel } from "../MonthViewModel";
import * as CalendarScrollEvents from "../CalendarScrollEvents";
import { Month } from "../Month";
import { styles } from "../Calendar.styles";
import { create, isGreater, isLess } from "../CalendarDateShape";
import * as CalendarUtils from "../CalendarUtils";
import { CalendarContext } from "../CalendarContext";
import { CalendarDay } from "../CalendarDay";
export var CalendarDataTids = {
  root: 'Calendar__root',
  month: 'MonthView__month',
  dayCell: 'DayCellView__root',
  headerMonth: 'MonthView__headerMonth',
  headerYear: 'MonthView__headerYear'
};

/**
 * Компонент календаря `Calendar` из DatePicker'а помогает выбирать дату с помощью мыши.
 */
export var Calendar = rootNode(_class = (_Calendar = /*#__PURE__*/function (_React$Component) {
  function Calendar(_props) {
    var _this$props$initialYe;
    var _this;
    _this = _React$Component.call(this, _props) || this;
    _this.getProps = createPropsGetter(Calendar.defaultProps);
    _this.animation = animation();
    _this.touchStartY = null;

    /**
     * Прокручивает календарь до переданной даты
     * @public
     */
    _this.scrollToMonth = /*#__PURE__*/function () {
      var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(month, year) {
        var monthNative, minDate, maxDate, minMonth, maxMonth, currentMonth, diffInMonths, maxMonthsToAdd, onEnd, isYearChanges, monthsToPrependCount, monthsToPrepend, monthsToAppendCount, monthsToAppend;
        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) switch (_context.prev = _context.next) {
            case 0:
              monthNative = CalendarUtils.getMonthInNativeFormat(month);
              if (!_this.animation.inProgress()) {
                _context.next = 5;
                break;
              }
              _this.animation.finish();
              // FIXME: Dirty hack to await batched updates
              _context.next = 5;
              return new Promise(function (r) {
                return globalObject.setTimeout(r, 0);
              });
            case 5:
              minDate = _this.getDateInNativeFormat(_this.getProps().minDate);
              maxDate = _this.getDateInNativeFormat(_this.getProps().maxDate);
              if (!(minDate && isGreater(minDate, create(32, monthNative, year)))) {
                _context.next = 11;
                break;
              }
              minMonth = CalendarUtils.getMonthInHumanFormat(minDate.month);
              _this.scrollToMonth(minMonth, minDate.year);
              return _context.abrupt("return");
            case 11:
              if (!(maxDate && isLess(maxDate, create(0, monthNative, year)))) {
                _context.next = 15;
                break;
              }
              maxMonth = CalendarUtils.getMonthInHumanFormat(maxDate.month);
              _this.scrollToMonth(maxMonth, maxDate.year);
              return _context.abrupt("return");
            case 15:
              currentMonth = _this.state.months[1];
              diffInMonths = currentMonth.month + currentMonth.year * 12 - monthNative - year * 12;
              if (!(diffInMonths === 0)) {
                _context.next = 20;
                break;
              }
              _this.scrollTo(0);
              return _context.abrupt("return");
            case 20:
              maxMonthsToAdd = themeConfig(_this.theme).MAX_MONTHS_TO_APPEND_ON_SCROLL;
              onEnd = function onEnd() {
                _this.setState({
                  months: CalendarUtils.getMonths(monthNative, year),
                  scrollPosition: 0
                });
              };
              isYearChanges = function isYearChanges(state) {
                return state.months[1].year !== year &&
                // if diff in months is 2 or less,
                // either year is not changing either months already
                // have right isFirstInYear/isLastInYear flags
                Math.abs(diffInMonths) > 2;
              };

              // If scrolling upwards, prepend maximum maxMonthsToAdd months
              // and scroll to the first month
              if (diffInMonths > 0) {
                monthsToPrependCount = Math.min(Math.abs(diffInMonths) - 1, maxMonthsToAdd);
                monthsToPrepend = Array.from({
                  length: monthsToPrependCount
                }, function (_, index) {
                  return MonthViewModel.create(monthNative + index, year);
                });
                _this.setState(function (state) {
                  var yearChanges = isYearChanges(state);
                  if (yearChanges) {
                    // Mutating here can lead to some unexpected bugs
                    // but we couldn't find any yet
                    state.months[0].isFirstInYear = true;
                    if (monthsToPrepend.length) {
                      // Mutating item here is safe as it was just created
                      monthsToPrepend[monthsToPrepend.length - 1].isLastInYear = true;
                    }
                  }
                  return {
                    months: monthsToPrepend.concat(state.months),
                    scrollPosition: -CalendarUtils.getMonthsHeight(monthsToPrepend, _this.theme)
                  };
                }, function () {
                  var targetPosition = _this.state.months[0].getHeight(_this.theme);
                  _this.scrollTo(targetPosition, onEnd);
                });
              }

              // If scrolling downwards, append maximum maxMonthsToAdd months
              // and scroll to the last but one month
              if (diffInMonths < 0) {
                monthsToAppendCount = Math.min(Math.abs(diffInMonths), maxMonthsToAdd);
                monthsToAppend = Array.from({
                  length: monthsToAppendCount
                }, function (_, index) {
                  return MonthViewModel.create(monthNative + index - monthsToAppendCount + 2, year);
                });
                _this.setState(function (state) {
                  if (isYearChanges(state)) {
                    // Mutating here can lead to some unexpected bugs
                    // but we couldn't find any yet
                    state.months[state.months.length - 1].isLastInYear = true;
                    // Mutating item here is safe as it was just created
                    if (monthsToAppend[0]) {
                      monthsToAppend[0].isFirstInYear = true;
                    }
                  }
                  return {
                    months: state.months.concat(monthsToAppend)
                  };
                }, function () {
                  var targetPosition = -1 * CalendarUtils.getMonthsHeight(_this.state.months.slice(1, -2), _this.theme);
                  _this.scrollTo(targetPosition, onEnd);
                });
              }
            case 25:
            case "end":
              return _context.stop();
          }
        }, _callee);
      }));
      return function (_x, _x2) {
        return _ref.apply(this, arguments);
      };
    }();
    _this.handleMonthChange = function (visibleMonths) {
      var currentMonth = visibleMonths[0];
      var changeInfo = {
        month: CalendarUtils.getMonthInHumanFormat(currentMonth.month),
        year: currentMonth.year
      };
      _this.props.onMonthChange == null || _this.props.onMonthChange(changeInfo);
    };
    _this.getViewModel = function (item) {
      return item[1];
    };
    _this.renderMain = function () {
      var monthsForRender = _this.getVisibleMonths(_this.state);
      var wrapperStyle = {
        height: themeConfig(_this.theme).WRAPPER_HEIGHT
      };
      var props = _this.getProps();
      var context = {
        value: _this.getDateInNativeFormat(props.value),
        minDate: _this.getDateInNativeFormat(props.minDate),
        maxDate: _this.getDateInNativeFormat(props.maxDate),
        isHoliday: props.isHoliday,
        renderDay: props.renderDay,
        today: CalendarUtils.getTodayDate(),
        onDateClick: _this.handleDateClick
      };
      return /*#__PURE__*/(
        React.createElement(CommonWrapper, _extends({
          rootNodeRef: _this.setRootNode
        }, props), /*#__PURE__*/
        React.createElement("div", {
          ref: _this.refRoot,
          "data-tid": CalendarDataTids.root,
          className: cx(styles.root(_this.theme))
        }, /*#__PURE__*/
        React.createElement("div", {
          style: wrapperStyle,
          className: styles.wrapper()
        }, /*#__PURE__*/
        React.createElement(CalendarContext.Provider, {
          value: context
        }, monthsForRender.map(_this.renderMonth, _this))), /*#__PURE__*/
        React.createElement("div", {
          className: styles.separator(_this.theme)
        })))
      );
    };
    _this.refRoot = function (element) {
      if (!_this.root && element) {
        if (isMobile) {
          element.addEventListener('touchstart', _this.handleTouchStart);
          element.addEventListener('touchmove', _this.throttledHandleTouchMove);
        } else {
          element.addEventListener('wheel', _this.handleWheel, {
            passive: false
          });
        }
      }
      if (_this.root && !element) {
        if (isMobile) {
          _this.root.removeEventListener('touchstart', _this.handleTouchStart);
          _this.root.removeEventListener('touchmove', _this.throttledHandleTouchMove);
        } else {
          _this.root.removeEventListener('wheel', _this.handleWheel);
        }
      }
      _this.root = element;
    };
    _this.handleDateClick = function (dateShape) {
      var value = InternalDateTransformer.dateToHumanString(dateShape);
      _this.props.onValueChange == null || _this.props.onValueChange(value);
    };
    _this.handleMonthYearChange = function (monthNative, year) {
      var month = CalendarUtils.getMonthInHumanFormat(monthNative);
      _this.scrollToMonth(month, year);
    };
    _this.executeAnimations = function (pixelY) {
      _this.setState(function (_ref2) {
        var months = _ref2.months,
          scrollPosition = _ref2.scrollPosition;
        var targetPosition = CalendarUtils.calculateScrollPosition(months, scrollPosition, pixelY, _this.theme).scrollPosition;
        return {
          scrollTarget: targetPosition
        };
      }, _this.handleWheelEnd);
      _this.animation.animate(pixelY, function (deltaY) {
        // FIXME: Typescript not resolving setState cb type
        _this.setState(CalendarUtils.applyDelta(deltaY, _this.theme));
      });
      CalendarScrollEvents.emit();
    };
    _this.handleTouchStart = function (event) {
      if (!isInstanceOf(event, globalObject.TouchEvent)) {
        return;
      }
      var clientY = event.targetTouches[0].clientY;
      _this.touchStartY = clientY;
    };
    _this.handleTouchMove = function (event) {
      if (!isInstanceOf(event, globalObject.TouchEvent)) {
        return;
      }
      var clientY = event.changedTouches[0].clientY;
      var deltaY = (_this.touchStartY || 0) - clientY;
      _this.touchStartY = clientY;
      _this.executeAnimations(deltaY);
    };
    _this.throttledHandleTouchMove = throttle(_this.handleTouchMove, 10);
    _this.handleWheel = function (event) {
      if (!isInstanceOf(event, globalObject.WheelEvent)) {
        return;
      }
      event.preventDefault();
      var _normalizeWheel = normalizeWheel(event),
        pixelY = _normalizeWheel.pixelY;
      _this.executeAnimations(pixelY);
    };
    _this.handleWheelEnd = function () {
      if (_this.wheelEndTimeout) {
        globalObject.clearTimeout(_this.wheelEndTimeout);
      }
      _this.wheelEndTimeout = globalObject.setTimeout(_this.scrollToNearestWeek, 300);
    };
    _this.scrollToNearestWeek = function () {
      var _this$state = _this.state,
        scrollTarget = _this$state.scrollTarget,
        scrollDirection = _this$state.scrollDirection;
      var thresholdHeight = themeConfig(_this.theme).MONTH_TITLE_OFFSET_HEIGHT + themeConfig(_this.theme).DAY_HEIGHT;
      if (scrollTarget < thresholdHeight) {
        var targetPosition = 0;
        if (scrollDirection < 0) {
          targetPosition = thresholdHeight;
        }
        _this.setState({
          scrollTarget: targetPosition
        }, function () {
          var amount = scrollTarget - targetPosition;
          _this.animation.animate(amount, function (deltaY) {
            // FIXME: Typescript not resolving setState cb type
            _this.setState(CalendarUtils.applyDelta(deltaY, _this.theme));
          });
        });
      }
    };
    _this.scrollTo = function (pos, onEnd) {
      var scrollAmmount = pos - _this.state.scrollPosition;
      return _this.scrollAmount(scrollAmmount, onEnd);
    };
    _this.scrollAmount = function (scrollAmmount, onEnd) {
      return _this.animation.animate(scrollAmmount, function (deltaY) {
        _this.setState(function (_ref3) {
          var scrollPosition = _ref3.scrollPosition;
          return {
            scrollPosition: scrollPosition + deltaY
          };
        });
      }, onEnd);
    };
    var _this$getProps = _this.getProps(),
      _minDate = _this$getProps.minDate,
      _maxDate = _this$getProps.maxDate,
      _value = _this$getProps.value;
    var minDateShape = _this.getDateInNativeFormat(_minDate);
    var maxDateShape = _this.getDateInNativeFormat(_maxDate);
    var _dateShape = _this.getDateInNativeFormat(_value);
    var today = CalendarUtils.getTodayDate();
    var initialDate = CalendarUtils.getInitialDate({
      today: today,
      date: _dateShape,
      minDate: minDateShape,
      maxDate: maxDateShape
    });
    var initialMonth = _this.props.initialMonth ? CalendarUtils.getMonthInNativeFormat(_this.props.initialMonth) : initialDate.month;
    var initialYear = (_this$props$initialYe = _this.props.initialYear) != null ? _this$props$initialYe : initialDate.year;
    _this.state = {
      scrollPosition: 0,
      months: CalendarUtils.getMonths(initialMonth, initialYear),
      scrollDirection: 1,
      scrollTarget: 0
    };
    return _this;
  }
  _inheritsLoose(Calendar, _React$Component);
  Calendar.formatDate = function formatDate(date, month, year) {
    return new InternalDate().setComponents({
      date: date,
      month: month,
      year: year
    }).toString({
      withPad: true
    });
  };
  var _proto = Calendar.prototype;
  _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
    var _this$props = this.props,
      value = _this$props.value,
      onMonthChange = _this$props.onMonthChange;
    if (value && !shallowEqual(value, prevProps.value)) {
      var date = new InternalDate().parseValue(value).getComponentsLikeNumber();
      this.scrollToMonth(date.month, date.year);
    }
    if (onMonthChange) {
      var visibleMonthsModels = this.getVisibleMonths(this.state).map(this.getViewModel);
      var prevFirstVisibleMonthModels = this.getVisibleMonths(prevState).map(this.getViewModel);
      if (visibleMonthsModels.length > 0 && prevFirstVisibleMonthModels.length > 0) {
        var currentMonth = visibleMonthsModels[0].month;
        var prevCurrentMonth = prevFirstVisibleMonthModels[0].month;
        if (currentMonth !== prevCurrentMonth) {
          this.handleMonthChange(visibleMonthsModels);
        }
      }
    }
  };
  _proto.componentWillUnmount = function componentWillUnmount() {
    if (this.animation.inProgress()) {
      this.animation.cancel();
    }
  };
  _proto.render = function render() {
    var _this2 = this;
    return /*#__PURE__*/React.createElement(ThemeContext.Consumer, null, function (theme) {
      _this2.theme = theme;
      return _this2.renderMain();
    });
  };
  _proto.renderMonth = function renderMonth(_ref4) {
    var top = _ref4[0],
      month = _ref4[1];
    return /*#__PURE__*/React.createElement(Month, {
      key: month.month + '-' + month.year,
      top: top,
      month: month,
      onMonthYearChange: this.handleMonthYearChange
    });
  };
  _proto.getDateInNativeFormat = function getDateInNativeFormat(date) {
    return new InternalDate().parseValue(date).toNativeFormat();
  };
  _proto.getMonthPositions = function getMonthPositions(months, scrollPosition) {
    var positions = [scrollPosition - months[0].getHeight(this.theme)];
    for (var i = 1; i < months.length; i++) {
      var position = positions[i - 1] + months[i - 1].getHeight(this.theme);
      positions.push(position);
    }
    return positions;
  };
  _proto.getVisibleMonths = function getVisibleMonths(state) {
    var _this3 = this;
    var months = state.months,
      scrollPosition = state.scrollPosition;
    var positions = this.getMonthPositions(months, scrollPosition);
    return months.map(function (x, i) {
      return [positions[i], x];
    }).filter(function (_ref5) {
      var top = _ref5[0],
        month = _ref5[1];
      return CalendarUtils.isMonthVisible(top, month, _this3.theme);
    });
  };
  return Calendar;
}(React.Component), _Calendar.__KONTUR_REACT_UI__ = 'Calendar', _Calendar.displayName = 'Calendar', _Calendar.Day = CalendarDay, _Calendar.defaultProps = {
  minDate: _Calendar.formatDate(MIN_DATE, MIN_MONTH, MIN_YEAR),
  maxDate: _Calendar.formatDate(MAX_DATE, MAX_MONTH, MAX_YEAR),
  isHoliday: function isHoliday(_day, isWeekend) {
    return isWeekend;
  }
}, _Calendar)) || _class;