import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo, useState, forwardRef, useEffect, useCallback } from 'react';
import Text from '../../Text';
import { CALENDAR_TYPES } from '../Input/local';
import { getCalendarTypeFromLocale, getDateOfISOWeek, getWeek, getWeekYear, parseToDate, toUTCDateString } from '../Input/utils';
import { useConfiguration, useConsolidatedRef, useFocusWithin, useI18n } from '../../../hooks';
import { cap } from '../../../utils';
import VisuallyHiddenText from '../../VisuallyHiddenText';
import { getAdjustedUTCDateTime } from '../utils';
import { getDaysOfMonth, getLocalizedDays, getWeeksOfMonth } from './utils';
import { StyledColumnLabel, StyledPick, StyledDay, StyledCalendar, StyledWeekGrid, StyledWeekLabel, StyledWeekNumber } from './Calendar.styles';
const ariaOpts = { day: 'numeric', month: 'long', year: 'numeric' };
const Calendar = forwardRef(function Calendar({ testId, selected, current = selected ? new Date(selected.getTime()) : getAdjustedUTCDateTime(), selectedRange, showHighlight, mode = 'date', showWeekNumber, calendarType: calendar = CALENDAR_TYPES.ISO_8601, onSelect, onFocus, disabled, min: minProp, max: maxProp }, ref) {
    const { locale } = useConfiguration();
    const min = minProp ? parseToDate(minProp) : -Infinity;
    const max = maxProp ? parseToDate(maxProp) : Infinity;
    if (min instanceof Date)
        min.setUTCMinutes(min.getUTCMinutes() - min.getTimezoneOffset());
    if (max instanceof Date)
        max.setUTCMinutes(max.getUTCMinutes() - max.getTimezoneOffset());
    const showWeekLabel = mode === 'week' ? false : showWeekNumber;
    const [focusedDay, setFocusedDay] = useState(current);
    useEffect(() => {
        setFocusedDay(current);
    }, [current.getUTCFullYear(), current.getUTCMonth()]);
    const calendarType = useMemo(() => calendar ?? getCalendarTypeFromLocale(locale), [locale, calendar]);
    const [highlightBoundary, setHighlightBoundary] = useState();
    const onKeyDown = useCallback((e) => {
        if ((mode === 'date'
            ? ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']
            : ['ArrowUp', 'ArrowDown']).includes(e.key)) {
            e.preventDefault();
            const newFocused = new Date(focusedDay);
            switch (e.key) {
                case 'ArrowUp':
                    newFocused.setUTCDate(focusedDay.getUTCDate() - 7);
                    break;
                case 'ArrowDown':
                    newFocused.setUTCDate(focusedDay.getUTCDate() + 7);
                    break;
                case 'ArrowRight':
                    newFocused.setUTCDate(focusedDay.getUTCDate() + 1);
                    break;
                case 'ArrowLeft':
                    newFocused.setUTCDate(focusedDay.getUTCDate() - 1);
                    break;
                default:
            }
            if (newFocused < min || newFocused > max)
                return;
            setFocusedDay(newFocused);
            onFocus?.(newFocused);
            setHighlightBoundary(newFocused);
        }
        if (e.key === 'Enter') {
            e.preventDefault();
            onSelect?.(focusedDay);
        }
    }, [mode, focusedDay, onFocus, onSelect]);
    const containerRef = useConsolidatedRef(ref);
    const hasFocus = useFocusWithin([containerRef]);
    const today = toUTCDateString(getAdjustedUTCDateTime());
    const isDisabled = (day) => {
        const maxDate = new Date(max);
        maxDate.setUTCMinutes(maxDate.getUTCMinutes() - maxDate.getTimezoneOffset());
        return Number(min) > day.getTime() || day.getTime() > maxDate.getTime();
    };
    const isSelected = (day) => {
        return [
            selected && toUTCDateString(selected),
            selectedRange?.startDate && toUTCDateString(selectedRange?.startDate),
            selectedRange?.endDate && toUTCDateString(selectedRange?.endDate)
        ].includes(toUTCDateString(day));
    };
    const isWeekSelected = (day) => {
        return (selected &&
            toUTCDateString(day) ===
                toUTCDateString(getDateOfISOWeek(getWeek(selected, calendarType, 'UTC'), getWeekYear(selected, calendarType, 'UTC'))));
    };
    const isFocused = (day) => {
        return toUTCDateString(day) === toUTCDateString(focusedDay);
    };
    const isWeekFocused = (day) => {
        return (toUTCDateString(day) ===
            toUTCDateString(getDateOfISOWeek(getWeek(focusedDay, calendarType, 'UTC'), getWeekYear(focusedDay, calendarType, 'UTC'))));
    };
    const isInRange = (day) => {
        return (selectedRange &&
            selectedRange.startDate &&
            selectedRange.endDate &&
            day >= selectedRange.startDate &&
            day <= selectedRange.endDate);
    };
    const isHighlighted = (day) => {
        if (!selectedRange || !showHighlight || !highlightBoundary)
            return false;
        const betweenHighlightAndBoundary = (rangeBoundaryType) => {
            const rangeBoundary = selectedRange[rangeBoundaryType];
            if (!rangeBoundary)
                return false;
            return !!(rangeBoundary &&
                ((day >= rangeBoundary && day <= highlightBoundary) ||
                    (day <= rangeBoundary && day >= highlightBoundary)));
        };
        return betweenHighlightAndBoundary('startDate') || betweenHighlightAndBoundary('endDate');
    };
    useEffect(() => {
        if (hasFocus)
            containerRef.current?.querySelector('button[tabindex="0"]')?.focus();
    }, [hasFocus, focusedDay]);
    const days = getDaysOfMonth(current, calendarType ?? CALENDAR_TYPES.ISO_8601).map(day => {
        return (_jsx(StyledDay, { tabIndex: isFocused(day) ? 0 : -1, disabled: disabled || mode === 'week' || isDisabled(day), current: toUTCDateString(day) === today, highlighted: isInRange(day) || isHighlighted(day), "aria-selected": isSelected(day) || undefined, "aria-current": isSelected(day) ? 'date' : undefined, dayInNexOrPrevMonth: day.getUTCMonth() !== current.getUTCMonth(), "aria-label": toUTCDateString(day, Intl.DateTimeFormat(locale, ariaOpts)), onMouseOver: showHighlight ? () => setHighlightBoundary(day) : undefined, onMouseOut: showHighlight ? () => setHighlightBoundary(undefined) : undefined, onClick: () => {
                setFocusedDay(day);
                onFocus?.(day);
                onSelect?.(day);
            }, children: day.getUTCDate().toLocaleString(locale) }, day.getTime()));
    });
    const names = useMemo(() => getLocalizedDays(calendarType, locale), [locale, calendarType]);
    const t = useI18n();
    const displayNames = new Intl.DisplayNames(locale, {
        style: 'long',
        type: 'dateTimeField'
    });
    const displayNamesShort = new Intl.DisplayNames(locale, {
        style: 'short',
        type: 'dateTimeField'
    });
    const weeks = getWeeksOfMonth(current, calendarType).map(({ weekNo, weekYear }) => {
        return (_jsxs(_Fragment, { children: [_jsx(Text, { as: StyledWeekNumber, "aria-hidden": true, children: weekNo.toString() }, weekNo), _jsx(VisuallyHiddenText, { children: `${displayNames.of('weekOfYear')} ${weekNo.toString()} ${weekYear.toString()}` })] }));
    });
    const weeksButtons = getWeeksOfMonth(current, calendarType).map(({ weekNo, weekYear }) => {
        const dateOfISOWeek = getDateOfISOWeek(weekNo, weekYear);
        const lastDayOfWeek = new Date(dateOfISOWeek);
        lastDayOfWeek.setDate(dateOfISOWeek.getDate() + 6);
        return (_jsxs(StyledPick, { tabIndex: isWeekFocused(dateOfISOWeek) ? 0 : -1, disabled: disabled || mode === 'date' || isDisabled(dateOfISOWeek), "aria-selected": isWeekSelected(dateOfISOWeek) || undefined, "aria-label": `${displayNames.of('weekOfYear')} ${weekNo.toString()}: ${toUTCDateString(dateOfISOWeek, Intl.DateTimeFormat(locale, ariaOpts))} - ${toUTCDateString(lastDayOfWeek, Intl.DateTimeFormat(locale, ariaOpts))}`, "aria-current": isWeekFocused(dateOfISOWeek) ? 'date' : undefined, onMouseDown: () => {
                setFocusedDay(dateOfISOWeek);
                onFocus?.(dateOfISOWeek);
                onSelect?.(dateOfISOWeek);
            }, children: [`${cap(displayNames.of('weekOfYear'))} `, weekNo.toLocaleString(locale)] }, weekNo));
    });
    const weekLabel = mode === 'date' ? (_jsxs(_Fragment, { children: [_jsx(Text, { as: StyledWeekLabel, forwardedAs: 'abbr', title: cap(displayNames.of('weekOfYear')), "aria-hidden": true, children: cap(displayNamesShort.of('weekOfYear')) }), _jsx(VisuallyHiddenText, { children: displayNames.of('weekOfYear') })] })) : (_jsx(Text, { as: StyledWeekLabel, children: cap(displayNames.of('weekOfYear')) }));
    const headerLabels = names.map(([name, abbr]) => (_jsxs("th", { role: 'columnheader', children: [_jsx(Text, { as: StyledColumnLabel, forwardedAs: 'abbr', title: name, "aria-hidden": true, children: abbr }, name), _jsx(VisuallyHiddenText, { children: name })] }, name)));
    const splitToWeeks = (resultArray, item, index) => {
        const chunkIndex = Math.floor(index / 7);
        if (!resultArray[chunkIndex]) {
            resultArray[chunkIndex] = [];
        }
        resultArray[chunkIndex].push(item);
        return resultArray;
    };
    const cells = days.reduce(splitToWeeks, []).map((week, index) => {
        const rowHeader = mode === 'week' ? weeksButtons[index] : weeks[index];
        return (
        // eslint-disable-next-line react/no-array-index-key
        _jsxs("tr", { children: [showWeekLabel && _jsx("th", { scope: 'row', children: rowHeader }), week.map((day, dayIndex) => {
                    // eslint-disable-next-line react/no-array-index-key
                    return _jsx("td", { children: day }, `day-${dayIndex}`);
                })] }, `week-${index}`));
    });
    const weeksCells = weeksButtons.map((week, index) => {
        return (
        // eslint-disable-next-line react/no-array-index-key
        _jsx("tr", { children: _jsx("td", { children: week }) }, `week-${index}`));
    });
    const weeksGrid = (_jsxs(StyledWeekGrid, { role: 'grid', onKeyDown: onKeyDown, ref: containerRef, children: [_jsx("thead", { children: _jsx("tr", { children: _jsx("th", { children: weekLabel }) }) }), _jsx("tbody", { children: weeksCells })] }));
    return (_jsxs(_Fragment, { children: [mode === 'week' && weeksGrid, _jsxs(StyledCalendar, { "data-testid": testId, ref: mode === 'date' ? containerRef : undefined, "aria-label": current.toLocaleString(locale, {
                    month: 'long',
                    year: 'numeric',
                    timeZone: 'UTC'
                }), onKeyDown: onKeyDown, "aria-hidden": mode === 'week' ? true : undefined, role: 'grid', children: [_jsx("thead", { children: _jsxs("tr", { children: [showWeekLabel && _jsx("th", { children: weekLabel }), headerLabels] }) }), _jsx("tbody", { children: cells })] }), _jsx(VisuallyHiddenText, { "aria-live": 'polite', role: 'alert', children: t('calendar_assist') })] }));
});
export default Calendar;
//# sourceMappingURL=Calendar.js.map