"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.undoLoad = void 0;
const typeorm_1 = require("typeorm");
const sales_base_1 = require("@things-factory/sales-base");
const warehouse_base_1 = require("@things-factory/warehouse-base");
const worksheet_base_1 = require("@things-factory/worksheet-base");
exports.undoLoad = {
    async undoLoad(_, { doId }, context) {
        const { domain, tx, user } = context.state;
        const deliveryOrder = await tx.getRepository(sales_base_1.DeliveryOrder).findOne({
            where: { id: doId, domain },
            relations: ['orderInventories', 'orderInventories.inventory', 'releaseGood', 'releaseGood.orderInventories']
        });
        if (!deliveryOrder) {
            throw new Error(`Delivery order not found`);
        }
        const releaseGood = deliveryOrder.releaseGood;
        const doInventories = deliveryOrder.orderInventories;
        if (!doInventories.length) {
            await tx.getRepository(sales_base_1.DeliveryOrder).delete(deliveryOrder.id);
            return true;
        }
        // OPTIMIZATION 1: Batch fetch all worksheet details in one query instead of N queries
        const doInventoryIds = doInventories.map(oi => oi.id);
        const worksheetDetails = await tx.getRepository(worksheet_base_1.WorksheetDetail).find({
            where: {
                targetInventory: { id: (0, typeorm_1.In)(doInventoryIds) },
                type: worksheet_base_1.WORKSHEET_TYPE.LOADING,
                status: worksheet_base_1.WORKSHEET_STATUS.DONE
            },
            relations: ['targetInventory']
        });
        // Create a map for O(1) lookup of worksheet details by targetInventory.id
        const wsdByTargetId = new Map();
        worksheetDetails.forEach((wsd) => {
            var _a;
            const targetInvId = (_a = wsd.targetInventory) === null || _a === void 0 ? void 0 : _a.id;
            if (targetInvId) {
                wsdByTargetId.set(targetInvId, wsd);
            }
        });
        // OPTIMIZATION 2: Create a map for O(1) lookup of previous (split) order inventories
        // Key: inventoryId_productId_productDetailId, Value: OrderInventory with LOADING status
        const previousOiMap = new Map();
        const doInventoryIdSet = new Set(doInventoryIds);
        releaseGood.orderInventories.forEach(roInventory => {
            if (!doInventoryIdSet.has(roInventory.id) && roInventory.status === sales_base_1.ORDER_INVENTORY_STATUS.LOADING) {
                const key = `${roInventory.inventoryId}_${roInventory.productId}_${roInventory.productDetailId}`;
                previousOiMap.set(key, roInventory);
            }
        });
        // Categorize order inventories for batch processing
        const toTerminate = [];
        const toRevert = [];
        for (const doInventory of doInventories) {
            const worksheetDetail = wsdByTargetId.get(doInventory.id);
            const lookupKey = `${doInventory.inventoryId}_${doInventory.productId}_${doInventory.productDetailId}`;
            const previousOi = previousOiMap.get(lookupKey);
            if (previousOi) {
                toTerminate.push({ doInventory, previousOi, wsd: worksheetDetail });
            }
            else {
                toRevert.push({ doInventory, wsd: worksheetDetail });
            }
        }
        // OPTIMIZATION 3: Batch update operations
        const now = new Date();
        // Process terminations (split order inventories)
        if (toTerminate.length > 0) {
            // Update previous OIs with combined quantities - must be done individually due to different values
            await Promise.all(toTerminate.map(({ doInventory, previousOi }) => tx.getRepository(sales_base_1.OrderInventory).update(previousOi.id, {
                releaseQty: previousOi.releaseQty + doInventory.releaseQty,
                pickedQty: previousOi.pickedQty + doInventory.pickedQty,
                sortedQty: previousOi.sortedQty + doInventory.sortedQty,
                releaseUomValue: previousOi.releaseUomValue + doInventory.releaseUomValue,
                updater: user,
                updatedAt: now
            })));
            // Batch terminate the DO inventories
            const terminateIds = toTerminate.map(t => t.doInventory.id);
            await tx.getRepository(sales_base_1.OrderInventory).update({ id: (0, typeorm_1.In)(terminateIds) }, {
                deliveryOrder: null,
                status: sales_base_1.ORDER_INVENTORY_STATUS.TERMINATED,
                updatedAt: now,
                updater: user
            });
            // Batch delete worksheet details
            const wsdToDeleteIds = toTerminate.filter(t => { var _a; return (_a = t.wsd) === null || _a === void 0 ? void 0 : _a.id; }).map(t => t.wsd.id);
            if (wsdToDeleteIds.length > 0) {
                await tx.getRepository(worksheet_base_1.WorksheetDetail).delete({ id: (0, typeorm_1.In)(wsdToDeleteIds) });
            }
        }
        // Process reversions (non-split order inventories)
        if (toRevert.length > 0) {
            // Batch update order inventories to LOADING status
            const revertOiIds = toRevert.map(r => r.doInventory.id);
            await tx.getRepository(sales_base_1.OrderInventory).update({ id: (0, typeorm_1.In)(revertOiIds) }, {
                deliveryOrder: null,
                status: sales_base_1.ORDER_INVENTORY_STATUS.LOADING,
                loadedAt: null,
                loadedByUser: null,
                loadedBy: null,
                updater: user
            });
            // Batch update worksheet details to EXECUTING status
            const wsdToUpdateIds = toRevert.filter(r => { var _a; return (_a = r.wsd) === null || _a === void 0 ? void 0 : _a.id; }).map(r => r.wsd.id);
            if (wsdToUpdateIds.length > 0) {
                await tx.getRepository(worksheet_base_1.WorksheetDetail).update({ id: (0, typeorm_1.In)(wsdToUpdateIds) }, {
                    status: worksheet_base_1.WORKSHEET_STATUS.EXECUTING,
                    updater: user,
                    updatedAt: now
                });
            }
        }
        // OPTIMIZATION 4: Generate inventory histories in parallel
        await Promise.all(doInventories.map(doInventory => {
            const inventory = doInventory.inventory;
            return (0, warehouse_base_1.generateInventoryHistory)(inventory, releaseGood, warehouse_base_1.INVENTORY_TRANSACTION_TYPE.UNDO_LOADING, 0, 0, user, tx);
        }));
        await tx.getRepository(sales_base_1.DeliveryOrder).delete(deliveryOrder.id);
        return true;
    }
};
//# sourceMappingURL=undo-load.js.map