import { isValid, isSameDay, getDate, getMonth, getYear, isSameMonth, isSameYear, getTime, startOfMonth, addDays, addMonths, addYears, getDay, parse, format, startOfYear } from 'date-fns';
import { START_YEAR } from './config';
function getDerivedTimeFromKeyboardEvent(prevValue, event) {
    const now = getTime(Date.now());
    if (typeof prevValue !== 'number')
        return now;
    switch (event.code) {
        case 'ArrowUp':
            return getTime(addDays(prevValue, -7));
        case 'ArrowDown':
            return getTime(addDays(prevValue, 7));
        case 'ArrowRight':
            return getTime(addDays(prevValue, 1));
        case 'ArrowLeft':
            return getTime(addDays(prevValue, -1));
    }
    return now;
}
const matcherMap = {
    date: isSameDay,
    month: isSameMonth,
    year: isSameYear
};
function matchDate(sourceTime, patternTime, type = 'date') {
    const matcher = matcherMap[type];
    if (Array.isArray(sourceTime)) {
        return sourceTime.some((time) => matcher(time, patternTime));
    }
    else {
        return matcher(sourceTime, patternTime);
    }
}
function dateItem(time, monthTs, valueTs, currentTs) {
    let inSpan = false;
    let startOfSpan = false;
    let endOfSpan = false;
    if (Array.isArray(valueTs)) {
        if (valueTs[0] < time && time < valueTs[1]) {
            inSpan = true;
        }
        if (matchDate(valueTs[0], time))
            startOfSpan = true;
        if (matchDate(valueTs[1], time))
            endOfSpan = true;
    }
    return {
        type: 'date',
        dateObject: {
            date: getDate(time),
            month: getMonth(time),
            year: getYear(time)
        },
        inCurrentMonth: isSameMonth(time, monthTs),
        isCurrentDate: matchDate(currentTs, time),
        inSpan,
        startOfSpan,
        endOfSpan,
        selected: valueTs !== null && matchDate(valueTs, time),
        ts: getTime(time)
    };
}
function monthItem(monthTs, valueTs, currentTs) {
    return {
        type: 'month',
        dateObject: {
            month: getMonth(monthTs),
            year: getYear(monthTs)
        },
        isCurrentMonth: isSameMonth(currentTs, monthTs),
        selected: valueTs !== null && matchDate(valueTs, monthTs, 'month'),
        ts: getTime(monthTs)
    };
}
function yearItem(yearTs, valueTs, currentTs) {
    return {
        type: 'year',
        dateObject: {
            year: getYear(yearTs)
        },
        isCurrentYear: isSameYear(currentTs, yearTs),
        selected: valueTs !== null && matchDate(valueTs, yearTs, 'year'),
        ts: getTime(yearTs)
    };
}
/**
 * Given time to display calendar, given the selected time, given current time,
 * return the date array of display time's month.
 */
function dateArray(monthTs, valueTs, currentTs, startDay, strip = false) {
    const displayMonth = getMonth(monthTs);
    // First day of current month
    let displayMonthIterator = getTime(startOfMonth(monthTs));
    // Last day of last month
    let lastMonthIterator = getTime(addDays(displayMonthIterator, -1));
    const calendarDays = [];
    let protectLastMonthDateIsShownFlag = !strip;
    while (getDay(lastMonthIterator) !== startDay ||
        protectLastMonthDateIsShownFlag) {
        calendarDays.unshift(dateItem(lastMonthIterator, monthTs, valueTs, currentTs));
        lastMonthIterator = getTime(addDays(lastMonthIterator, -1));
        protectLastMonthDateIsShownFlag = false;
    }
    while (getMonth(displayMonthIterator) === displayMonth) {
        calendarDays.push(dateItem(displayMonthIterator, monthTs, valueTs, currentTs));
        displayMonthIterator = getTime(addDays(displayMonthIterator, 1));
    }
    const endIndex = strip
        ? calendarDays.length <= 28
            ? 28
            : calendarDays.length <= 35
                ? 35
                : 42
        : 42;
    while (calendarDays.length < endIndex) {
        calendarDays.push(dateItem(displayMonthIterator, monthTs, valueTs, currentTs));
        displayMonthIterator = getTime(addDays(displayMonthIterator, 1));
    }
    return calendarDays;
}
function monthArray(monthTs, valueTs, currentTs) {
    const calendarMonths = [];
    const yearStart = startOfYear(monthTs);
    for (let i = 0; i < 12; i++) {
        calendarMonths.push(monthItem(getTime(addMonths(yearStart, i)), valueTs, currentTs));
    }
    return calendarMonths;
}
function yearArray(yearTs, valueTs, currentTs) {
    const calendarYears = [];
    const time1900 = new Date(START_YEAR, 0, 1);
    // 1900 is not a round time, so we use 1911 as start...
    // new Date(1900, 0, 1)
    // 1899-12-31T15:54:17.000Z
    for (let i = 0; i < 200; i++) {
        calendarYears.push(yearItem(getTime(addYears(time1900, i)), valueTs, currentTs));
    }
    return calendarYears;
}
function strictParse(string, pattern, backup, option) {
    const result = parse(string, pattern, backup, option);
    if (!isValid(result))
        return result;
    else if (format(result, pattern, option) === string)
        return result;
    else
        return new Date(NaN);
}
export { dateArray, monthArray, yearArray, strictParse, getDerivedTimeFromKeyboardEvent };
