"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoomRate = exports.RoomRatePriceInclusion = void 0;
const core_1 = require("@roomstay/core");
const dayjs_1 = __importDefault(require("dayjs"));
const AvailabilityAPI_1 = __importDefault(require("../../api/AvailabilityAPI"));
const Fee_1 = require("../Fee");
const RoomRateNight_1 = __importDefault(require("./RoomRateNight"));
const StaticContextProvider_1 = __importDefault(require("../../providers/StaticContextProvider"));
var RoomRatePriceInclusion;
(function (RoomRatePriceInclusion) {
    RoomRatePriceInclusion["ExcludingFees"] = "Fees Excluded";
    RoomRatePriceInclusion["ExcludingTaxes"] = "Taxes Excluded";
    RoomRatePriceInclusion["ExcludingFeesAndTaxes"] = "Excluding Fees and Taxes";
    RoomRatePriceInclusion["IncludingFeesAndTaxes"] = "Including Fees and Taxes";
})(RoomRatePriceInclusion || (exports.RoomRatePriceInclusion = RoomRatePriceInclusion = {}));
class RoomRate {
    constructor() {
        this.mealsIncluded = {
            breakfast: false,
            lunch: false,
            dinner: false,
        };
        this.getTotalPrice = (getCost = false) => {
            let total = 0;
            const nightsAsArray = Object.values(this.nightRates);
            nightsAsArray.forEach((night) => {
                total += Number(getCost ? night.cost : night.total);
            });
            return total;
        };
        this.getTotalPriceBeforeDiscount = (getCost = false) => {
            let total = 0;
            const nightsAsArray = Object.values(this.nightRates);
            nightsAsArray.forEach((night) => {
                total += Number(getCost ? night.cost : night.total) + Number(night.discount);
            });
            return total;
        };
        this.getTotalDiscount = () => {
            return this.nightRateTotal((night) => {
                return Number(night.discount);
            });
        };
        this.isStayXPayYRate = () => {
            return !!(this.nightsToDiscount && this.nightsToDiscount > 0);
        };
        this.getAveragePrice = (getCost = false) => {
            const total = this.getTotalPrice(getCost);
            return total / Object.keys(this.nightRates).length;
        };
        /**
         * Average nightly price before ANY discount (real + artificial combined correctly)
         */
        this.getAveragePriceBeforeDiscount = (getCost = false) => {
            const nights = Object.values(this.nightRates);
            if (!nights.length)
                return 0;
            // Step 1: calculate true original (before real discounts)
            let originalTotal = 0;
            nights.forEach((night) => {
                var _a;
                const base = Number(getCost ? night.cost : night.total);
                const realDiscount = Number((_a = night.discount) !== null && _a !== void 0 ? _a : 0);
                originalTotal += base + realDiscount;
            });
            let averageOriginal = originalTotal / nights.length;
            // Step 2: inflate using artificial discount
            if (this.artificialDiscountPercent != null) {
                averageOriginal = (100 * averageOriginal) / (100 - this.artificialDiscountPercent);
            }
            if (this.artificialDiscountAmount != null) {
                averageOriginal += this.artificialDiscountAmount;
            }
            return averageOriginal;
        };
        this.getTotalFees = () => {
            return this.nightRateTotal((night) => {
                return Number(night.fee);
            });
        };
        this.getItemisedFees = (hotelFees) => {
            const feeMap = new Map();
            this.getNightRateArray().forEach((night) => {
                if (night.fees) {
                    const fees = (0, Fee_1.mapSimpleFeesToItemisedFees)(night.fees, hotelFees);
                    (0, Fee_1.accumulateFees)(feeMap, fees);
                }
            });
            return (0, Fee_1.feeMapToArray)(feeMap);
        };
        this.getTotalTaxes = () => {
            return this.nightRateTotal((night) => {
                return Number(night.tax);
            });
        };
        this.getPriceType = () => {
            let hasFees = false;
            let hasTax = false;
            Object.values(this.nightRates).forEach((night) => {
                if (night.fee && night.fee !== '0') {
                    hasFees = true;
                }
                if (night.tax && night.tax !== '0') {
                    hasTax = true;
                }
            });
            if (hasFees && hasTax) {
                return RoomRatePriceInclusion.ExcludingFeesAndTaxes;
            }
            else if (hasFees) {
                return RoomRatePriceInclusion.ExcludingTaxes;
            }
            else if (hasTax) {
                return RoomRatePriceInclusion.ExcludingFees;
            }
            else {
                return RoomRatePriceInclusion.IncludingFeesAndTaxes;
            }
        };
        this.getGuaranteePolicy = () => {
            var _a;
            return (_a = AvailabilityAPI_1.default.policyMappings.guarantee[this.guaranteeCode]) !== null && _a !== void 0 ? _a : '';
        };
        this.getCancellationPolicy = () => {
            var _a;
            return (_a = AvailabilityAPI_1.default.policyMappings.cancel[this.cancelPolicyCode]) !== null && _a !== void 0 ? _a : '';
        };
        this.getPriceSortValue = () => {
            return this.getAveragePrice();
        };
        this.getNightRateArray = () => {
            if (!this.nightRatesArray.length) {
                this.nightRatesArray = Object.values(this.nightRates);
            }
            return this.nightRatesArray;
        };
        this.reduceNightData = (reducer) => {
            var _a;
            return (_a = this.getNightRateArray().reduce(reducer, 0)) !== null && _a !== void 0 ? _a : 0;
        };
        this.nightRateTotal = (value) => {
            return this.reduceNightData((sum, redNight) => {
                const summingValue = value(redNight);
                return sum ? sum + summingValue : summingValue;
            });
        };
        this.datesOverlap = (startDate1, endDate1, startDate2, endDate2) => {
            const overlap1 = startDate1.isSameOrBefore(endDate2) && startDate2.isSameOrBefore(endDate1);
            const overlap2 = startDate1.isSameOrAfter(endDate2) && startDate2.isSameOrAfter(endDate1);
            return overlap1 || overlap2;
        };
        this.getDefaultLabelsByDate = (requestedDate) => {
            const days = [];
            let currentDate = requestedDate.startDate.clone();
            while (currentDate.isSameOrBefore(requestedDate.endDate)) {
                days.push({
                    label: currentDate.format('dddd, MMM DD, YYYY'),
                    alternateLabel: '', // No rate label available
                });
                currentDate = currentDate.add(1, 'day');
            }
            return days;
        };
        this.getPossiblyOverwrittenDetails = (requestedDate) => {
            const rateOverrides = this.getAvailableRateOverrides();
            if (!rateOverrides.length)
                return { name: this.name, shortDescription: this.description, longDescription: this.longDescription };
            const originalRequestedStartDate = requestedDate.startDate;
            let shortDescription = this.description;
            let longDescription = this.longDescription;
            let name = this.name;
            // Loop through all overrides and check for any matching the booking dates
            rateOverrides.forEach((override) => {
                if (!originalRequestedStartDate)
                    return;
                const queryStartDate = (0, dayjs_1.default)(override.queryStartDate);
                const queryEndDate = (0, dayjs_1.default)(override.queryEndDate);
                const overlap = (!override.queryStartDate || queryStartDate.isSameOrBefore(originalRequestedStartDate, 'date')) &&
                    (!override.queryEndDate || queryEndDate.isSameOrAfter(originalRequestedStartDate, 'date'));
                const stayDay = originalRequestedStartDate.format('dddd');
                if (overlap && override.enabledPerDay[stayDay] === true) {
                    name = override.name;
                    shortDescription = override.shortDescription;
                    longDescription = override.longDescription;
                }
            });
            return { name, shortDescription, longDescription };
        };
        this.getAvailableRateOverrides = () => {
            var _a;
            if (!((_a = this.rateOverrides) === null || _a === void 0 ? void 0 : _a.length))
                return [];
            const now = (0, dayjs_1.default)();
            //startDate/endDate/queryStartDate/queryEndDate must not empty
            const rateOverrides = this.rateOverrides.filter((override) => {
                // Check if the override can apply to this query in current time.
                // We don't require any of the filter fields, so if they are set we should check that they apply.
                if (override.startDate) {
                    if (!(0, dayjs_1.default)(override.startDate).isSameOrBefore(now)) {
                        return false;
                    }
                    if (override.endDate && !(0, dayjs_1.default)(override.endDate).isSameOrAfter(now)) {
                        return false;
                    }
                }
                return true;
            });
            return rateOverrides;
        };
        this.hasRateVariesByDay = (requestedDate) => {
            // Validate startDate/endDate
            if (!(requestedDate === null || requestedDate === void 0 ? void 0 : requestedDate.startDate) || !requestedDate.endDate)
                return false;
            const rateOverrides = this.getAvailableRateOverrides();
            if (!rateOverrides.length)
                false;
            const originalRequestedStartDate = (0, dayjs_1.default)(requestedDate.startDate);
            const originalRequestedEndDate = (0, dayjs_1.default)(requestedDate.endDate);
            // Loop through all overrides and check for any matching the booking dates
            return rateOverrides.some((override) => {
                const overrideStartDate = (0, dayjs_1.default)(override.startDate);
                const overrideEndDate = (0, dayjs_1.default)(override.endDate);
                const queryStartDate = (0, dayjs_1.default)(override.queryStartDate);
                const queryEndDate = (0, dayjs_1.default)(override.queryEndDate);
                if (override.startDate && overrideStartDate.isAfter(originalRequestedEndDate)) {
                    return false;
                }
                if (override.endDate && overrideEndDate.isBefore(originalRequestedStartDate)) {
                    return false;
                }
                if (override.queryStartDate && queryStartDate.isAfter(originalRequestedEndDate)) {
                    return false;
                }
                if (override.queryEndDate && queryEndDate.isBefore(originalRequestedStartDate)) {
                    return false;
                }
                return true;
            });
        };
        this.getDisplayRates = (requestedDate) => {
            const rateOverrides = this.getAvailableRateOverrides();
            if (!rateOverrides.length)
                return [];
            const originalRequestedStartDate = (0, dayjs_1.default)(requestedDate.startDate);
            const originalRequestedEndDate = (0, dayjs_1.default)(requestedDate.endDate);
            const displayRates = [
                {
                    name: this.name,
                    shortDescription: this.description,
                    longDescription: this.longDescription,
                    override: {
                        name: this.name,
                        shortDescription: this.description,
                        longDescription: this.longDescription,
                        enabledPerDay: {
                            Monday: true,
                            Tuesday: true,
                            Wednesday: true,
                            Thursday: true,
                            Friday: true,
                            Saturday: true,
                            Sunday: true,
                        },
                        startDate: '',
                        endDate: '',
                        queryStartDate: '',
                        queryEndDate: '',
                    },
                },
            ];
            // Loop through all overrides and check for any matching the booking dates
            rateOverrides.forEach((override) => {
                const overrideStartDate = (0, dayjs_1.default)(override.startDate);
                const overrideEndDate = (0, dayjs_1.default)(override.endDate);
                const queryStartDate = (0, dayjs_1.default)(override.queryStartDate);
                const queryEndDate = (0, dayjs_1.default)(override.queryEndDate);
                //requestedStartDate & requestedEndDate must be in date range of override's startDate/endDate
                const requestedStartDate = originalRequestedStartDate.isBefore(overrideStartDate) ? overrideStartDate : originalRequestedStartDate;
                const requestedEndDate = originalRequestedEndDate.isAfter(overrideEndDate) ? overrideEndDate : originalRequestedEndDate;
                const overlap = this.datesOverlap(requestedStartDate, requestedEndDate, queryStartDate, queryEndDate);
                if (overlap) {
                    if (requestedStartDate.isBefore(queryStartDate, 'date')) {
                        if (queryEndDate.isBefore(requestedEndDate, 'date')) {
                            displayRates.push({
                                override: override,
                                name: override.name,
                                shortDescription: override.shortDescription,
                                longDescription: override.longDescription,
                                startDate: queryStartDate,
                                endDate: queryEndDate,
                            });
                        }
                        else {
                            displayRates.push({
                                override: override,
                                name: override.name,
                                shortDescription: override.shortDescription,
                                longDescription: override.longDescription,
                                startDate: queryStartDate,
                                endDate: requestedEndDate,
                            });
                        }
                    }
                    else {
                        if (queryEndDate.isBefore(requestedEndDate, 'date')) {
                            displayRates.push({
                                override: override,
                                name: override.name,
                                shortDescription: override.shortDescription,
                                longDescription: override.longDescription,
                                startDate: requestedStartDate,
                                endDate: queryEndDate,
                            });
                        }
                        else {
                            displayRates.push({
                                override: override,
                                name: override.name,
                                shortDescription: override.shortDescription,
                                longDescription: override.longDescription,
                                startDate: requestedStartDate,
                                endDate: requestedEndDate,
                            });
                        }
                    }
                }
            });
            return displayRates;
        };
        this.displayRateToDayLabel = (displayRate) => {
            if (!displayRate.startDate || !displayRate.endDate)
                return '';
            const dayDayLabels = {};
            const startDayLabel = displayRate.startDate.format('MMM DD, YYYY');
            const endDayLabel = displayRate.endDate.format('MMM DD, YYYY');
            dayDayLabels[startDayLabel] = startDayLabel;
            dayDayLabels[endDayLabel] = endDayLabel;
            return `${Object.values(dayDayLabels).join(' - ')}`;
        };
        this.displayRateToDayName = (displayRate) => {
            if (!displayRate.startDate || !displayRate.endDate)
                return '';
            const dayNames = {};
            const startDayName = displayRate.startDate.format('dddd');
            const endDayName = displayRate.endDate.format('dddd');
            dayNames[startDayName] = startDayName;
            dayNames[endDayName] = endDayName;
            return `${Object.values(dayNames).join(' - ')}`;
        };
        this.getLabelsByDate = (requestedDate) => {
            const overrides = this.getAvailableRateOverrides();
            if (!overrides.length) {
                // If overrides are null or empty, return an empty rateLabelByDay for the whole date range
                return this.getDefaultLabelsByDate(requestedDate);
            }
            const labelsByDate = {};
            let dayOneOverride = null;
            overrides.forEach((override) => {
                var _a, _b, _c, _d, _e;
                const overrideStartDate = (0, dayjs_1.default)(override.startDate);
                const overrideEndDate = (0, dayjs_1.default)(override.endDate);
                const overrideQueryStartDate = (0, dayjs_1.default)(override.queryStartDate);
                const overrideQueryEndDate = (0, dayjs_1.default)(override.queryEndDate);
                const isApplicableForStayDate = () => {
                    if (overrideStartDate && overrideStartDate.isAfter(stayDate)) {
                        return false;
                    }
                    if (overrideEndDate && overrideEndDate.isBefore(stayDate)) {
                        return false;
                    }
                    if (overrideQueryStartDate && overrideQueryStartDate.isAfter(stayDate)) {
                        return false;
                    }
                    if (overrideQueryEndDate && overrideQueryEndDate.isBefore(stayDate)) {
                        return false;
                    }
                    return true;
                };
                let stayDate = requestedDate.startDate.clone();
                while (stayDate.isSameOrBefore(requestedDate.endDate, 'date')) {
                    const dayIndex = stayDate.format('YYYY-MM-DD');
                    const dayLabel = stayDate.format('dddd, MMM DD, YYYY');
                    const stayDay = stayDate.format('dddd');
                    //stayDate must be in date range of override's startDate/endDate and queryStartDate/queryEndDate
                    if (isApplicableForStayDate()) {
                        if (override.enabledPerDay[stayDay] === true) {
                            // Keep setting the dayOneOverride until the first day of the stay is reached, this will tell us
                            // what is the initial rate label for the stay
                            if (stayDate.isSame(requestedDate.startDate, 'date')) {
                                dayOneOverride = override;
                            }
                            labelsByDate[dayIndex] = {
                                label: dayLabel,
                                date: dayIndex,
                                alternateLabel: override.name,
                                shortDescription: override.shortDescription,
                                longDescription: override.longDescription,
                            };
                        }
                    }
                    else if (!((_a = labelsByDate[dayIndex]) === null || _a === void 0 ? void 0 : _a.label)) {
                        // No rate label override
                        labelsByDate[dayIndex] = {
                            label: dayLabel,
                            date: dayIndex,
                            alternateLabel: '',
                        };
                    }
                    if (dayOneOverride === override) {
                        // If the first day of the stay is an override, the user thinks all dates are the same, so we
                        // need to remove the override from this day, and specify the default for the remaining empty days.
                        if (((_b = labelsByDate[dayIndex]) === null || _b === void 0 ? void 0 : _b.label) === dayLabel && ((_c = labelsByDate[dayIndex]) === null || _c === void 0 ? void 0 : _c.alternateLabel) === override.name) {
                            labelsByDate[dayIndex] = {
                                label: dayLabel,
                                date: dayIndex,
                                alternateLabel: '',
                                shortDescription: '',
                                longDescription: '',
                            };
                        }
                        else if (((_d = labelsByDate[dayIndex]) === null || _d === void 0 ? void 0 : _d.alternateLabel) === '') {
                            labelsByDate[dayIndex] = {
                                label: dayLabel,
                                date: dayIndex,
                                alternateLabel: this.name,
                                shortDescription: this.description,
                                longDescription: this.longDescription,
                            };
                        }
                    }
                    // If label dates aren't populated, they're the originl rate labels.
                    if (!((_e = labelsByDate[dayIndex]) === null || _e === void 0 ? void 0 : _e.label)) {
                        labelsByDate[dayIndex] = {
                            label: dayLabel,
                            date: dayIndex,
                            alternateLabel: dayOneOverride === override ? this.name : '',
                            shortDescription: this.description,
                            longDescription: this.longDescription,
                        };
                    }
                    stayDate = stayDate.add(1, 'day');
                }
            });
            return Object.values(labelsByDate).sort((a, b) => {
                return a.date > b.date ? 1 : -1;
            });
        };
        this.nightRates = {};
        this.nightRatesArray = [];
    }
    static fromData(data) {
        var _a;
        const rate = new this();
        rate.applyData(data);
        if ((_a = StaticContextProvider_1.default.MemberOnlyRateCodes) === null || _a === void 0 ? void 0 : _a.includes(data.code)) {
            rate.isMemberOnly = true;
        }
        return rate;
    }
    applyData(data) {
        Object.keys(data).map((key) => {
            this[key] = data[key];
        }, this);
    }
    setRoom(room) {
        this.room = room;
    }
    getRoom() {
        return this.room;
    }
    /**
     * Do not use this method if RMS deposit quotes are enabled.
     */
    getDepositAccordingToGuaranteePolicy(bookingRateFees) {
        const depositPercentage = this.getGuaranteePolicy().depositPercentage;
        const nightsAsArray = this.getNightRateArray();
        const totalPriceIncludingAllFees = this.getTotalPrice() + bookingRateFees;
        switch (this.getGuaranteePolicy().depositMode) {
            case core_1.EGuaranteePolicyDepositMode.None:
                return 0;
            case core_1.EGuaranteePolicyDepositMode.Full:
                return totalPriceIncludingAllFees;
            case core_1.EGuaranteePolicyDepositMode.Percentage:
                if (depositPercentage) {
                    return totalPriceIncludingAllFees * (depositPercentage / 100);
                }
                return totalPriceIncludingAllFees;
            case core_1.EGuaranteePolicyDepositMode.FirstNight:
                return Number(nightsAsArray[0].total) + bookingRateFees / nightsAsArray.length;
            case null:
                throw Error('Cannot calculate deposit without a deposit mode');
        }
    }
    /**
     * Not implemented
     */
    getRecommendedSortValue() {
        return 0;
    }
    getOrderSortValue() {
        return 0;
    }
    serialize() {
        const serializedNightRates = {};
        Object.keys(this.nightRates).forEach((date) => {
            serializedNightRates[date] = this.nightRates[date].serialize();
        });
        const toSerialize = {
            name: this.name,
            code: this.code,
            description: this.description,
            longDescription: this.longDescription,
            isMemberOnly: this.isMemberOnly,
            guaranteeCode: this.guaranteeCode,
            cancelPolicyCode: this.cancelPolicyCode,
            nightRates: serializedNightRates,
            lowestPrice: this.lowestPrice,
            promoCode: this.promoCode,
            promoCodeName: this.promoCodeName,
            mealsIncluded: this.mealsIncluded,
            rateOverrides: this.rateOverrides,
            minNightStayOverride: this.minNightStayOverride,
            isUpsellRate: this.isUpsellRate,
            nightsToDiscount: this.nightsToDiscount,
            artificialDiscountAmount: this.artificialDiscountAmount,
            artificialDiscountPercent: this.artificialDiscountPercent,
        };
        return toSerialize;
    }
    unSerialize(data) {
        var _a;
        const { name, code, description, longDescription, isMemberOnly, guaranteeCode, cancelPolicyCode, lowestPrice, promoCode, promoCodeName, mealsIncluded, nightsToDiscount } = data;
        const unSerializedNightRates = {};
        if (data.nightRates) {
            (_a = Object.keys(data.nightRates)) === null || _a === void 0 ? void 0 : _a.forEach((date) => {
                const nightRate = new RoomRateNight_1.default();
                unSerializedNightRates[date] = nightRate.unSerialize(data.nightRates[date]);
            });
        }
        this.name = name;
        this.code = code;
        this.description = description;
        this.longDescription = longDescription;
        this.isMemberOnly = isMemberOnly;
        this.guaranteeCode = guaranteeCode;
        this.cancelPolicyCode = cancelPolicyCode;
        this.lowestPrice = lowestPrice;
        this.promoCode = promoCode;
        this.promoCodeName = promoCodeName;
        this.mealsIncluded = mealsIncluded;
        this.nightRates = unSerializedNightRates;
        this.nightRatesArray = [];
        this.rateOverrides = data.rateOverrides;
        this.minNightStayOverride = data.minNightStayOverride;
        this.isUpsellRate = data.isUpsellRate;
        this.nightsToDiscount = nightsToDiscount;
        this.artificialDiscountAmount = data.artificialDiscountAmount;
        this.artificialDiscountPercent = data.artificialDiscountPercent;
        return this;
    }
}
exports.RoomRate = RoomRate;
//# sourceMappingURL=RoomRate.js.map