/*
 * Copyright 2023 Palantir Technologies, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { __assign, __awaiter, __extends, __generator } from "tslib";
import classNames from "classnames";
import { format } from "date-fns";
import * as React from "react";
import { DayPicker } from "react-day-picker";
import { Button, DISPLAYNAME_PREFIX, Divider } from "@blueprintjs/core";
import { DatePickerShortcutMenu, DatePickerUtils, DateUtils, Errors, TimePicker, TimezoneUtils, } from "@blueprintjs/datetime";
import { Classes, dayPickerClassNameOverrides } from "../../classes";
import { DateFnsLocalizedComponent } from "../dateFnsLocalizedComponent";
import { DatePicker3Dropdown } from "../react-day-picker/datePicker3Dropdown";
import { IconLeft, IconRight } from "../react-day-picker/datePickerNavIcons";
import { DatePicker3Provider } from "./datePicker3Context";
/**
 * Date picker (v3) component.
 *
 * @see https://blueprintjs.com/docs/#datetime2/date-picker3
 */
var DatePicker3 = /** @class */ (function (_super) {
    __extends(DatePicker3, _super);
    function DatePicker3(props) {
        var _this = _super.call(this, props) || this;
        _this.ignoreNextMonthChange = false;
        /**
         * Custom formatter to render weekday names in the calendar header. The default formatter generally works fine,
         * but it was returning CAPITALIZED strings for some reason, while we prefer Title Case.
         */
        _this.renderWeekdayName = function (date) {
            return format(date, "EEEEEE", { locale: _this.state.locale });
        };
        _this.handleDaySelect = function (day, selectedDay, activeModifiers, e) {
            var _a, _b;
            if (activeModifiers.disabled) {
                return;
            }
            else if (day === undefined) {
                _this.handleClearClick();
                return;
            }
            _this.updateDay(day);
            (_b = (_a = _this.props.dayPickerProps) === null || _a === void 0 ? void 0 : _a.onSelect) === null || _b === void 0 ? void 0 : _b.call(_a, day, selectedDay, activeModifiers, e);
            // allow toggling selected date by clicking it again (if prop enabled)
            var newValue = _this.props.canClearSelection && activeModifiers.selected
                ? null
                : DateUtils.getDateTime(day, _this.state.value);
            _this.updateValue(newValue, true);
        };
        _this.handleShortcutClick = function (shortcut, selectedShortcutIndex) {
            var _a = _this.props, onShortcutChange = _a.onShortcutChange, currentShortcutIndex = _a.selectedShortcutIndex;
            var dateRange = shortcut.dateRange, includeTime = shortcut.includeTime;
            var newDate = dateRange[0];
            var newValue = includeTime ? newDate : DateUtils.getDateTime(newDate, _this.state.value);
            if (newDate == null) {
                return;
            }
            _this.updateDay(newDate);
            _this.updateValue(newValue, true);
            if (currentShortcutIndex === undefined) {
                _this.setState({ selectedShortcutIndex: selectedShortcutIndex });
            }
            var datePickerShortcut = __assign(__assign({}, shortcut), { date: newDate });
            onShortcutChange === null || onShortcutChange === void 0 ? void 0 : onShortcutChange(datePickerShortcut, selectedShortcutIndex);
        };
        _this.updateDay = function (day) {
            if (_this.props.value === undefined) {
                // set now if uncontrolled, otherwise they'll be updated in `componentDidUpdate`
                _this.setState({
                    displayMonth: day.getMonth(),
                    displayYear: day.getFullYear(),
                    selectedDay: day.getDate(),
                });
            }
            if (_this.state.value != null && _this.state.value.getMonth() !== day.getMonth()) {
                _this.ignoreNextMonthChange = true;
            }
        };
        _this.handleClearClick = function () { return _this.updateValue(null, true); };
        _this.handleMonthChange = function (newDate) {
            var _a, _b;
            var date = _this.computeValidDateInSpecifiedMonthYear(newDate.getFullYear(), newDate.getMonth());
            _this.setState({ displayMonth: date.getMonth(), displayYear: date.getFullYear() });
            if (_this.state.value !== null) {
                // if handleDayClick just got run (so this flag is set), then the
                // user selected a date in a new month, so don't invoke onChange a
                // second time
                _this.updateValue(date, false, _this.ignoreNextMonthChange);
                _this.ignoreNextMonthChange = false;
            }
            (_b = (_a = _this.props.dayPickerProps) === null || _a === void 0 ? void 0 : _a.onMonthChange) === null || _b === void 0 ? void 0 : _b.call(_a, date);
        };
        _this.handleTodayClick = function () {
            var timezone = _this.props.timezone;
            var today = new Date();
            var value = timezone != null ? TimezoneUtils.convertLocalDateToTimezoneTime(today, timezone) : today;
            var displayMonth = value.getMonth();
            var displayYear = value.getFullYear();
            var selectedDay = value.getDate();
            _this.setState({ displayMonth: displayMonth, displayYear: displayYear, selectedDay: selectedDay });
            _this.updateValue(value, true);
        };
        _this.handleTimeChange = function (time) {
            var _a, _b;
            (_b = (_a = _this.props.timePickerProps) === null || _a === void 0 ? void 0 : _a.onChange) === null || _b === void 0 ? void 0 : _b.call(_a, time);
            var value = _this.state.value;
            var newValue = DateUtils.getDateTime(value != null ? value : new Date(), time);
            _this.updateValue(newValue, true);
        };
        var value = getInitialValue(props);
        var initialMonth = getInitialMonth(props, value);
        _this.state = {
            displayMonth: initialMonth.getMonth(),
            displayYear: initialMonth.getFullYear(),
            locale: undefined,
            selectedDay: value == null ? null : value.getDate(),
            selectedShortcutIndex: _this.props.selectedShortcutIndex !== undefined ? _this.props.selectedShortcutIndex : -1,
            value: value,
        };
        return _this;
    }
    DatePicker3.prototype.render = function () {
        var _a;
        var _b;
        var _c = this.props, className = _c.className, dayPickerProps = _c.dayPickerProps, footerElement = _c.footerElement, maxDate = _c.maxDate, minDate = _c.minDate, showActionsBar = _c.showActionsBar;
        var _d = this.state, displayMonth = _d.displayMonth, displayYear = _d.displayYear, locale = _d.locale;
        return (React.createElement("div", { className: classNames(Classes.DATEPICKER, className, (_a = {},
                _a[Classes.DATEPICKER3_HIGHLIGHT_CURRENT_DAY] = this.props.highlightCurrentDay,
                _a[Classes.DATEPICKER3_REVERSE_MONTH_AND_YEAR] = this.props.reverseMonthAndYearMenus,
                _a)) },
            this.maybeRenderShortcuts(),
            React.createElement("div", { className: Classes.DATEPICKER_CONTENT },
                React.createElement(DatePicker3Provider, __assign({}, this.props, this.state),
                    React.createElement(DayPicker, __assign({ locale: locale, showOutsideDays: true }, dayPickerProps, { captionLayout: "dropdown-buttons", classNames: __assign(__assign({}, dayPickerClassNameOverrides), dayPickerProps === null || dayPickerProps === void 0 ? void 0 : dayPickerProps.classNames), components: __assign({ Dropdown: DatePicker3Dropdown, IconLeft: IconLeft, IconRight: IconRight }, dayPickerProps === null || dayPickerProps === void 0 ? void 0 : dayPickerProps.components), formatters: __assign({ formatWeekdayName: this.renderWeekdayName }, dayPickerProps === null || dayPickerProps === void 0 ? void 0 : dayPickerProps.formatters), fromDate: minDate, mode: "single", month: new Date(displayYear, displayMonth), onMonthChange: this.handleMonthChange, onSelect: this.handleDaySelect, required: !this.props.canClearSelection, selected: (_b = this.state.value) !== null && _b !== void 0 ? _b : undefined, toDate: maxDate })),
                    this.maybeRenderTimePicker(),
                    showActionsBar && this.renderOptionsBar(),
                    footerElement))));
    };
    DatePicker3.prototype.componentDidMount = function () {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, _super.prototype.componentDidMount.call(this)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    DatePicker3.prototype.componentDidUpdate = function (prevProps) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                _super.prototype.componentDidUpdate.call(this, prevProps);
                if (this.props.value !== prevProps.value) {
                    if (this.props.value == null) {
                        // clear the value
                        this.setState({ value: null });
                    }
                    else {
                        this.setState({
                            displayMonth: this.props.value.getMonth(),
                            displayYear: this.props.value.getFullYear(),
                            selectedDay: this.props.value.getDate(),
                            value: this.props.value,
                        });
                    }
                }
                if (this.props.selectedShortcutIndex !== prevProps.selectedShortcutIndex) {
                    this.setState({ selectedShortcutIndex: this.props.selectedShortcutIndex });
                }
                return [2 /*return*/];
            });
        });
    };
    DatePicker3.prototype.validateProps = function (props) {
        var defaultValue = props.defaultValue, initialMonth = props.initialMonth, maxDate = props.maxDate, minDate = props.minDate, value = props.value;
        if (defaultValue != null && !DateUtils.isDayInRange(defaultValue, [minDate, maxDate])) {
            console.error(Errors.DATEPICKER_DEFAULT_VALUE_INVALID);
        }
        if (initialMonth != null && !DateUtils.isMonthInRange(initialMonth, [minDate, maxDate])) {
            console.error(Errors.DATEPICKER_INITIAL_MONTH_INVALID);
        }
        if (maxDate != null && minDate != null && maxDate < minDate && !DateUtils.isSameDay(maxDate, minDate)) {
            console.error(Errors.DATEPICKER_MAX_DATE_INVALID);
        }
        if (value != null && !DateUtils.isDayInRange(value, [minDate, maxDate])) {
            console.error(Errors.DATEPICKER_VALUE_INVALID);
        }
    };
    DatePicker3.prototype.renderOptionsBar = function () {
        var _a = this.props, clearButtonText = _a.clearButtonText, todayButtonText = _a.todayButtonText, minDate = _a.minDate, maxDate = _a.maxDate, canClearSelection = _a.canClearSelection;
        var todayEnabled = isTodayEnabled(minDate, maxDate);
        return [
            React.createElement(Divider, { key: "div" }),
            React.createElement("div", { className: Classes.DATEPICKER_FOOTER, key: "footer" },
                React.createElement(Button, { disabled: !todayEnabled, onClick: this.handleTodayClick, text: todayButtonText, variant: "minimal" }),
                React.createElement(Button, { disabled: !canClearSelection, onClick: this.handleClearClick, text: clearButtonText, variant: "minimal" })),
        ];
    };
    DatePicker3.prototype.maybeRenderTimePicker = function () {
        var _a = this.props, timePrecision = _a.timePrecision, timePickerProps = _a.timePickerProps, minDate = _a.minDate, maxDate = _a.maxDate;
        if (timePrecision == null && timePickerProps === undefined) {
            return null;
        }
        var applyMin = this.state.value != null && DateUtils.isSameDay(this.state.value, minDate);
        var applyMax = this.state.value != null && DateUtils.isSameDay(this.state.value, maxDate);
        return (React.createElement("div", { className: Classes.DATEPICKER_TIMEPICKER_WRAPPER },
            React.createElement(TimePicker, __assign({ precision: timePrecision, minTime: applyMin ? minDate : undefined, maxTime: applyMax ? maxDate : undefined }, timePickerProps, { onChange: this.handleTimeChange, value: this.state.value }))));
    };
    DatePicker3.prototype.maybeRenderShortcuts = function () {
        var shortcuts = this.props.shortcuts;
        if (shortcuts == null || shortcuts === false) {
            return null;
        }
        var selectedShortcutIndex = this.state.selectedShortcutIndex;
        var _a = this.props, maxDate = _a.maxDate, minDate = _a.minDate, timePrecision = _a.timePrecision;
        // Reuse the existing date range shortcuts and only care about start date
        var dateRangeShortcuts = shortcuts === true
            ? true
            : shortcuts.map(function (shortcut) { return (__assign(__assign({}, shortcut), { dateRange: [shortcut.date, null] })); });
        return [
            React.createElement(DatePickerShortcutMenu, { key: "shortcuts", allowSingleDayRange: true, maxDate: maxDate, minDate: minDate, selectedShortcutIndex: selectedShortcutIndex, shortcuts: dateRangeShortcuts, timePrecision: timePrecision, onShortcutClick: this.handleShortcutClick, useSingleDateShortcuts: true }),
            React.createElement(Divider, { key: "div" }),
        ];
    };
    DatePicker3.prototype.computeValidDateInSpecifiedMonthYear = function (displayYear, displayMonth) {
        var _a = this.props, minDate = _a.minDate, maxDate = _a.maxDate;
        var selectedDay = this.state.selectedDay;
        // month is 0-based, date is 1-based. date 0 is last day of previous month.
        var maxDaysInMonth = new Date(displayYear, displayMonth + 1, 0).getDate();
        var displayDate = selectedDay == null ? 1 : Math.min(selectedDay, maxDaysInMonth);
        // 12:00 matches the underlying react-day-picker timestamp behavior
        var value = DateUtils.getDateTime(new Date(displayYear, displayMonth, displayDate, 12), this.state.value);
        // clamp between min and max dates
        if (value < minDate) {
            return minDate;
        }
        else if (value > maxDate) {
            return maxDate;
        }
        return value;
    };
    /**
     * Update `value` by invoking `onChange` (always) and setting state (if uncontrolled).
     */
    DatePicker3.prototype.updateValue = function (value, isUserChange, skipOnChange) {
        var _a, _b;
        if (skipOnChange === void 0) { skipOnChange = false; }
        if (!skipOnChange) {
            (_b = (_a = this.props).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, value, isUserChange);
        }
        if (this.props.value === undefined) {
            this.setState({ value: value });
        }
    };
    DatePicker3.defaultProps = {
        canClearSelection: true,
        clearButtonText: "Clear",
        dayPickerProps: {},
        highlightCurrentDay: false,
        locale: "en-US",
        maxDate: DatePickerUtils.getDefaultMaxDate(),
        minDate: DatePickerUtils.getDefaultMinDate(),
        reverseMonthAndYearMenus: false,
        shortcuts: false,
        showActionsBar: false,
        todayButtonText: "Today",
    };
    DatePicker3.displayName = "".concat(DISPLAYNAME_PREFIX, ".DatePicker3");
    return DatePicker3;
}(DateFnsLocalizedComponent));
export { DatePicker3 };
function getInitialValue(props) {
    // !== because `null` is a valid value (no date)
    if (props.value !== undefined) {
        return props.value;
    }
    if (props.defaultValue !== undefined) {
        return props.defaultValue;
    }
    return null;
}
function getInitialMonth(props, value) {
    var _a, _b;
    var rangeFromProps = [(_a = props.minDate) !== null && _a !== void 0 ? _a : null, (_b = props.maxDate) !== null && _b !== void 0 ? _b : null];
    var today = new Date();
    // != because we must have a real `Date` to begin the calendar on.
    if (props.initialMonth != null) {
        return props.initialMonth;
    }
    else if (value != null) {
        return value;
    }
    else if (DateUtils.isDayInRange(today, rangeFromProps)) {
        return today;
    }
    else {
        return DateUtils.getDateBetween(rangeFromProps);
    }
}
function isTodayEnabled(minDate, maxDate) {
    var today = new Date();
    return DateUtils.isDayInRange(today, [minDate, maxDate]);
}
//# sourceMappingURL=datePicker3.js.map