"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useRangeDatepicker = void 0;
const date_fns_1 = require("date-fns");
const react_1 = require("react");
const react_day_picker_1 = require("react-day-picker");
const focus_1 = require("../../../utils/helpers/focus");
const i18n_hooks_1 = require("../../../utils/i18n/i18n.hooks");
const Date_locale_1 = require("../../Date.locale");
const date_utils_1 = require("../../date-utils");
const getValidationMessage = (from = {}, to = {}) => ({
    from: Object.assign({ isDisabled: false, isWeekend: false, isEmpty: false, isInvalid: false, isBefore: false, isAfter: false, isValidDate: true }, from),
    to: Object.assign({ isDisabled: false, isWeekend: false, isEmpty: false, isInvalid: false, isBefore: false, isAfter: false, isBeforeFrom: false, isValidDate: true }, to),
});
const RANGE = {
    FROM: "FROM",
    TO: "TO",
};
const fromValidation = (day, opt) => {
    const isBefore = (opt === null || opt === void 0 ? void 0 : opt.fromDate) && day && (0, date_fns_1.differenceInCalendarDays)(opt === null || opt === void 0 ? void 0 : opt.fromDate, day) > 0;
    const isAfter = (opt === null || opt === void 0 ? void 0 : opt.toDate) && day && (0, date_fns_1.differenceInCalendarDays)(day, opt === null || opt === void 0 ? void 0 : opt.toDate) > 0;
    if ((0, date_utils_1.isValidDate)(day) &&
        !((opt === null || opt === void 0 ? void 0 : opt.disableWeekends) && (0, date_fns_1.isWeekend)(day)) &&
        !((opt === null || opt === void 0 ? void 0 : opt.disabled) && (0, react_day_picker_1.dateMatchModifiers)(day, opt.disabled))) {
        return {
            isValidDate: false,
            isInvalid: !(0, date_utils_1.isValidDate)(day),
            isWeekend: (opt === null || opt === void 0 ? void 0 : opt.disableWeekends) && (0, date_fns_1.isWeekend)(day),
            isDisabled: (opt === null || opt === void 0 ? void 0 : opt.disabled) && (0, react_day_picker_1.dateMatchModifiers)(day, opt.disabled),
            isBefore,
            isAfter,
        };
    }
    if (isBefore || isAfter) {
        return {
            isValidDate: false,
            isBefore,
            isAfter,
        };
    }
};
const toValidation = (day, from, opt) => {
    var _a;
    const isBefore = (opt === null || opt === void 0 ? void 0 : opt.fromDate) && day && (0, date_fns_1.differenceInCalendarDays)(opt === null || opt === void 0 ? void 0 : opt.fromDate, day) > 0;
    const isAfter = (opt === null || opt === void 0 ? void 0 : opt.toDate) && day && (0, date_fns_1.differenceInCalendarDays)(day, opt === null || opt === void 0 ? void 0 : opt.toDate) > 0;
    const isBeforeFrom = (_a = (from && (0, date_fns_1.differenceInCalendarDays)(from, day) > 0)) !== null && _a !== void 0 ? _a : false;
    if ((0, date_utils_1.isValidDate)(day) &&
        !((opt === null || opt === void 0 ? void 0 : opt.disableWeekends) && (0, date_fns_1.isWeekend)(day)) &&
        !((opt === null || opt === void 0 ? void 0 : opt.disabled) && (0, react_day_picker_1.dateMatchModifiers)(day, opt.disabled))) {
        return {
            isValidDate: false,
            isInvalid: !(0, date_utils_1.isValidDate)(day),
            isWeekend: (opt === null || opt === void 0 ? void 0 : opt.disableWeekends) && (0, date_fns_1.isWeekend)(day),
            isDisabled: (opt === null || opt === void 0 ? void 0 : opt.disabled) && (0, react_day_picker_1.dateMatchModifiers)(day, opt.disabled),
            isBefore,
            isAfter,
            isBeforeFrom,
        };
    }
    if (isBefore || isAfter || isBeforeFrom) {
        return {
            isValidDate: false,
            isBefore,
            isAfter,
            isBeforeFrom,
        };
    }
};
const initialValidation = (range, opt) => {
    if (!(range === null || range === void 0 ? void 0 : range.from)) {
        return getValidationMessage({ isEmpty: true, isValidDate: false }, { isEmpty: true, isValidDate: false });
    }
    const fromVal = fromValidation(range.from, opt);
    const toVal = range.to
        ? toValidation(range.to, range.from, opt)
        : { isEmpty: true, isValidDate: false };
    return getValidationMessage(Object.assign({}, fromVal), Object.assign({}, toVal));
};
/**
 *
 * @see 🏷️ {@link UseRangeDatepickerOptions}
 * @see 🏷️ {@link UseRangeDatepickerValue}
 * @see 🏷️ {@link RangeValidationT}
 * @example
 * const { datepickerProps, fromInputProps, toInputProps } = useRangeDatepicker({
 *   fromDate: new Date("Aug 23 2019"),
 *   onRangeChange: console.log,
 *   onValidate: console.log,
 * });
 */
const useRangeDatepicker = (opt = {}) => {
    const { locale: _locale, defaultSelected: _defaultSelected, today = new Date(), fromDate, toDate, disabled, disableWeekends, onRangeChange, inputFormat, onValidate, defaultMonth, allowTwoDigitYear = true, } = opt;
    const [anchorRef, setAnchorRef] = (0, react_1.useState)(null);
    const localeFromProvider = (0, i18n_hooks_1.useDateLocale)();
    const locale = _locale ? (0, Date_locale_1.getLocaleFromString)(_locale) : localeFromProvider;
    const [defaultSelected, setDefaultSelected] = (0, react_1.useState)(_defaultSelected);
    // Initialize states
    const [month, setMonth] = (0, react_1.useState)((defaultSelected === null || defaultSelected === void 0 ? void 0 : defaultSelected.from) || (defaultSelected === null || defaultSelected === void 0 ? void 0 : defaultSelected.to) || defaultMonth || today);
    const [selectedRange, setSelectedRange] = (0, react_1.useState)(defaultSelected !== null && defaultSelected !== void 0 ? defaultSelected : { from: undefined, to: undefined });
    const [fromInputValue, setFromInputValue] = (0, react_1.useState)((defaultSelected === null || defaultSelected === void 0 ? void 0 : defaultSelected.from)
        ? (0, date_utils_1.formatDateForInput)(defaultSelected.from, locale, "date", inputFormat)
        : "");
    const [toInputValue, setToInputValue] = (0, react_1.useState)((defaultSelected === null || defaultSelected === void 0 ? void 0 : defaultSelected.to)
        ? (0, date_utils_1.formatDateForInput)(defaultSelected.to, locale, "date", inputFormat)
        : "");
    const [validation, setValidation] = (0, react_1.useState)(initialValidation(selectedRange, opt));
    const [open, setOpen] = (0, react_1.useState)(false);
    const updateRange = (range) => {
        onRangeChange === null || onRangeChange === void 0 ? void 0 : onRangeChange(range);
        setSelectedRange(range);
    };
    const updateValidation = (from = {}, to = {}) => {
        const msg = getValidationMessage(from, to);
        setValidation(msg);
        onValidate === null || onValidate === void 0 ? void 0 : onValidate(msg);
    };
    const reset = () => {
        updateRange(defaultSelected !== null && defaultSelected !== void 0 ? defaultSelected : { from: undefined, to: undefined });
        setMonth((defaultSelected === null || defaultSelected === void 0 ? void 0 : defaultSelected.from) || (defaultSelected === null || defaultSelected === void 0 ? void 0 : defaultSelected.to) || defaultMonth || today);
        setValidation(initialValidation(defaultSelected !== null && defaultSelected !== void 0 ? defaultSelected : { from: undefined, to: undefined }, opt));
        setFromInputValue((defaultSelected === null || defaultSelected === void 0 ? void 0 : defaultSelected.from)
            ? (0, date_utils_1.formatDateForInput)(defaultSelected.from, locale, "date", inputFormat)
            : "");
        setToInputValue((defaultSelected === null || defaultSelected === void 0 ? void 0 : defaultSelected.to)
            ? (0, date_utils_1.formatDateForInput)(defaultSelected.to, locale, "date", inputFormat)
            : "");
        setDefaultSelected(_defaultSelected);
    };
    const setSelected = (range) => {
        updateRange(range);
        setFromInputValue((range === null || range === void 0 ? void 0 : range.from)
            ? (0, date_utils_1.formatDateForInput)(range.from, locale, "date", inputFormat)
            : "");
        setToInputValue((range === null || range === void 0 ? void 0 : range.to)
            ? (0, date_utils_1.formatDateForInput)(range === null || range === void 0 ? void 0 : range.to, locale, "date", inputFormat)
            : "");
        setValidation(initialValidation(range, opt));
    };
    const handleFocus = (e, src) => {
        if (e.target.readOnly) {
            return;
        }
        const day = (0, date_utils_1.parseDate)(e.target.value, today, locale, "date", allowTwoDigitYear);
        if ((0, date_utils_1.isValidDate)(day)) {
            src === RANGE.FROM
                ? setFromInputValue((0, date_utils_1.formatDateForInput)(day, locale, "date", inputFormat))
                : setToInputValue((0, date_utils_1.formatDateForInput)(day, locale, "date", inputFormat));
            const isBefore = fromDate && day && (0, date_fns_1.differenceInCalendarDays)(fromDate, day) > 0;
            const isAfter = toDate && day && (0, date_fns_1.differenceInCalendarDays)(day, toDate) > 0;
            !isBefore && !isAfter && setMonth(day);
        }
    };
    const handleBlur = (e, src) => {
        const day = (0, date_utils_1.parseDate)(e.target.value, today, locale, "date", allowTwoDigitYear);
        if (!(0, date_utils_1.isValidDate)(day)) {
            return;
        }
        if (src === RANGE.FROM) {
            setFromInputValue((0, date_utils_1.formatDateForInput)(day, locale, "date", inputFormat));
        }
        else if (src === RANGE.TO) {
            setToInputValue((0, date_utils_1.formatDateForInput)(day, locale, "date", inputFormat));
        }
    };
    const validateDay = (day) => {
        return ((0, date_utils_1.isValidDate)(day) &&
            !(disableWeekends && (0, date_fns_1.isWeekend)(day)) &&
            !(disabled && (0, react_day_picker_1.dateMatchModifiers)(day, disabled)));
    };
    const handleSelect = (range) => {
        if ((range === null || range === void 0 ? void 0 : range.from) && (range === null || range === void 0 ? void 0 : range.to)) {
            if (!(0, date_fns_1.isSameDay)(range.from, range.to)) {
                setOpen(false);
                anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.focus();
            }
        }
        setFromInputValue((range === null || range === void 0 ? void 0 : range.from)
            ? (0, date_utils_1.formatDateForInput)(range.from, locale, "date", inputFormat)
            : "");
        setToInputValue((range === null || range === void 0 ? void 0 : range.to)
            ? (0, date_utils_1.formatDateForInput)(range.to, locale, "date", inputFormat)
            : "");
        updateRange({ from: range === null || range === void 0 ? void 0 : range.from, to: range === null || range === void 0 ? void 0 : range.to });
        updateValidation({ isValidDate: !!(range === null || range === void 0 ? void 0 : range.from), isEmpty: !(range === null || range === void 0 ? void 0 : range.from) }, { isValidDate: !!(range === null || range === void 0 ? void 0 : range.to), isEmpty: !(range === null || range === void 0 ? void 0 : range.to) });
    };
    const fromChange = (val = "", day, isBefore = false, isAfter = false) => {
        setFromInputValue(val);
        if (!validateDay(day)) {
            updateRange(Object.assign(Object.assign({}, selectedRange), { from: undefined }));
            updateValidation({
                isEmpty: !val,
                isValidDate: false,
                isInvalid: !(0, date_utils_1.isValidDate)(day),
                isWeekend: disableWeekends && (0, date_fns_1.isWeekend)(day),
                isDisabled: disabled && (0, react_day_picker_1.dateMatchModifiers)(day, disabled),
                isBefore,
                isAfter,
            }, validation.to);
            return;
        }
        if (isBefore || isAfter) {
            updateRange(Object.assign(Object.assign({}, selectedRange), { from: undefined }));
            updateValidation({
                isValidDate: false,
                isBefore,
                isAfter,
            }, validation.to);
            return;
        }
        if ((selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.to) &&
            (0, date_fns_1.differenceInCalendarDays)(day, selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.to) > 0) {
            updateRange({ to: day, from: day });
            setToInputValue((0, date_utils_1.formatDateForInput)(day, locale, "date", inputFormat));
            setMonth(day);
            updateValidation();
            return;
        }
        if (toInputValue && !(selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.to)) {
            const toDay = (0, date_utils_1.parseDate)(toInputValue, today, locale, "date", allowTwoDigitYear);
            if (validateDay(toDay)) {
                updateRange({ from: day, to: toDay });
                setMonth(day);
                updateValidation();
                return;
            }
        }
        updateRange(Object.assign(Object.assign({}, selectedRange), { from: day }));
        updateValidation({}, validation.to);
        setMonth(day);
    };
    const toChange = (val = "", day, isBefore = false, isAfter = false) => {
        setToInputValue(val);
        if (!validateDay(day)) {
            updateRange({ from: selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.from, to: undefined });
            updateValidation(validation.from, {
                isEmpty: !val,
                isValidDate: false,
                isInvalid: !(0, date_utils_1.isValidDate)(day),
                isWeekend: disableWeekends && (0, date_fns_1.isWeekend)(day),
                isDisabled: disabled && (0, react_day_picker_1.dateMatchModifiers)(day, disabled),
                isBefore,
                isAfter,
            });
            return;
        }
        if (isBefore || isAfter) {
            updateRange({ from: selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.from, to: undefined });
            updateValidation(validation.from, {
                isValidDate: false,
                isBefore,
                isAfter,
            });
            return;
        }
        /* If to-value < from-value */
        if ((selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.from) &&
            (0, date_fns_1.differenceInCalendarDays)(selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.from, day) > 0) {
            updateRange({ from: selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.from, to: undefined });
            updateValidation(validation.from, {
                isValidDate: false,
                isBeforeFrom: true,
            });
            return;
        }
        updateRange({ from: selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.from, to: day });
        updateValidation(validation.from, {});
        setMonth(day);
    };
    /* live-update datepicker based on changes in inputfields */
    const handleChange = (e, src) => {
        const day = (0, date_utils_1.parseDate)(e.target.value, today, locale, "date", allowTwoDigitYear);
        const isBefore = fromDate && (0, date_fns_1.differenceInCalendarDays)(fromDate, day) > 0;
        const isAfter = toDate && (0, date_fns_1.differenceInCalendarDays)(day, toDate) > 0;
        return src === RANGE.FROM
            ? fromChange(e.target.value, day, isBefore, isAfter)
            : toChange(e.target.value, day, isBefore, isAfter);
    };
    const datepickerProps = {
        month,
        onMonthChange: setMonth,
        onSelect: handleSelect,
        selected: selectedRange,
        locale: _locale,
        fromDate,
        toDate,
        today,
        mode: "range",
        open,
        onOpenToggle: () => setOpen((x) => !x),
        onClose: () => {
            setOpen(false);
            /* Delay focus to allow "open"-button to update title before focus */
            queueMicrotask(() => (0, focus_1.focusElement)(anchorRef, { sync: false, preventScroll: true }));
        },
        disabled,
        disableWeekends,
    };
    const fromInputProps = {
        onChange: (e) => handleChange(e, RANGE.FROM),
        onFocus: (e) => handleFocus(e, RANGE.FROM),
        onBlur: (e) => handleBlur(e, RANGE.FROM),
        value: fromInputValue,
        setAnchorRef,
        callerName: "from",
    };
    const toInputProps = {
        onChange: (e) => handleChange(e, RANGE.TO),
        onFocus: (e) => handleFocus(e, RANGE.TO),
        onBlur: (e) => handleBlur(e, RANGE.TO),
        value: toInputValue,
        setAnchorRef,
        callerName: "to",
    };
    return {
        datepickerProps,
        fromInputProps,
        toInputProps,
        reset,
        selectedRange,
        setSelected,
    };
};
exports.useRangeDatepicker = useRangeDatepicker;
//# sourceMappingURL=useRangeDatepicker.js.map