import React, { useState, useCallback, useMemo, forwardRef, useEffect, useReducer, useRef } from "react";
import cls from "classnames";
import { useForkRef } from "../../../hooks";
import { YEAR_RENDER_COUNT, getNextDate, isValueUpdate, I18N } from "../utils";
import { useKeyNavigation, useCalendarNavigation, useCalendarDateChange } from "../hooks";
import { CalendarDays, CalendarHeader, CalendarMonths, CalendarQuarters, CalendarYears, EventTooltip } from "../ui";
import { getInitialState, reducer, sizeMap } from "../store/reducer";
import { ActionType, CalendarState } from "../store/types";
import { IsOutOfRange } from "../CalendarBase/CalendarBase.styles";
import { classes } from "../Calendar.tokens";
import { base as viewCSS } from "./variations/_view/base";
import { base as sizeCSS } from "./variations/_size/base";
import { base as eventTooltipSizeCSS } from "./variations/_tooltip-size/base";
import { StyledCalendar, StyledSeparator, StyledWrapper } from "./CalendarDouble.styles";
/**
 * Компонент двойного календаря.
 */ export const calendarDoubleRoot = (Root)=>/*#__PURE__*/ forwardRef(({ className, value: externalValue, focusedDate, min: minDate, max: maxDate, renderFromDate, includeEdgeDates, type = 'Days', eventTooltipOptions, eventList, disabledList, eventMonthList, disabledMonthList, eventQuarterList, disabledQuarterList, eventYearList, disabledYearList, locale = 'ru', stretched, onChangeValue, ...rest }, outerRootRef)=>{
        const [firstValue, secondValue] = useMemo(()=>Array.isArray(externalValue) ? externalValue : [
                externalValue
            ], [
            externalValue
        ]);
        const value = focusedDate || secondValue || firstValue;
        const isSingleMode = !Array.isArray(externalValue);
        const [internalValue, setInternalValue] = useState(externalValue);
        const [prevExternalForSelection, setPrevExternalForSelection] = useState(externalValue);
        if (isValueUpdate(externalValue, prevExternalForSelection)) {
            setInternalValue(externalValue);
            setPrevExternalForSelection(externalValue);
        }
        const [hoveredItem, setHoveredItem] = useState();
        const [prevType, setPrevType] = useState(type);
        const [prevValue, setPrevValue] = useState(value);
        const [outOfRangeKey, setOutOfRangeKey] = useState(0);
        const [eventNodes, setEventNodes] = useState([]);
        const eventTooltipTargetRef = useRef(null);
        const rootRef = useRef(null);
        const innerRootRef = useForkRef(rootRef, outerRootRef);
        const min = minDate && new Date(minDate);
        const max = maxDate && new Date(maxDate);
        if (min) {
            min.setHours(0, 0, 0, 0);
        }
        if (max) {
            max.setHours(23, 59, 59, 999);
        }
        const [state, dispatch] = useReducer(reducer, getInitialState(value, min, renderFromDate, type, true));
        const { date, calendarState, startYear, size } = state;
        const [firstDate, setFirstDate] = useState(date);
        const [secondDate, setSecondDate] = useState(date);
        const { handleNext, handlePrev } = useCalendarNavigation({
            calendarState,
            date,
            dispatch
        });
        const [selectIndexes, onKeyDown, onSelectIndexes, outerRefs, isOutOfRange] = useKeyNavigation({
            isDouble: true,
            size,
            calendarState,
            onNext: handleNext,
            onPrev: handlePrev
        });
        const handleOnChangeValue = useCallback((newDay, dateInfo)=>{
            if (isSingleMode && externalValue == null) {
                setInternalValue(newDay);
            }
            onChangeValue?.(newDay, dateInfo);
        }, [
            onChangeValue,
            isSingleMode,
            externalValue
        ]);
        const { handleOnChangeDay, handleOnChangeMonth, handleOnChangeQuarter, handleOnChangeYear, handleUpdateCalendarState } = useCalendarDateChange({
            type,
            onChangeValue: handleOnChangeValue,
            onSelectIndexes,
            dispatch
        });
        const updateSecondDate = ()=>{
            if (calendarState === CalendarState.Days) {
                const [nextYear, nextMonthIndex] = getNextDate(date.year, date.monthIndex);
                setSecondDate({
                    year: nextYear,
                    monthIndex: nextMonthIndex,
                    day: date.day
                });
            }
            if (calendarState === CalendarState.Months || calendarState === CalendarState.Quarters) {
                setSecondDate({
                    year: date.year + 1,
                    monthIndex: date.monthIndex,
                    day: date.day
                });
            }
            if (calendarState === CalendarState.Years) {
                setSecondDate({
                    year: startYear + YEAR_RENDER_COUNT,
                    monthIndex: date.monthIndex,
                    day: date.day
                });
            }
        };
        const getBoundaryDates = ()=>{
            // NOTE: Если установить индекс дня равным "0", то берется последнее число предыдущего месяца
            if (calendarState === CalendarState.Days) {
                return [
                    new Date(firstDate.year, firstDate.monthIndex, 1),
                    new Date(secondDate.year, secondDate.monthIndex + 1, 0)
                ];
            }
            if (calendarState === CalendarState.Months || calendarState === CalendarState.Quarters) {
                return [
                    new Date(firstDate.year, 0),
                    new Date(secondDate.year, 12, 0)
                ];
            }
            const endVisibleYear = startYear + YEAR_RENDER_COUNT * 2 - 1;
            return [
                new Date(startYear, 0),
                new Date(endVisibleYear, 11)
            ];
        };
        // Изменяем ключ каждый раз как пытаемся перейти на даты которые находятся за пределами min/max ограничений.
        // Это необходимо для того чтобы screen-reader корректно озвучивал уведомление aria-live="assertive"
        // о том что нет доступных дат
        const handleKeyDown = useCallback((event)=>{
            setOutOfRangeKey((previousState)=>Number(!previousState));
            onKeyDown(event);
        }, [
            onKeyDown
        ]);
        const handleTriggerEventTooltip = (structureRef, events)=>{
            if (structureRef?.current && events?.length) {
                eventTooltipTargetRef.current = structureRef.current;
                setEventNodes(events);
            } else if (eventTooltipTargetRef.current !== null) {
                eventTooltipTargetRef.current = null;
                setEventNodes([]);
            }
        };
        const tooltipContent = ()=>{
            const TooltipBodyWrapper = eventTooltipOptions?.bodyWrapper;
            if (!TooltipBodyWrapper) {
                return eventNodes;
            }
            return /*#__PURE__*/ React.createElement(TooltipBodyWrapper, null, eventNodes);
        };
        useEffect(()=>{
            if (prevType !== calendarState) {
                dispatch({
                    type: ActionType.UPDATE_CALENDAR_STATE,
                    payload: {
                        calendarState,
                        size: sizeMap[calendarState].double
                    }
                });
                setPrevType(calendarState);
            }
        }, [
            calendarState
        ]);
        useEffect(()=>{
            if (prevType !== type) {
                dispatch({
                    type: ActionType.UPDATE_CALENDAR_STATE,
                    payload: {
                        calendarState: type,
                        size: sizeMap[type].double
                    }
                });
                setPrevType(type);
            }
        }, [
            type
        ]);
        useEffect(()=>{
            if (value && prevValue && isValueUpdate(value, prevValue) || value && !prevValue) {
                const [minVisibleDate, maxVisibleDate] = getBoundaryDates();
                if (focusedDate || value > maxVisibleDate || value < minVisibleDate) {
                    dispatch({
                        type: ActionType.UPDATE_DATE,
                        payload: {
                            value
                        }
                    });
                }
            }
            if (!value && !prevValue) {
                dispatch({
                    type: ActionType.UPDATE_DATE,
                    payload: {
                        value: renderFromDate || min || new Date()
                    }
                });
            }
            setPrevValue(value);
        }, [
            value
        ]);
        useEffect(()=>{
            setFirstDate(date);
            updateSecondDate();
        }, [
            date,
            calendarState
        ]);
        return /*#__PURE__*/ React.createElement(Root, {
            ref: innerRootRef,
            className: cls(className, {
                [classes.stretched]: stretched
            }),
            "aria-label": I18N.selectDate[locale],
            eventTooltipSize: eventTooltipOptions?.size,
            ...rest
        }, isOutOfRange && /*#__PURE__*/ React.createElement(IsOutOfRange, {
            key: outOfRangeKey,
            "aria-atomic": "true",
            role: "alert",
            "aria-live": "assertive",
            "aria-relevant": "additions"
        }, "Далее нет доступных дат."), /*#__PURE__*/ React.createElement(CalendarHeader, {
            isDouble: true,
            size: rest.size,
            firstDate: firstDate,
            secondDate: secondDate,
            startYear: startYear,
            type: calendarState,
            onPrev: handlePrev,
            onNext: handleNext,
            onUpdateCalendarState: handleUpdateCalendarState,
            locale: locale
        }), /*#__PURE__*/ React.createElement(StyledWrapper, null, calendarState === CalendarState.Days && /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(CalendarDays, {
            value: internalValue,
            date: firstDate,
            min: min,
            max: max,
            eventList: eventList,
            disabledList: disabledList,
            includeEdgeDates: includeEdgeDates,
            hoveredDay: hoveredItem,
            selectIndexes: selectIndexes,
            onChangeDay: handleOnChangeDay,
            onHoverDay: setHoveredItem,
            onSetSelected: onSelectIndexes,
            onTriggerEventTooltip: handleTriggerEventTooltip,
            onKeyDown: handleKeyDown,
            outerRefs: outerRefs,
            locale: locale,
            isDouble: true
        }), /*#__PURE__*/ React.createElement(StyledSeparator, null), /*#__PURE__*/ React.createElement(CalendarDays, {
            value: internalValue,
            date: secondDate,
            min: min,
            max: max,
            eventList: eventList,
            disabledList: disabledList,
            includeEdgeDates: includeEdgeDates,
            hoveredDay: hoveredItem,
            selectIndexes: selectIndexes,
            onChangeDay: handleOnChangeDay,
            onHoverDay: setHoveredItem,
            onSetSelected: onSelectIndexes,
            onTriggerEventTooltip: handleTriggerEventTooltip,
            onKeyDown: handleKeyDown,
            outerRefs: outerRefs,
            locale: locale,
            isDouble: true,
            isSecond: true
        })), calendarState === CalendarState.Months && /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(CalendarMonths, {
            value: internalValue,
            date: firstDate,
            min: min,
            max: max,
            eventList: eventMonthList,
            disabledList: disabledMonthList,
            hoveredMonth: hoveredItem,
            selectIndexes: selectIndexes,
            onChangeMonth: handleOnChangeMonth,
            onSetSelected: onSelectIndexes,
            onHoverMonth: setHoveredItem,
            onTriggerEventTooltip: handleTriggerEventTooltip,
            onKeyDown: handleKeyDown,
            outerRefs: outerRefs,
            locale: locale,
            isDouble: true
        }), /*#__PURE__*/ React.createElement(StyledSeparator, null), /*#__PURE__*/ React.createElement(CalendarMonths, {
            value: internalValue,
            date: secondDate,
            min: min,
            max: max,
            eventList: eventMonthList,
            disabledList: disabledMonthList,
            hoveredMonth: hoveredItem,
            selectIndexes: selectIndexes,
            onChangeMonth: handleOnChangeMonth,
            onSetSelected: onSelectIndexes,
            onHoverMonth: setHoveredItem,
            onTriggerEventTooltip: handleTriggerEventTooltip,
            onKeyDown: handleKeyDown,
            outerRefs: outerRefs,
            locale: locale,
            isDouble: true,
            isSecond: true
        })), calendarState === CalendarState.Quarters && /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(CalendarQuarters, {
            value: internalValue,
            date: firstDate,
            min: min,
            max: max,
            eventList: eventQuarterList,
            disabledList: disabledQuarterList,
            hoveredQuarter: hoveredItem,
            selectIndexes: selectIndexes,
            onChangeQuarter: handleOnChangeQuarter,
            onSetSelected: onSelectIndexes,
            onHoverQuarter: setHoveredItem,
            onTriggerEventTooltip: handleTriggerEventTooltip,
            onKeyDown: handleKeyDown,
            outerRefs: outerRefs,
            isDouble: true
        }), /*#__PURE__*/ React.createElement(StyledSeparator, null), /*#__PURE__*/ React.createElement(CalendarQuarters, {
            value: internalValue,
            date: secondDate,
            min: min,
            max: max,
            eventList: eventQuarterList,
            disabledList: disabledQuarterList,
            hoveredQuarter: hoveredItem,
            selectIndexes: selectIndexes,
            onChangeQuarter: handleOnChangeQuarter,
            onSetSelected: onSelectIndexes,
            onHoverQuarter: setHoveredItem,
            onTriggerEventTooltip: handleTriggerEventTooltip,
            onKeyDown: handleKeyDown,
            outerRefs: outerRefs,
            isDouble: true,
            isSecond: true
        })), calendarState === CalendarState.Years && /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(CalendarYears, {
            value: internalValue,
            date: firstDate,
            startYear: startYear,
            selectIndexes: selectIndexes,
            min: min,
            max: max,
            eventList: eventYearList,
            disabledList: disabledYearList,
            hoveredYear: hoveredItem,
            onChangeYear: handleOnChangeYear,
            onSetSelected: onSelectIndexes,
            onHoverYear: setHoveredItem,
            onTriggerEventTooltip: handleTriggerEventTooltip,
            onKeyDown: handleKeyDown,
            outerRefs: outerRefs,
            isDouble: true
        }), /*#__PURE__*/ React.createElement(StyledSeparator, null), /*#__PURE__*/ React.createElement(CalendarYears, {
            value: internalValue,
            date: secondDate,
            startYear: startYear + YEAR_RENDER_COUNT,
            selectIndexes: selectIndexes,
            min: min,
            max: max,
            eventList: eventYearList,
            disabledList: disabledYearList,
            hoveredYear: hoveredItem,
            onChangeYear: handleOnChangeYear,
            onSetSelected: onSelectIndexes,
            onHoverYear: setHoveredItem,
            onTriggerEventTooltip: handleTriggerEventTooltip,
            onKeyDown: handleKeyDown,
            outerRefs: outerRefs,
            isDouble: true,
            isSecond: true
        }))), eventTooltipTargetRef.current && eventNodes?.length && /*#__PURE__*/ React.createElement(EventTooltip, {
            opened: Boolean(eventNodes?.length),
            target: eventTooltipTargetRef,
            frame: rootRef,
            text: tooltipContent(),
            trigger: "hover",
            ...eventTooltipOptions
        }));
    });
export const calendarDoubleConfig = {
    name: 'CalendarDouble',
    tag: 'div',
    layout: calendarDoubleRoot,
    base: StyledCalendar,
    variations: {
        view: {
            css: viewCSS
        },
        size: {
            css: sizeCSS
        },
        eventTooltipSize: {
            css: eventTooltipSizeCSS
        }
    },
    defaults: {
        view: 'primary',
        size: 'm',
        eventTooltipSizeCSS: 'm'
    }
};
