"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OrderByDocumentProducerComparator = void 0;
// TODO: this smells funny
/** @hidden */
const TYPEORDCOMPARATOR = Object.freeze({
    NoValue: {
        ord: 0,
    },
    undefined: {
        ord: 1,
    },
    boolean: {
        ord: 2,
        compFunc: (a, b) => {
            return a === b ? 0 : a > b ? 1 : -1;
        },
    },
    number: {
        ord: 4,
        compFunc: (a, b) => {
            return a === b ? 0 : a > b ? 1 : -1;
        },
    },
    string: {
        ord: 5,
        compFunc: (a, b) => {
            return a === b ? 0 : a > b ? 1 : -1;
        },
    },
});
/** @hidden */
class OrderByDocumentProducerComparator {
    sortOrder;
    constructor(sortOrder) {
        this.sortOrder = sortOrder;
    } // TODO: This should be an enum
    /**
     * Compares document producers based on their partition key range minInclusive values.
     * Uses minEPK as a tie-breaker when minInclusive values are equal.
     */
    targetPartitionKeyRangeDocProdComparator(docProd1, docProd2) {
        const a = docProd1.getTargetPartitionKeyRange().minInclusive;
        const b = docProd2.getTargetPartitionKeyRange().minInclusive;
        // Primary comparison using minInclusive (ascending lexicographic order)
        if (a !== b) {
            return a < b ? -1 : 1;
        }
        // Tie-breaker: use minEPK when minInclusive values are equal
        const epkA = docProd1.startEpk;
        const epkB = docProd2.startEpk;
        return epkA !== undefined && epkB !== undefined ? (epkA < epkB ? -1 : epkA > epkB ? 1 : 0) : 0;
    }
    compare(docProd1, docProd2) {
        // Need to check for split, since we don't want to dereference "item" of undefined / exception
        if (docProd1.gotSplit()) {
            return -1;
        }
        if (docProd2.gotSplit()) {
            return 1;
        }
        const orderByItemsRes1 = this.getOrderByItems(docProd1.peekBufferedItems()[0]);
        const orderByItemsRes2 = this.getOrderByItems(docProd2.peekBufferedItems()[0]);
        // validate order by items and types
        // TODO: once V1 order by on different types is fixed this need to change
        this.validateOrderByItems(orderByItemsRes1, orderByItemsRes2);
        // no async call in the for loop
        for (let i = 0; i < orderByItemsRes1.length; i++) {
            // compares the orderby items one by one
            const compRes = this.compareOrderByItem(orderByItemsRes1[i], orderByItemsRes2[i]);
            if (compRes !== 0) {
                if (this.sortOrder[i] === "Ascending") {
                    return compRes;
                }
                else if (this.sortOrder[i] === "Descending") {
                    return -compRes;
                }
            }
        }
        // If all ORDER BY comparisons result in equality, use partition range as tie-breaker
        const partitionRangeResult = this.targetPartitionKeyRangeDocProdComparator(docProd1, docProd2);
        return partitionRangeResult;
    }
    // TODO: This smells funny
    compareValue(item1, type1, item2, type2) {
        if (type1 === "object" || type2 === "object") {
            throw new Error("Tried to compare an object type");
        }
        const type1Ord = TYPEORDCOMPARATOR[type1].ord;
        const type2Ord = TYPEORDCOMPARATOR[type2].ord;
        const typeCmp = type1Ord - type2Ord;
        if (typeCmp !== 0) {
            // if the types are different, use type ordinal
            return typeCmp;
        }
        // both are of the same type
        if (type1Ord === TYPEORDCOMPARATOR["undefined"].ord ||
            type1Ord === TYPEORDCOMPARATOR["NoValue"].ord) {
            // if both types are undefined or Null they are equal
            return 0;
        }
        const compFunc = TYPEORDCOMPARATOR[type1].compFunc;
        if (typeof compFunc === "undefined") {
            throw new Error("Cannot find the comparison function");
        }
        // same type and type is defined compare the items
        return compFunc(item1, item2);
    }
    compareOrderByItem(orderByItem1, orderByItem2) {
        return this.compareValue(orderByItem1["item"], this.getType(orderByItem1), orderByItem2["item"], this.getType(orderByItem2));
    }
    validateOrderByItems(res1, res2) {
        if (res1.length !== res2.length) {
            throw new Error(`Expected ${res1.length}, but got ${res2.length}.`);
        }
        if (res1.length !== this.sortOrder.length) {
            throw new Error("orderByItems cannot have a different size than sort orders.");
        }
        for (let i = 0; i < this.sortOrder.length; i++) {
            const type1 = this.getType(res1[i]);
            const type2 = this.getType(res2[i]);
            if (type1 !== type2) {
                throw new Error(`Expected ${type1}, but got ${type2}. Cannot execute cross partition order-by queries on mixed types. Consider filtering your query using IS_STRING or IS_NUMBER to get around this exception.`);
            }
        }
    }
    getType(orderByItem) {
        // TODO: any item?
        if (orderByItem === undefined || orderByItem.item === undefined) {
            return "NoValue";
        }
        const type = typeof orderByItem.item;
        if (TYPEORDCOMPARATOR[type] === undefined) {
            throw new Error(`unrecognizable type ${type}`);
        }
        return type;
    }
    getOrderByItems(res) {
        // TODO: any res?
        return res["orderByItems"];
    }
}
exports.OrderByDocumentProducerComparator = OrderByDocumentProducerComparator;
//# sourceMappingURL=orderByDocumentProducerComparator.js.map