"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasketContextWrapper = BasketContextWrapper;
const contexts_1 = require("../index.js");
const hooks_1 = require("../../hooks/index.js");
const core_1 = require("@roomstay/core");
const BaseAPI_1 = require("api/BaseAPI");
const dayjs_1 = __importDefault(require("dayjs"));
const BasketContextErrorHandler_1 = require("handlers/BasketContextErrorHandler");
const HistoryConsistentPush_1 = require("hooks/HistoryConsistentPush");
const UpdateQueryParameters_1 = require("hooks/UpdateQueryParameters");
const AppQueryParameters_1 = require("models/AppQueryParameters");
const react_1 = __importStar(require("react"));
const react_i18next_1 = require("react-i18next");
const react_router_dom_1 = require("react-router-dom");
const BasketReducer_1 = require("reducers/BasketReducer");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const Translation_1 = require("translations/Translation");
const BookingAPI_1 = __importDefault(require("../../api/BookingAPI"));
const Icon_1 = require("../../components/generic/Icon/Icon");
const Text_1 = __importDefault(require("../../components/generic/Text"));
const MemberSignInModal_1 = require("../../components/members/SignInModal/MemberSignInModal");
const PromoCodeVerificationAlert_1 = require("../../components/steps/room/UserSearchSummary/PromoCodeVerificationAlert");
const ConfirmationQuery_1 = require("../../hooks/ConfirmationQuery");
const useSignedInMember_1 = require("../../hooks/useSignedInMember");
const BasketAddonRow_1 = __importDefault(require("../../models/BasketAddonRow"));
const BasketRow_1 = __importStar(require("../../models/BasketRow"));
const Fee_1 = require("../../models/Fee");
const StepAddon_1 = __importDefault(require("../../pages/steps/StepAddon/StepAddon"));
const StepThanks_1 = __importDefault(require("../../pages/steps/StepThanks/StepThanks"));
const InlineAddonStepFeature_1 = __importDefault(require("../../providers/feature/InlineAddonStepFeature"));
const PromotionProvider_1 = require("../../providers/PromotionProvider");
const CalendarHelper_1 = require("../../util/CalendarHelper");
const DataLayer_1 = __importDefault(require("../../util/DataLayer"));
const Debounce_1 = require("../../util/Debounce");
const GetStepRoom_1 = __importDefault(require("../../util/GetStepRoom"));
const StepManager_1 = __importDefault(require("../../util/StepManager"));
const StringHelper_1 = __importDefault(require("../../util/StringHelper"));
const AddonDiscountCalculator_1 = require("../../utils/AddonDiscountCalculator");
function BasketContextWrapper(props) {
    var _a, _b, _c, _d;
    const { t } = (0, react_i18next_1.useTranslation)();
    const { hotel } = (0, hooks_1.useCurrentHotel)();
    const updateQueryParams = (0, UpdateQueryParameters_1.useUpdateQueryParameters)();
    const { isInitialCheckout } = (0, ConfirmationQuery_1.useConfirmationQuery)();
    const navigate = (0, react_router_dom_1.useNavigate)();
    const historyConsistentPush = (0, HistoryConsistentPush_1.useHistoryConsistentPush)();
    const { signedInUser, isExternalMemberHotel, memberNumber, memberOnlyModalPromotion } = (0, useSignedInMember_1.useSignedInMember)();
    const notifications = (0, react_1.useContext)(contexts_1.NotificationContext);
    const beContext = (0, react_1.useContext)(contexts_1.BookingEngineContext);
    const { showPromoCodeAlert } = (0, react_1.useContext)(contexts_1.HotelOverridesContext);
    const initialSession = (_a = beContext === null || beContext === void 0 ? void 0 : beContext.engine) === null || _a === void 0 ? void 0 : _a.getInitialSession();
    const [currentBasketRows, basketRowReducer] = (0, react_1.useReducer)(BasketReducer_1.BasketReducer, (_b = initialSession === null || initialSession === void 0 ? void 0 : initialSession.basketRows) !== null && _b !== void 0 ? _b : []);
    const [pencilId, setPencilId] = (0, react_1.useState)((_c = initialSession === null || initialSession === void 0 ? void 0 : initialSession.pencilId) !== null && _c !== void 0 ? _c : undefined);
    const [selectedBasketRow, setSelectedBasketRow] = (0, react_1.useState)(null);
    const [basketAddonRows, setBasketAddonRows] = (0, react_1.useState)([]);
    // This is where we store the fees that aren't included in the rates/addons.
    const [basketFeeRows, setBasketFeeRows] = (0, react_1.useState)([]);
    // Promo code details from availability response
    const [promoCodeDetails, setPromoCodeDetails] = (0, react_1.useState)(null);
    const [isLoadingPriceQuote, setIsLoadingPriceQuote] = (0, react_1.useState)(false);
    const [hasPriceQuoteError, setHasPriceQuoteError] = (0, react_1.useState)(false); // If true, showing an error and enabling retry
    let loadedStartDate = null;
    let loadedEndDate = null;
    if ((_d = initialSession === null || initialSession === void 0 ? void 0 : initialSession.basketRows) === null || _d === void 0 ? void 0 : _d.length) {
        loadedStartDate = initialSession.basketRows[0].getStartDate();
        loadedEndDate = initialSession.basketRows[0].getEndDate();
    }
    else {
        if (initialSession === null || initialSession === void 0 ? void 0 : initialSession.startDate) {
            loadedStartDate = initialSession.startDate;
        }
        if (initialSession === null || initialSession === void 0 ? void 0 : initialSession.endDate) {
            loadedEndDate = initialSession.endDate;
        }
    }
    const [isDatePickerOpen, setIsDatePickerOpen] = (0, react_1.useState)(false);
    const [startDate, setStartDate] = (0, react_1.useState)(loadedStartDate !== null && loadedStartDate !== void 0 ? loadedStartDate : null);
    const [endDate, setEndDate] = (0, react_1.useState)(loadedEndDate !== null && loadedEndDate !== void 0 ? loadedEndDate : null);
    const [hasCheckedOut, setHasCheckedOut] = (0, react_1.useState)(false);
    const [reservationData, setReservationData] = (0, react_1.useState)((initialSession === null || initialSession === void 0 ? void 0 : initialSession.reservation) || null);
    const [knownErrorPromoCodes, setKnownErrorPromoCodes] = (0, react_1.useState)([]);
    /**
     * Handles availability warnings related to promo codes.
     * Updates basket rows if a promo code is invalid, and logs other warnings.
     *
     * @param warnings - List of warning messages.
     */
    BasketContextErrorHandler_1.BasketContextErrorHandler.handleAvailabilityWarnings = (warnings) => {
        warnings.forEach((warning) => {
            if (warning.type === 'PromoCode') {
                // PromoCode warning message
                handleParsedPromoCodeWarning(warning);
            }
            else {
                // Unexpected warning message
                contexts_1.ErrorHandler.add({ message: warning.message });
            }
        });
    };
    function handleParsedPromoCodeWarning(warning) {
        const promoCode = warning.data;
        if (!promoCode)
            return;
        updatePromoCodeRows(promoCode, warning.message);
        addPromoCodeToKnownErrors(promoCode);
    }
    function updatePromoCodeRows(promoCode, message) {
        currentBasketRows.forEach((row) => {
            if (promoCode.toLowerCase() === row.getPromoCode().toLowerCase()) {
                row.setPromoCodeStatus(BasketRow_1.EPromoCodeStatus.Invalid, message);
                workingContext.updateBasketRow(row, false);
            }
        });
    }
    function addPromoCodeToKnownErrors(promoCode) {
        const upperPromo = promoCode.toUpperCase();
        if (!knownErrorPromoCodes.includes(upperPromo)) {
            setKnownErrorPromoCodes((oldCodes) => [...oldCodes, upperPromo]);
        }
    }
    BasketContextErrorHandler_1.BasketContextErrorHandler.handlePromoSuccess = (promo, oneTimeUseOnly) => {
        let foundFirstValid = false;
        currentBasketRows.forEach((row) => {
            if (row.getPromoCode() === promo && row.getPromoCode()) {
                if (foundFirstValid && oneTimeUseOnly) {
                    row.setPromoCodeStatus(BasketRow_1.EPromoCodeStatus.Forbidden, 'This promo code can only be used on one room.');
                    workingContext.updateBasketRow(row, false);
                    return;
                }
                if (row.getPromoCodeStatus() === BasketRow_1.EPromoCodeStatus.Valid) {
                    foundFirstValid = true;
                    return;
                }
                row.setPromoCodeStatus(BasketRow_1.EPromoCodeStatus.Valid);
                workingContext.updateBasketRow(row, false);
                foundFirstValid = true;
            }
        });
    };
    // Handles availability warnings related to promo codes end
    const debounceUseEffect = (0, react_1.useCallback)((0, Debounce_1.debounce)(() => {
        var _a;
        if (!startDate || !endDate) {
            return;
        }
        if (isInitialCheckout) {
            DataLayer_1.default.instance.sendDateSelection({
                startDate,
                endDate,
                adult: selectedBasketRow === null || selectedBasketRow === void 0 ? void 0 : selectedBasketRow.getAdults(),
                children: selectedBasketRow === null || selectedBasketRow === void 0 ? void 0 : selectedBasketRow.getChildren(),
                infant: selectedBasketRow === null || selectedBasketRow === void 0 ? void 0 : selectedBasketRow.getInfants(),
            });
        }
        (_a = beContext.sessionProvider) === null || _a === void 0 ? void 0 : _a.updateSession({
            startDate,
            endDate,
        });
        // TODO re-visit this as we don't want to set dates for everything constantly
        workingContext.getAllDatelessRows().forEach((row) => {
            row.setStartDate(startDate);
            row.setEndDate(endDate);
            workingContext.updateBasketRow(row);
        });
        //When trying to call row.discardGuestsState() in somewhere, we got conflicted here with BasketRow.setStartDate& BasketRow.setEndDate, it set back to previous startDate/endDate
        //Use debounceUseEffect to fix it
        if (selectedBasketRow) {
            selectedBasketRow.setStartDate(startDate);
            selectedBasketRow.setEndDate(endDate);
            workingContext.updateBasketRow(selectedBasketRow, false);
        }
        else if (currentBasketRows.length === 1) {
            const onlyBasketRow = currentBasketRows[0];
            onlyBasketRow.setStartDate(startDate);
            onlyBasketRow.setEndDate(endDate);
            workingContext.updateBasketRow(onlyBasketRow, false);
        }
    }, 500), [startDate, endDate]);
    (0, react_1.useEffect)(() => {
        debounceUseEffect();
    }, [debounceUseEffect]);
    (0, react_1.useEffect)(() => {
        if (signedInUser) {
            if (isExternalMemberHotel) {
                // I don't think we want to set external member promos on the basket row; they are just a means to a member discount
                return;
            }
            const memberPromoCode = hotel === null || hotel === void 0 ? void 0 : hotel.memberOnlyPromoCode;
            const urlPromoCode = (0, CalendarHelper_1.getQueryParamFromHash)(AppQueryParameters_1.EAppQueryParameters.PromoCode);
            const allowDefaultMemberPromo = memberPromoCode && !(memberOnlyModalPromotion === null || memberOnlyModalPromotion === void 0 ? void 0 : memberOnlyModalPromotion.memberOnlyDisableDefaultPromo);
            currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows.forEach((row) => {
                // Only set promo code if row doesn't already have one
                if (row.getPromoCode())
                    return;
                // Determine the default promo code using precedence:
                const promoToApply = urlPromoCode || (allowDefaultMemberPromo ? memberPromoCode : null);
                if (promoToApply) {
                    row.setPromoCode(promoToApply);
                    updateBasketRow(row);
                }
            });
        }
    }, [signedInUser]);
    (0, react_1.useEffect)(() => {
        var _a;
        (_a = beContext.sessionProvider) === null || _a === void 0 ? void 0 : _a.updateSession({ basketRows: currentBasketRows });
    }, [currentBasketRows, selectedBasketRow]);
    (0, react_1.useEffect)(() => {
        updateBasketFeeRows();
    }, [currentBasketRows, basketAddonRows]);
    const checkAndApplyMemberOnlyPromo = (row, skipUpdate = false) => {
        if (row && (hotel === null || hotel === void 0 ? void 0 : hotel.memberOnlyPromoCode) && signedInUser && !row.getPromoCode()) {
            if (isExternalMemberHotel) {
                // I don't think we want to set external member promos on the basket row; they are just a means to a member discount
                return;
            }
            row.setPromoCode(hotel.memberOnlyPromoCode);
            if (!skipUpdate) {
                workingContext.updateBasketRow(row);
            }
        }
    };
    const returnAggregationAndTranslation = (reduction, translation) => {
        const count = typeof reduction === 'number' ? reduction : currentBasketRows.reduce((total, row) => total + reduction(row), 0);
        if (translation) {
            return StringHelper_1.default.pluralWithDictAndCount(count, translation, t);
        }
        else {
            return count;
        }
    };
    const getNextAvailableBasketRow = (deletedBasketRow) => {
        if (!currentBasketRows.length)
            return null;
        // Filter out the excluded basket row if provided
        const filteredRows = deletedBasketRow ? currentBasketRows.filter((row) => row.getID() !== deletedBasketRow.getID()) : currentBasketRows;
        // Find the first row that does not have a rate assigned
        const availableRow = filteredRows.find((row) => !row.getRate());
        // Return a row without a rate if found
        // If no such row exists , return the last row in the list
        const nextBasketRow = availableRow !== null && availableRow !== void 0 ? availableRow : filteredRows[filteredRows.length - 1];
        nextBasketRow.normalizeDateRange();
        return nextBasketRow;
    };
    const updateQueryParameters = (row) => {
        if (!row)
            return;
        const queryParameters = {};
        if (row.getEndDate()) {
            let startDate = selectedBasketRow === null || selectedBasketRow === void 0 ? void 0 : selectedBasketRow.getStartDate();
            if (!startDate) {
                startDate = row.getStartDate();
            }
            queryParameters[AppQueryParameters_1.EAppQueryParameters.Arrive] = startDate.format(BaseAPI_1.API_DATE_FORMAT);
            queryParameters[AppQueryParameters_1.EAppQueryParameters.Depart] = row.getEndDate().format(BaseAPI_1.API_DATE_FORMAT);
        }
        if (row.getPromoCode())
            queryParameters[AppQueryParameters_1.EAppQueryParameters.PromoCode] = row.getPromoCode();
        if (row.getChildren() !== undefined)
            queryParameters[AppQueryParameters_1.EAppQueryParameters.Children] = row.getChildren().toString();
        if (row.getAdults() !== undefined)
            queryParameters[AppQueryParameters_1.EAppQueryParameters.Adults] = row.getAdults().toString();
        updateQueryParams(queryParameters);
    };
    const updateBasketRow = (row, updateGlobalDates = true) => {
        updateQueryParameters(row);
        if (updateGlobalDates && row.getID() === (selectedBasketRow === null || selectedBasketRow === void 0 ? void 0 : selectedBasketRow.getID())) {
            setStartDate(row.getStartDate());
            setEndDate(row.getEndDate());
        }
        // Here we check if there is no promocode on the row. If there isn't this function will check for existing member details
        // and automatically apply a member only promocode
        checkAndApplyMemberOnlyPromo(row, true);
        // Update promo code details from availability cache
        const promoCode = row.getPromoCode();
        if (promoCode) {
            const promoDetails = BookingAPI_1.default.Availability.getPromoCodeDetails(promoCode);
            if (promoDetails) {
                setPromoCodeDetails(promoDetails);
            }
        }
        else {
            // Clear promo details if no promo code
            setPromoCodeDetails(null);
        }
        basketRowReducer({ method: 'Update', row });
    };
    const syncSelectedDateRange = (row) => {
        if (!row)
            return;
        if ((!row.getStartDate() || !row.getEndDate()) && startDate && endDate) {
            row.setStartDate(startDate);
            row.setEndDate(endDate);
        }
        else if (!startDate && !endDate && row.getStartDate() && row.getEndDate()) {
            setStartDate(row.getStartDate());
            setEndDate(row.getEndDate());
        }
    };
    const { loadedPromotion } = (0, PromotionProvider_1.usePromotionContainer)({ selectedBasketRow: selectedBasketRow, updateBasketRow });
    const genericRetryStrategy = ({ maxRetryAttempts = 3, scalingDuration = 0, } = {}) => (attempts) => {
        return attempts.pipe((0, operators_1.mergeMap)((error, i) => {
            const retryAttempt = i + 1;
            if (retryAttempt > maxRetryAttempts) {
                return (0, rxjs_1.throwError)(error);
            }
            console.log(`Attempt ${retryAttempt}: retrying in ${scalingDuration}ms`);
            return (0, rxjs_1.timer)(scalingDuration);
        }), (0, operators_1.finalize)(() => console.log('We are done!')));
    };
    /**
     * Booking Fees.
     * These fees aren't related to an addon or a room, they're just on the booking itself.
     */
    const getBookingFees = () => {
        var _a;
        const hotelFees = (_a = hotel === null || hotel === void 0 ? void 0 : hotel.fees) !== null && _a !== void 0 ? _a : [];
        const newFees = [];
        let totalBookingFees = 0;
        for (const fee of (0, core_1.getFilteredBookingFees)(hotelFees)) {
            const feeAmount = (0, core_1.calculateFeeAmount)(fee);
            newFees.push({ hotelFee: fee, amount: feeAmount });
            totalBookingFees += feeAmount;
        }
        // We need to add percentage fees on top of the Booking Fees, if the percentage fee is configured to include previous fees in calculation.
        if (totalBookingFees > 0) {
            let baseAmount = totalBookingFees;
            for (const fee of hotelFees.filter((fee) => fee.feeType === core_1.EFeeType.PercentageOfBooking && fee.includePreviousFeesInCalculation && !fee.extractFeeFromRates)) {
                const feeAmount = (0, core_1.calculateFeeAmount)(fee, {
                    baseAmount,
                });
                console.log(`Booking Fees: Calculated ${fee.name} fee amount on ${baseAmount} = ${feeAmount}`);
                newFees.push({ hotelFee: fee, amount: feeAmount });
                baseAmount += feeAmount;
            }
        }
        return newFees;
    };
    const getBookingRateFees = (row) => {
        var _a, _b;
        const hotelFees = (_a = hotel === null || hotel === void 0 ? void 0 : hotel.fees) !== null && _a !== void 0 ? _a : [];
        const newFees = [];
        const initialCost = (_b = row.getTotalPrice(true)) !== null && _b !== void 0 ? _b : 0;
        let totalFeesAdded = 0;
        let totalFeesIncludedInRates = 0;
        const rateFees = (0, core_1.getFilteredBookingRateFees)(hotelFees);
        // In order to calculate fees that were removed from the incoming rates using extractFeeFromRates option, we need
        // to know which fees were removed. When there's more than 1 percentage fee that's been removed, we can't
        // work backwards to figure out what the original price was without this.
        const removedPercentages = rateFees.filter((fee) => fee.extractFeeFromRates).map((fee) => fee.amount);
        for (const fee of rateFees) {
            if (fee.extractFeeFromRates || fee.includeInRates) {
                // For these fees, we need to add the amount to the the totalFeesIncludedInRates variable, so that we can
                // correctly calculate subsequent fees that are using the 'extractFeeFromRates' option.
                const baseAmount = fee.extractFeeFromRates ? initialCost : initialCost + totalFeesIncludedInRates;
                totalFeesIncludedInRates += (0, core_1.calculateFeeAmount)(fee, {
                    baseAmount,
                    numberOfNights: row.getNumberOfDays(),
                    extractedFromRates: fee.extractFeeFromRates,
                    removedPercentages,
                });
            }
            let feeCalculationBaseAmount;
            if (fee.extractFeeFromRates) {
                // If this fee is included in the rates, there's no additional fee to calculate.
                feeCalculationBaseAmount = fee.includeInRates ? 0 : initialCost;
            }
            else if (fee.includeInRates) {
                // These fees are already included in the rates, we only need to calculate "fees on fees" if applicable.
                if (!fee.includePreviousFeesInCalculation) {
                    continue;
                }
                feeCalculationBaseAmount = totalFeesAdded;
            }
            else {
                // This fee either needs to include the full running total, or just the initial cost.
                feeCalculationBaseAmount = fee.includePreviousFeesInCalculation ? initialCost + totalFeesAdded + totalFeesIncludedInRates : initialCost;
            }
            const feeAmount = (0, core_1.calculateFeeAmount)(fee, {
                baseAmount: feeCalculationBaseAmount,
                numberOfNights: row.getNumberOfDays(),
                extractedFromRates: fee.extractFeeFromRates,
                removedPercentages,
            });
            newFees.push({ hotelFee: fee, amount: feeAmount });
            if (!fee.extractFeeFromRates) {
                totalFeesAdded += feeAmount;
            }
        }
        return newFees;
    };
    const getBookingAddonFees = () => {
        var _a;
        const hotelFees = (_a = hotel === null || hotel === void 0 ? void 0 : hotel.fees) !== null && _a !== void 0 ? _a : [];
        const newFees = [];
        const validBasketAddonRows = basketAddonRows.filter((row) => row.isValid());
        for (const row of validBasketAddonRows) {
            const initialCost = row.getTotalPrice() - row.getTotalFees();
            let totalFeesAdded = 0;
            let totalFeesIncludedInRates = 0;
            const addonFees = (0, core_1.getFilteredBookingAddonFees)(hotelFees).filter((fee) => fee.appliesToAddons);
            const removedPercentages = addonFees.filter((fee) => fee.extractFeeFromRates).map((fee) => fee.amount);
            const hasItemisedFees = row.getItemisedFees(hotelFees).length > 0;
            for (const fee of addonFees) {
                if ((fee.extractFeeFromRates || fee.includeInRates) && hasItemisedFees) {
                    const baseAmount = fee.extractFeeFromRates ? initialCost : initialCost + totalFeesIncludedInRates;
                    totalFeesIncludedInRates += (0, core_1.calculateFeeAmount)(fee, {
                        baseAmount,
                        numberOfNights: row.getNightModifier(),
                        extractedFromRates: fee.extractFeeFromRates,
                        removedPercentages,
                    });
                }
                let feeCalculationBaseAmount;
                if (fee.extractFeeFromRates) {
                    feeCalculationBaseAmount = fee.includeInRates && hasItemisedFees ? 0 : initialCost;
                }
                else if (fee.includeInRates && hasItemisedFees) {
                    feeCalculationBaseAmount = fee.includePreviousFeesInCalculation ? totalFeesAdded : 0;
                }
                else {
                    feeCalculationBaseAmount = fee.includePreviousFeesInCalculation ? initialCost + totalFeesAdded + totalFeesIncludedInRates : initialCost;
                }
                const feeAmount = (0, core_1.calculateFeeAmount)(fee, {
                    baseAmount: feeCalculationBaseAmount,
                    numberOfNights: row.getNightModifier(),
                    extractedFromRates: fee.extractFeeFromRates && !fee.includeInRates,
                    removedPercentages,
                });
                newFees.push({ hotelFee: fee, amount: feeAmount });
                if (!fee.extractFeeFromRates) {
                    totalFeesAdded += feeAmount;
                }
            }
        }
        return newFees;
    };
    const updateBasketFeeRows = () => {
        const validBasketRows = currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows.filter((row) => row.isValid());
        if (!validBasketRows.length || !(hotel === null || hotel === void 0 ? void 0 : hotel.fees)) {
            setBasketFeeRows([]);
            return;
        }
        const newFees = [];
        /**
         * Booking Fees.
         * These fees aren't related to an addon or a room, they're just on the booking itself.
         */
        newFees.push(...getBookingFees());
        /**
         * Booking Rate Fees.
         */
        for (const row of validBasketRows) {
            const rateFees = getBookingRateFees(row);
            newFees.push(...rateFees);
        }
        /**
         * Booking Addon Fees.
         */
        newFees.push(...getBookingAddonFees());
        setBasketFeeRows(newFees);
    };
    const quoteQueryID = (0, react_1.useMemo)(() => {
        const requireQuote = (hotel === null || hotel === void 0 ? void 0 : hotel.rmsUseDepositQuote) || (hotel === null || hotel === void 0 ? void 0 : hotel.bookingFlow) === core_1.EBookingFlow.PreCheckoutPencil;
        if (!hotel || !requireQuote) {
            return '';
        }
        return BookingAPI_1.default.Reservation.buildQuoteQueryID(currentBasketRows, isExternalMemberHotel ? memberNumber : undefined);
    }, [currentBasketRows, isExternalMemberHotel, memberNumber, hotel === null || hotel === void 0 ? void 0 : hotel.hotelID]);
    /**
     * Indicates whether required pricing data is ready.
     *
     * - Returns `true` when the hotel does not require a quote.
     * - While a quote is loading, verifies all basket rows contain
     *   the required pricing data (`quoteDetails` or `pencilId`).
     * - Used to block UI actions until pricing is ready.
     */
    const isQuoteOrPencilReady = (0, react_1.useMemo)(() => {
        const requireQuote = !!(hotel === null || hotel === void 0 ? void 0 : hotel.rmsUseDepositQuote) || !!((hotel === null || hotel === void 0 ? void 0 : hotel.bookingFlow) && (hotel === null || hotel === void 0 ? void 0 : hotel.bookingFlow) == core_1.EBookingFlow.PreCheckoutPencil);
        const isLoadedQuoteOrPencil = currentBasketRows.every((basketRow) => {
            if ((hotel === null || hotel === void 0 ? void 0 : hotel.bookingFlow) === core_1.EBookingFlow.PreCheckoutPencil) {
                return !!basketRow.pencilId;
            }
            if (hotel === null || hotel === void 0 ? void 0 : hotel.rmsUseDepositQuote) {
                return !!basketRow.quoteDetails;
            }
            return true;
        });
        if (!hotel || !requireQuote || (requireQuote && !isLoadingPriceQuote && isLoadedQuoteOrPencil)) {
            return true;
        }
        return false;
    }, [isLoadingPriceQuote, hotel === null || hotel === void 0 ? void 0 : hotel.rmsUseDepositQuote, hotel === null || hotel === void 0 ? void 0 : hotel.bookingFlow, currentBasketRows]);
    /**
     * Calculate total price of all addons with promo code discounts applied.
     * Uses AddonDiscountCalculator to handle sequential discount allocation.
     */
    const calculateAddonTotalWithDiscounts = (0, react_1.useCallback)(() => {
        return AddonDiscountCalculator_1.AddonDiscountCalculator.calculateTotal(basketAddonRows, currentBasketRows, promoCodeDetails);
    }, [basketAddonRows, currentBasketRows, promoCodeDetails]);
    // Whether another room can be added (within max rooms per booking limit)
    const canAddAnotherRoom = (0, react_1.useMemo)(() => {
        return !(hotel === null || hotel === void 0 ? void 0 : hotel.maxRoomsPerBooking) || (hotel === null || hotel === void 0 ? void 0 : hotel.maxRoomsPerBooking) > currentBasketRows.length;
    }, [hotel === null || hotel === void 0 ? void 0 : hotel.maxRoomsPerBooking, currentBasketRows.length]);
    const workingContext = {
        startDate: startDate,
        endDate: endDate,
        selectedBasketRow: selectedBasketRow,
        currentBasketRows,
        basketAddonRows,
        basketFeeRows,
        updateBasketFeeRows,
        hasCheckedOut,
        reservationData: reservationData,
        isLoadingPriceQuote,
        loadedPromotion,
        promoCodeDetails,
        quoteQueryID,
        pencilId,
        /**
         * Adds a new basket row and applies relevant updates:
         * - Synchronizes the selected date range.
         * - Applies any applicable member-only promotions.
         * - Ensures proper selection logic for the basket row.
         *
         * Selection logic:
         * - If the basket is empty, the newly added row is automatically selected.
         * - If a row is already selected:
         *   - If the selected row has an assigned rate, update the selection to the new row.
         *   - Otherwise, keep the current selection (allowing continued editing of the existing row).
         * - If no row is selected, set the new row as the selected row.
         *
         * @param {BasketRow} row - The basket row to add.
         */
        addBasketRow: (row) => {
            if (!canAddAnotherRoom)
                return;
            syncSelectedDateRange(row);
            checkAndApplyMemberOnlyPromo(row);
            // Determine if the basket is currently empty
            const isEmptyRows = !currentBasketRows.length;
            if (isEmptyRows) {
                // If empty, set the newly added row as the selected basket row
                setSelectedBasketRow(row);
            }
            else {
                if (selectedBasketRow) {
                    // Determine if the currently selected row has an assigned rate
                    const isSelectedRowAssignedRate = selectedBasketRow && selectedBasketRow.getRate();
                    if (isSelectedRowAssignedRate) {
                        // If the selected row has a rate, update the selection to the new row
                        setSelectedBasketRow(row);
                    }
                }
                else {
                    // If no row is currently selected, set the new row as the selected basket row
                    setSelectedBasketRow(row);
                }
            }
            basketRowReducer({ method: 'Add', row });
        },
        initBasketRows: (rows) => {
            const firstRow = rows[0];
            syncSelectedDateRange(firstRow);
            setSelectedBasketRow(firstRow);
            basketRowReducer({
                method: 'Init',
                rows: rows,
            });
        },
        //TODO: This function needs to be refactored to simplify a lot of this logic, and move notification raising to use the Event Manager instead
        addRate: (rate_1, ...args_1) => __awaiter(this, [rate_1, ...args_1], void 0, function* (rate, notify = true) {
            let basketRow = null;
            let translationString;
            const hasNoBasketRowSelected = !selectedBasketRow;
            let addons = [];
            if (selectedBasketRow) {
                selectedBasketRow.setPreGuestsState();
                basketRow = selectedBasketRow;
                if (selectedBasketRow.getRate()) {
                    translationString = Translation_1.Translation.Misc.RoomUpdatedNotification;
                }
                else {
                    if (!selectedBasketRow.getStartDate() && selectedBasketRow.getEndDate()) {
                        selectedBasketRow.setStartDate(startDate);
                        selectedBasketRow.setEndDate(endDate);
                    }
                    translationString = Translation_1.Translation.Misc.RoomAddedNotification;
                }
            }
            else {
                // When no basket row is selected and the guest clicks "Book Now", create a new basket row using the selected room rate and dates.
                if (!startDate || !endDate) {
                    console.error('room selected with no date values.');
                    return;
                }
                basketRow = new BasketRow_1.default(rate);
                basketRow.setStartDate(startDate);
                basketRow.setEndDate(endDate);
                translationString = Translation_1.Translation.Misc.RoomAddedNotification;
            }
            if (InlineAddonStepFeature_1.default.isActive()) {
                basketRow.setRate(rate); // TODO: Fix quick hack to make the Addons load
                addons = yield BookingAPI_1.default.Addons.findAddons([basketRow], hotel === null || hotel === void 0 ? void 0 : hotel.hotelID);
            }
            basketRow.setAuxiliaryFilter(beContext.currentGroupedRoomFilters);
            if (hasNoBasketRowSelected) {
                // When no basket row is currently selected, automatically select the newly created row so the guest can immediately review or edit it.
                setSelectedBasketRow(basketRow);
            }
            beContext.setCurrentGroupedRoomFilters({});
            basketRowReducer({ method: 'AddRate', rate, row: basketRow });
            // Update promo code details from availability cache
            if (rate.promoCode) {
                const promoDetails = BookingAPI_1.default.Availability.getPromoCodeDetails(rate.promoCode);
                if (promoDetails) {
                    setPromoCodeDetails(promoDetails);
                }
            }
            else {
                // Clear promo details if no promo code
                setPromoCodeDetails(null);
            }
            try {
                DataLayer_1.default.instance.sendRoomRateAddToCart(rate, basketRow.getNumberOfDays(), startDate !== null && startDate !== void 0 ? startDate : basketRow.getStartDate(), endDate !== null && endDate !== void 0 ? endDate : basketRow.getEndDate(), basketRow.getPreUpsellRoomRate());
            }
            catch (ex) {
                console.log('Failed recording basket Add event');
            }
            if (notify) {
                notifications.addNotification({
                    icon: Icon_1.IconType.Bed,
                    children: (react_1.default.createElement(Text_1.default, { inline: true },
                        t(translationString),
                        " ",
                        react_1.default.createElement("strong", { className: "u-marg-left--light" }, rate.getRoom().name))),
                });
            }
            const nextAvailable = getNextAvailableBasketRow();
            if (!(nextAvailable === null || nextAvailable === void 0 ? void 0 : nextAvailable.getRate())) {
                // If the next available row does not have a rate assigned,
                // set it as the selected basket row to allow rate assignment.
                setSelectedBasketRow(nextAvailable);
            }
            else {
                /**
                 * If all rows already have a rate assigned. Set the selected basket row to null, ensuring that no row is selected for editing.
                 */
                if (!hasNoBasketRowSelected) {
                    workingContext.clearSelectedRates();
                }
                // Determine whether the next step is an add-on selection step.
                const isNextStepAddon = StepManager_1.default.getNextStepUrl((0, GetStepRoom_1.default)()) === StepAddon_1.default.getStepUrl();
                if (isNextStepAddon) {
                    if (addons.length) {
                        historyConsistentPush(StepAddon_1.default.getStepUrl());
                    }
                    else {
                        historyConsistentPush(StepManager_1.default.getNextStepUrl(StepAddon_1.default));
                    }
                }
                else {
                    historyConsistentPush(StepManager_1.default.getNextStepUrl((0, GetStepRoom_1.default)()));
                }
            }
        }),
        quoteOrPencilReservation: (applyQuote) => {
            const maxRetryAttempts = 1;
            let callApi;
            // To avoid multiple requests, pencil reservation step also generates a quote (if applicable)
            if ((hotel === null || hotel === void 0 ? void 0 : hotel.bookingFlow) === core_1.EBookingFlow.PreCheckoutPencil) {
                callApi = BookingAPI_1.default.Reservation.pencilReservation;
            }
            else if (hotel === null || hotel === void 0 ? void 0 : hotel.rmsUseDepositQuote) {
                callApi = BookingAPI_1.default.Reservation.getPriceQuote;
            }
            else {
                return false;
            }
            setIsLoadingPriceQuote(true);
            setHasPriceQuoteError(false);
            // Clear currently assigned quote details
            // If we're fetching basket row information the currently held info should be disregarded.
            currentBasketRows.forEach((row) => {
                if (row.quoteDetails) {
                    row.quoteDetails = undefined;
                    updateBasketRow(row);
                }
            });
            const retryableGetQuote = () => (0, rxjs_1.defer)(() => callApi(hotel.hotelID, currentBasketRows, isExternalMemberHotel ? memberNumber : undefined, pencilId)).pipe((0, operators_1.retryWhen)(genericRetryStrategy({
                maxRetryAttempts: maxRetryAttempts,
            })));
            retryableGetQuote().subscribe((response) => {
                var _a, _b, _c;
                let pencilResponse = null;
                let quoteResponse;
                if ((hotel === null || hotel === void 0 ? void 0 : hotel.bookingFlow) === core_1.EBookingFlow.PreCheckoutPencil) {
                    // Pencil call also passes back the quote (if applicable)
                    pencilResponse = response;
                    // Important: When `hotel?.rmsUseDepositQuote` is false, `pencilResponse.quote` is always null
                    quoteResponse = pencilResponse.quote;
                    // Store parent pencil ID to pass back to the server later
                    setPencilId(pencilResponse.pencilId);
                    (_a = beContext.sessionProvider) === null || _a === void 0 ? void 0 : _a.updateSession({ pencilId: pencilResponse.pencilId });
                    // Future improvement: Handle availability errors
                }
                else if (hotel.rmsUseDepositQuote) {
                    quoteResponse = response;
                }
                if (!applyQuote()) {
                    // User has left the Confirmation step before the quote was ready, we'll stop here instead of updating the basket.
                    // If we don't stop here, the user will be redirected back to the Confirmation step.
                    setIsLoadingPriceQuote(false);
                    return;
                }
                // Ensure pencilId is assigned even when quoteResponse is not available
                for (const basketRow of currentBasketRows) {
                    const quoteRow = (_b = quoteResponse === null || quoteResponse === void 0 ? void 0 : quoteResponse.rows) === null || _b === void 0 ? void 0 : _b.find((row) => row.rowId === basketRow.getID());
                    const pencilRow = (_c = pencilResponse === null || pencilResponse === void 0 ? void 0 : pencilResponse.rows) === null || _c === void 0 ? void 0 : _c.find((row) => row.rowId === basketRow.getID());
                    if (quoteRow) {
                        basketRow.quoteDetails = quoteRow;
                    }
                    if (pencilRow) {
                        basketRow.pencilId = pencilRow.pencilId;
                    }
                    updateBasketRow(basketRow);
                }
            }, (error) => {
                setHasPriceQuoteError(true);
                console.log(error);
                console.log(`Error!: Retried  ${maxRetryAttempts} times then quit!`);
            }, () => {
                setIsLoadingPriceQuote(false);
            });
        },
        /**
         * Filter global list of rooms based on their validity.
         *
         * Useful for getting a complete summary of all the rooms.
         */
        getAllValidRows: () => {
            return currentBasketRows.filter((item) => item.isValid());
        },
        getAllDatelessRows: () => {
            return currentBasketRows.filter((item) => !item.getStartDate() || !item.getEndDate());
        },
        isValidDateRange: () => {
            return !!startDate && !!endDate && startDate.isBefore(endDate);
        },
        getNumberOfAdults: (withTranslation = false) => {
            return returnAggregationAndTranslation((row) => row.getAdults(), withTranslation ? Translation_1.Translation.Step.Date.Adult : undefined);
        },
        getNumberOfChildren: (withTranslation = false) => {
            return returnAggregationAndTranslation((row) => row.getChildren(), withTranslation ? Translation_1.Translation.Step.Date.Child : undefined);
        },
        getNumberOfInfants: (withTranslation = false) => {
            return returnAggregationAndTranslation((row) => row.getInfants(), withTranslation ? Translation_1.Translation.Step.Date.Infant : undefined);
        },
        getNumberOfRooms: (withTranslation = false) => {
            return returnAggregationAndTranslation(currentBasketRows.length, withTranslation ? Translation_1.Translation.Step.Date.Room : undefined);
        },
        getTotalPrice: () => {
            let totalPrice = returnAggregationAndTranslation((row) => { var _a; return (row.isValid() ? (_a = row.getRate()) === null || _a === void 0 ? void 0 : _a.getTotalPrice() : 0); });
            // If the hotel requires a deposit, and enableCreditCardSurchase is false, we need to add the deposit fees to the total price.
            if ((hotel === null || hotel === void 0 ? void 0 : hotel.rmsUseDepositQuote) && !(hotel === null || hotel === void 0 ? void 0 : hotel.enableCreditCardSurcharge) && (hotel === null || hotel === void 0 ? void 0 : hotel.addDepositFeeToBookingTotal)) {
                const depositFees = returnAggregationAndTranslation((row) => { var _a; return (row.quoteDetails ? (_a = row.quoteDetails) === null || _a === void 0 ? void 0 : _a.totalFees : 0); });
                totalPrice += depositFees;
            }
            // Add addon totals with promo code discounts applied
            totalPrice += calculateAddonTotalWithDiscounts();
            // Add fees that aren't included in the rates.
            basketFeeRows.forEach((row) => {
                totalPrice += row.amount;
            });
            return totalPrice;
        },
        getTotalQuoteDeposit: () => {
            return returnAggregationAndTranslation((row) => { var _a; return (row.quoteDetails ? (_a = row.quoteDetails) === null || _a === void 0 ? void 0 : _a.totalDeposit : 0); });
        },
        getDepositQuoteFees: () => {
            return returnAggregationAndTranslation((row) => { var _a; return (row.quoteDetails ? (_a = row.quoteDetails) === null || _a === void 0 ? void 0 : _a.totalFees : 0); });
        },
        getTotalFees: () => {
            let totalFees = returnAggregationAndTranslation((row) => { var _a; return (row.isValid() ? (_a = row.getRate()) === null || _a === void 0 ? void 0 : _a.getTotalFees() : 0); });
            if (totalFees == 0 && (hotel === null || hotel === void 0 ? void 0 : hotel.rmsUseDepositQuote)) {
                // Hotel may have credit card surcharges disabled, but they could still be included as a part of the quote.
                totalFees = returnAggregationAndTranslation((row) => { var _a; return (row.quoteDetails ? (_a = row.quoteDetails) === null || _a === void 0 ? void 0 : _a.totalFees : 0); });
            }
            basketAddonRows.forEach((row) => {
                totalFees += row.getTotalFees();
            });
            return totalFees;
        },
        /**
         * Aggregates all itemised fees from room rates, basket addons, and basket fees.
         * Returns a sorted list of aggregated fees, with percentage fees at the bottom.
         */
        getItemisedFees: () => {
            const feeMap = new Map();
            // Aggregate fees from room rates
            currentBasketRows.forEach((row) => {
                var _a, _b;
                if (row.isValid() && row.getRate() && (hotel === null || hotel === void 0 ? void 0 : hotel.fees)) {
                    const roomFees = (_b = (_a = row.getRate()) === null || _a === void 0 ? void 0 : _a.getItemisedFees(hotel.fees)) !== null && _b !== void 0 ? _b : [];
                    (0, Fee_1.accumulateFees)(feeMap, roomFees);
                }
            });
            // Aggregate fees from basket addon rows
            basketAddonRows.forEach((row) => {
                var _a;
                const addonFees = row.getItemisedFees((_a = hotel === null || hotel === void 0 ? void 0 : hotel.fees) !== null && _a !== void 0 ? _a : []);
                (0, Fee_1.accumulateFees)(feeMap, addonFees);
            });
            // Aggregate fees that aren't included in the rooms/addons
            basketFeeRows.forEach((basketFeeRow) => {
                (0, Fee_1.accumulateFees)(feeMap, [
                    {
                        hotelFee: basketFeeRow.hotelFee,
                        amount: basketFeeRow.amount,
                    },
                ]);
            });
            const fees = (0, Fee_1.feeMapToArray)(feeMap);
            return (0, Fee_1.sortFees)(fees, (hotel === null || hotel === void 0 ? void 0 : hotel.fees) || []);
        },
        getTotalPayNow: () => {
            var _a, _b;
            if (hotel === null || hotel === void 0 ? void 0 : hotel.rmsUseDepositQuote) {
                const depositFees = returnAggregationAndTranslation((row) => { var _a; return (row.quoteDetails ? (_a = row.quoteDetails) === null || _a === void 0 ? void 0 : _a.totalFees : 0); });
                return workingContext.getTotalQuoteDeposit() + depositFees;
            }
            const anyPolicyIsMissingDepositMode = currentBasketRows.some((row) => {
                var _a;
                const guaranteePolicy = (_a = row.getRate()) === null || _a === void 0 ? void 0 : _a.getGuaranteePolicy();
                return guaranteePolicy && guaranteePolicy.depositMode === null;
            });
            if (anyPolicyIsMissingDepositMode) {
                // This should only happen for SynXis.
                return null;
            }
            let totalPayNow = 0;
            const anyRateRequiresFullPrepayment = currentBasketRows.some((row) => { var _a; return ((_a = row.getRate()) === null || _a === void 0 ? void 0 : _a.getGuaranteePolicy().depositMode) === core_1.EGuaranteePolicyDepositMode.Full; });
            if (anyRateRequiresFullPrepayment) {
                // Add booking fees that aren't associated with a rate or addon.
                getBookingFees().forEach((fee) => {
                    totalPayNow += fee.amount;
                });
            }
            const firstRateGuaranteePolicy = (_b = (_a = currentBasketRows[0]) === null || _a === void 0 ? void 0 : _a.getRate()) === null || _b === void 0 ? void 0 : _b.getGuaranteePolicy();
            if (firstRateGuaranteePolicy) {
                const addonTotal = calculateAddonTotalWithDiscounts();
                const addonTotalWithFees = addonTotal + getBookingAddonFees().reduce((acc, row) => acc + row.amount, 0);
                const depositPercentage = firstRateGuaranteePolicy.depositPercentage;
                switch (firstRateGuaranteePolicy.depositMode) {
                    case core_1.EGuaranteePolicyDepositMode.Full:
                        totalPayNow += addonTotalWithFees;
                        break;
                    case core_1.EGuaranteePolicyDepositMode.Percentage:
                        if (depositPercentage) {
                            totalPayNow += addonTotalWithFees * (depositPercentage / 100);
                        }
                        break;
                    case core_1.EGuaranteePolicyDepositMode.None:
                    case core_1.EGuaranteePolicyDepositMode.FirstNight:
                        break;
                }
            }
            for (const row of currentBasketRows) {
                const rate = row.getRate();
                // The BookingRate fees that are not included in the rate.
                const bookingRateFees = getBookingRateFees(row).reduce((acc, fee) => acc + fee.amount, 0);
                if (rate) {
                    totalPayNow += rate.getDepositAccordingToGuaranteePolicy(bookingRateFees);
                }
            }
            return totalPayNow;
        },
        /**
         * Pops the provided row from the current basket array
         */
        removeBasketRow: (row) => {
            const rowsBeforeRemoval = currentBasketRows.length;
            basketRowReducer({ method: 'Remove', row });
            if (selectedBasketRow && row.getID() === selectedBasketRow.getID()) {
                const availableBasketRow = getNextAvailableBasketRow(row);
                if (availableBasketRow) {
                    setStartDate(availableBasketRow.getStartDate());
                    setEndDate(availableBasketRow.getEndDate());
                }
                setSelectedBasketRow(availableBasketRow);
            }
            if (rowsBeforeRemoval === 1) {
                workingContext.addBasketRow(new BasketRow_1.default());
            }
        },
        clearBasketRows: (confirm) => {
            if (confirm) {
                basketRowReducer({ method: 'Clear' });
            }
        },
        updateBasketRow,
        /**
         * Selects the given basket row and updates all relevant inputs (e.g., start date and end date).
         * - If the provided row is valid, it updates the selected row and sets the corresponding dates.
         * - If the row is null or invalid, it attempts to select the next available basket row.
         *
         * @param {BasketRow | null} row - The basket row to be selected.
         * @returns {Promise<void>} A promise that resolves when the selection is complete.
         */
        selectBasketRow: (row) => __awaiter(this, void 0, void 0, function* () {
            if (row && row.isValid()) {
                setStartDate(row.getStartDate());
                setEndDate(row.getEndDate());
                // Update the selected basket row
                setSelectedBasketRow(row);
            }
            else {
                // If the row is invalid or null, select the next available basket row
                setSelectedBasketRow(getNextAvailableBasketRow());
            }
        }),
        setStartDate,
        setEndDate,
        setPromoCodeDetails,
        booked: (data, requestData) => __awaiter(this, void 0, void 0, function* () {
            var _a;
            const updateObject = { reservation: data };
            if (requestData) {
                updateObject.reservationRequest = requestData;
            }
            yield ((_a = beContext.sessionProvider) === null || _a === void 0 ? void 0 : _a.updateSessionAsync(updateObject));
            setReservationData(data);
        }),
        rollback: () => {
            var _a;
            (_a = beContext.sessionProvider) === null || _a === void 0 ? void 0 : _a.updateSession({
                reservation: undefined,
            });
            setReservationData(null);
        },
        checkedOut: (data) => {
            var _a, _b;
            setHasCheckedOut(true);
            setReservationData(data);
            setSelectedBasketRow(null);
            basketRowReducer({ method: 'Clear' });
            const row = new BasketRow_1.default();
            row.setStartDate((0, dayjs_1.default)().add(1, 'day'));
            row.setEndDate((0, dayjs_1.default)().add(2, 'day'));
            basketRowReducer({ method: 'Add', row });
            setBasketAddonRows([]);
            setBasketFeeRows([]);
            const sessionProvider = (_a = beContext === null || beContext === void 0 ? void 0 : beContext.engine) === null || _a === void 0 ? void 0 : _a.getSessionProvider();
            sessionProvider.deleteSession();
            sessionProvider === null || sessionProvider === void 0 ? void 0 : sessionProvider.initialize(undefined, (_b = beContext === null || beContext === void 0 ? void 0 : beContext.engine) === null || _b === void 0 ? void 0 : _b.getSource());
            const params = new URLSearchParams(window.location.search);
            params.delete('planpay_checkout_id');
            // MPGS params
            params.delete('resultIndicator');
            params.delete('sessionVersion');
            params.delete('checkoutVersion');
            // Stripe params
            params.delete('payment_intent');
            params.delete('payment_intent_client_secret');
            params.delete('redirect_status');
            window.history.replaceState({}, '', `${window.location.pathname}?${params}`);
            navigate(StepThanks_1.default.getStepUrl());
        },
        /**
         * This is called when the users' payment was successfully verified, but there is additional payment required.
         */
        additionalPaymentRequired: (data) => __awaiter(this, void 0, void 0, function* () {
            var _a;
            const currentSession = (_a = beContext.sessionProvider) === null || _a === void 0 ? void 0 : _a.getSession();
            const resRequest = currentSession === null || currentSession === void 0 ? void 0 : currentSession.reservationRequest;
            const resData = reservationData;
            if (!resData)
                return;
            if (resRequest === null || resRequest === void 0 ? void 0 : resRequest.payment) {
                // Delete SplitPaymentAmount because from now any requests will be for the remaining amount
                delete resRequest.payment.SplitPaymentAmount;
            }
            resData.payment = data;
            yield beContext.sessionProvider.updateSessionAsync({ reservation: resData, reservationRequest: resRequest });
            setReservationData(resData);
        }),
        finish: () => {
            setReservationData(null);
            setHasCheckedOut(false);
        },
        /**
         * Set the selected basket row to null, ensuring that no row is selected for editing.
         * When the group booking limit is reached.
         * Ensure a basket row is always selected when navigating back to the Room step, allowing the guest to change the room rate directly.
         */
        clearSelectedRates: () => {
            if (canAddAnotherRoom) {
                setSelectedBasketRow(null);
            }
        },
        getRowForAddon: (selectedAddon) => {
            if (selectedAddon === undefined) {
                return;
            }
            return basketAddonRows.find((r) => { var _a; return ((_a = r.getAddon()) === null || _a === void 0 ? void 0 : _a.code) === (selectedAddon === null || selectedAddon === void 0 ? void 0 : selectedAddon.code); });
        },
        setAddonQuantities: (addon, quantities, selectedDate, selectedTime) => {
            var _a, _b, _c;
            let addonRow = basketAddonRows.find((r) => { var _a; return ((_a = r.getAddon()) === null || _a === void 0 ? void 0 : _a.code) === addon.code; });
            if (addonRow === undefined) {
                addonRow = new BasketAddonRow_1.default(addon);
                basketAddonRows.push(addonRow);
            }
            addonRow.setQuantity((_a = quantities.quantity) !== null && _a !== void 0 ? _a : 0);
            addonRow.setAdultQuantity((_b = quantities.adult) !== null && _b !== void 0 ? _b : 0);
            addonRow.setChildQuantity((_c = quantities.child) !== null && _c !== void 0 ? _c : 0);
            addonRow.setSelectedDate(selectedDate);
            addonRow.setSelectedTime(addon.availability[selectedDate].selectorTimes ? selectedTime : undefined);
            // TODO: Quick hack to fix Synxis Basic Addon quantities.
            if (addon.pricingType === core_1.EAddonPricingType.PER_ADULT_OCCUPANCY_PER_NIGHT) {
                // TODO: Because all addons only apply to first room, we can select the 0th from the list.
                const basketRow = currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows[0];
                if (basketRow) {
                    addonRow.setAdultQuantity(basketRow.getAdults());
                }
            }
            if (addon.pricingType === core_1.EAddonPricingType.PER_CHILD_OCCUPANCY_PER_NIGHT) {
                // TODO: Because all addons only apply to first room, we can select the 0th from the list.
                const basketRow = currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows[0];
                if (basketRow) {
                    addonRow.setChildQuantity(basketRow.getChildren());
                }
            }
            // TODO: More hackyness to handle RMS Per Person Fee requirements
            if (addon.pricingType === core_1.EAddonPricingType.PER_ALL_PERSONS || addon.pricingType === core_1.EAddonPricingType.PER_ALL_PERSONS_PER_NIGHT) {
                const basketRow = currentBasketRows === null || currentBasketRows === void 0 ? void 0 : currentBasketRows[0];
                if (basketRow) {
                    addonRow.setAdultQuantity(basketRow.getAdults());
                    addonRow.setChildQuantity(basketRow.getChildren());
                }
            }
            setBasketAddonRows([...basketAddonRows]);
        },
        removeBasketAddonRow: (row) => {
            setBasketAddonRows((oldRows) => oldRows.filter((item) => item.getID() !== row.getID()));
        },
        /**
         * Attempts to select the next available basket row that does not have a rate assigned.
         * If such a row is found, it updates the selected basket row and returns it.
         * Otherwise, it does nothing.
         *
         * @returns {BasketRow | undefined} The selected basket row without a rate, or undefined if none is found.
         */
        attemptSelectUnfilledRow: () => {
            const row = getNextAvailableBasketRow();
            if (row && !(row === null || row === void 0 ? void 0 : row.getRate())) {
                // Select the first available row without a rate and update the selected basket row
                setSelectedBasketRow(row);
                return row;
            }
            // No available unfilled row found, return undefined
        },
        // Callback triggered when the DatePicker open state changes.
        onDatePickerOpenChange: (isOpen) => {
            setIsDatePickerOpen(isOpen);
        },
        // Indicates whether the DatePicker is currently open.
        isDatePickerOpen: isDatePickerOpen,
        canAddAnotherRoom: canAddAnotherRoom,
        hasPriceQuoteError: hasPriceQuoteError,
        isQuoteOrPencilReady: isQuoteOrPencilReady,
    };
    return (react_1.default.createElement(contexts_1.BasketContext.Provider, { value: workingContext },
        react_1.default.createElement(contexts_1.AuthenticationContextProvider, null,
            props.children,
            react_1.default.createElement(MemberSignInModal_1.MemberSignInModal, null),
            showPromoCodeAlert && react_1.default.createElement(PromoCodeVerificationAlert_1.PromoCodeVerificationAlert, { selectedBasketRow: selectedBasketRow }))));
}
//# sourceMappingURL=BasketContextWrapper.js.map