/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 * @NModuleScope SameAccount
 * 
 * Script Name: PP_Order_cancel_pocancellation.js
 * Description: Automatically cancels related Purchase Orders when a Sales Order is cancelled.
 *              Processes each Sales Order line to find linked POs and cancels them.
 * 
 * Author: Portal People
 * Date: November 2025
 */

define(['N/record', 'N/search', 'N/log', 'N/runtime'], 
    function(record, search, log, runtime) {

    /**
     * After Submit event - triggered after a Sales Order is saved
     * 
     * @param {Object} context
     * @param {record.Record} context.newRecord - New record
     * @param {record.Record} context.oldRecord - Old record (before changes)
     * @param {string} context.type - Event type
     */
    function afterSubmit(context) {
        try {
            // Only process on edit/xedit operations (when status might change)
            if (context.type !== context.UserEventType.EDIT && 
                context.type !== context.UserEventType.XEDIT) {
                return;
            }

            var newRecord = context.newRecord;
            var oldRecord = context.oldRecord;
            
            // Get the order status
            var newStatus = newRecord.getValue({ fieldId: 'orderstatus' });
            var oldStatus = oldRecord ? oldRecord.getValue({ fieldId: 'orderstatus' }) : null;
            
            // Check if the order has been cancelled
            // Order Status: A = Pending Approval, B = Pending Fulfillment, 
            // C = Cancelled, D = Partially Fulfilled, E = Pending Billing, etc.
            if (newStatus === 'C' && oldStatus !== 'C') {
                log.audit({
                    title: 'Sales Order Cancelled',
                    details: 'Processing SO #' + newRecord.getValue({ fieldId: 'tranid' }) + 
                             ' (ID: ' + newRecord.id + ') for PO cancellation'
                });
                
                cancelRelatedPurchaseOrders(newRecord);
            }
            
        } catch (e) {
            log.error({
                title: 'Error in afterSubmit',
                details: 'Error: ' + e.message + '\nStack: ' + e.stack
            });
        }
    }

    /**
     * Finds and cancels all Purchase Orders linked to the Sales Order lines
     * 
     * @param {record.Record} salesOrderRecord - The cancelled Sales Order record
     */
    function cancelRelatedPurchaseOrders(salesOrderRecord) {
        var salesOrderId = salesOrderRecord.id;
        var salesOrderNumber = salesOrderRecord.getValue({ fieldId: 'tranid' });
        var purchaseOrderIds = [];
        
        try {
            // Get the number of line items in the Sales Order
            var lineCount = salesOrderRecord.getLineCount({ sublistId: 'item' });
            
            log.debug({
                title: 'Processing SO Lines',
                details: 'Found ' + lineCount + ' lines in SO #' + salesOrderNumber
            });
            
            // Loop through each line to find linked POs
            for (var i = 0; i < lineCount; i++) {
                var poId = salesOrderRecord.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'poid', // Purchase Order ID field on SO line
                    line: i
                });
                
                var itemName = salesOrderRecord.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'item_display',
                    line: i
                });
                
                if (poId) {
                    // Add to array if not already present
                    if (purchaseOrderIds.indexOf(poId) === -1) {
                        purchaseOrderIds.push(poId);
                        
                        log.debug({
                            title: 'PO Link Found',
                            details: 'Line ' + (i + 1) + ': Item "' + itemName + 
                                   '" linked to PO ID: ' + poId
                        });
                    }
                }
            }
            
            // If no POs found via line items, search for POs created from this SO
            if (purchaseOrderIds.length === 0) {
                purchaseOrderIds = searchRelatedPurchaseOrders(salesOrderId);
            }
            
            // Cancel each Purchase Order
            if (purchaseOrderIds.length > 0) {
                log.audit({
                    title: 'Cancelling Purchase Orders',
                    details: 'Found ' + purchaseOrderIds.length + ' PO(s) to cancel for SO #' + salesOrderNumber
                });
                
                var cancelledCount = 0;
                var failedCount = 0;
                
                for (var j = 0; j < purchaseOrderIds.length; j++) {
                    if (cancelPurchaseOrder(purchaseOrderIds[j], salesOrderNumber)) {
                        cancelledCount++;
                    } else {
                        failedCount++;
                    }
                }
                
                log.audit({
                    title: 'PO Cancellation Complete',
                    details: 'SO #' + salesOrderNumber + ': Successfully cancelled ' + 
                             cancelledCount + ' PO(s)' + 
                             (failedCount > 0 ? ', ' + failedCount + ' failed' : '')
                });
            } else {
                log.debug({
                    title: 'No POs to Cancel',
                    details: 'No linked Purchase Orders found for SO #' + salesOrderNumber
                });
            }
            
        } catch (e) {
            log.error({
                title: 'Error Processing PO Cancellation',
                details: 'SO #' + salesOrderNumber + ': ' + e.message + '\nStack: ' + e.stack
            });
        }
    }

    /**
     * Search for Purchase Orders related to the Sales Order
     * 
     * @param {string} salesOrderId - Internal ID of the Sales Order
     * @returns {Array} Array of Purchase Order internal IDs
     */
    function searchRelatedPurchaseOrders(salesOrderId) {
        var poIds = [];
        
        try {
            // Search for POs that reference this Sales Order
            var poSearch = search.create({
                type: search.Type.PURCHASE_ORDER,
                filters: [
                    ['type', 'anyof', 'PurchOrd'],
                    'AND',
                    ['mainline', 'is', 'T'],
                    'AND',
                    ['createdfrom', 'anyof', salesOrderId], // PO created from this SO
                    'AND',
                    ['status', 'noneof', ['PurchOrd:H', 'PurchOrd:G']] // Not closed or cancelled
                ],
                columns: [
                    search.createColumn({ name: 'internalid' }),
                    search.createColumn({ name: 'tranid' }),
                    search.createColumn({ name: 'status' })
                ]
            });
            
            var searchResults = poSearch.run().getRange({ start: 0, end: 1000 });
            
            if (searchResults && searchResults.length > 0) {
                for (var i = 0; i < searchResults.length; i++) {
                    var poId = searchResults[i].getValue({ name: 'internalid' });
                    var poNumber = searchResults[i].getValue({ name: 'tranid' });
                    var poStatus = searchResults[i].getText({ name: 'status' });
                    
                    poIds.push(poId);
                    
                    log.debug({
                        title: 'Related PO Found via Search',
                        details: 'PO #' + poNumber + ' (ID: ' + poId + '), Status: ' + poStatus
                    });
                }
            }
            
        } catch (e) {
            log.error({
                title: 'Error Searching for POs',
                details: 'Error: ' + e.message
            });
        }
        
        return poIds;
    }

    /**
     * Cancels a Purchase Order
     * 
     * @param {string} purchaseOrderId - Internal ID of the Purchase Order
     * @param {string} salesOrderNumber - Sales Order number (for logging)
     * @returns {boolean} True if successful, false otherwise
     */
    function cancelPurchaseOrder(purchaseOrderId, salesOrderNumber) {
        try {
            // Load the Purchase Order
            var poRecord = record.load({
                type: record.Type.PURCHASE_ORDER,
                id: purchaseOrderId,
                isDynamic: false
            });
            
            var poNumber = poRecord.getValue({ fieldId: 'tranid' });
            var currentStatus = poRecord.getText({ fieldId: 'status' });
            
            // Check if PO is already cancelled or fully billed/received
            var orderStatus = poRecord.getValue({ fieldId: 'orderstatus' });
            
            // Order Status values: A = Pending Supervisor Approval, B = Pending Receipt,
            // C = Rejected, D = Partially Received, E = Pending Bill/Partially Received,
            // F = Pending Bill, G = Fully Billed, H = Closed
            if (orderStatus === 'H' || orderStatus === 'G') {
                log.audit({
                    title: 'PO Cannot be Cancelled',
                    details: 'PO #' + poNumber + ' (ID: ' + purchaseOrderId + 
                             ') is already ' + currentStatus + 
                             '. Linked to SO #' + salesOrderNumber
                });
                return false;
            }
            
            // Set the order status to Closed/Cancelled
            // Note: Direct cancellation may not be possible if items are received
            // In that case, we close the PO
            var lineCount = poRecord.getLineCount({ sublistId: 'item' });
            var hasReceivedItems = false;
            
            // Check if any items have been received
            for (var i = 0; i < lineCount; i++) {
                var quantityReceived = poRecord.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'quantityreceived',
                    line: i
                }) || 0;
                
                if (quantityReceived > 0) {
                    hasReceivedItems = true;
                    break;
                }
            }
            
            if (hasReceivedItems) {
                // If items are received, we can only close the remaining lines
                log.audit({
                    title: 'PO Partially Received',
                    details: 'PO #' + poNumber + ' has received items. Closing remaining lines. ' +
                             'Linked to SO #' + salesOrderNumber
                });
                
                // Set remaining quantities to 0 (close lines)
                for (var j = 0; j < lineCount; j++) {
                    var qtyOrdered = poRecord.getSublistValue({
                        sublistId: 'item',
                        fieldId: 'quantity',
                        line: j
                    }) || 0;
                    
                    var qtyReceived = poRecord.getSublistValue({
                        sublistId: 'item',
                        fieldId: 'quantityreceived',
                        line: j
                    }) || 0;
                    
                    // Close line if not fully received
                    if (qtyReceived < qtyOrdered) {
                        poRecord.setSublistValue({
                            sublistId: 'item',
                            fieldId: 'isclosed',
                            line: j,
                            value: true
                        });
                    }
                }
            } else {
                // No items received, cancel the entire PO
                // Set memo to indicate cancellation reason
                var currentMemo = poRecord.getValue({ fieldId: 'memo' }) || '';
                var cancellationNote = 'AUTO-CANCELLED: Related Sales Order #' + 
                                      salesOrderNumber + ' was cancelled on ' + 
                                      new Date().toLocaleDateString();
                
                poRecord.setValue({
                    fieldId: 'memo',
                    value: currentMemo + (currentMemo ? '\n' : '') + cancellationNote
                });
                
                // Set all lines to closed
                for (var k = 0; k < lineCount; k++) {
                    poRecord.setSublistValue({
                        sublistId: 'item',
                        fieldId: 'isclosed',
                        line: k,
                        value: true
                    });
                }
            }
            
            // Save the Purchase Order
            var savedPoId = poRecord.save({
                enableSourcing: false,
                ignoreMandatoryFields: true
            });
            
            log.audit({
                title: 'PO Cancelled Successfully',
                details: 'PO #' + poNumber + ' (ID: ' + savedPoId + ') cancelled/closed. ' +
                         'Linked to SO #' + salesOrderNumber
            });
            
            return true;
            
        } catch (e) {
            log.error({
                title: 'Error Cancelling PO',
                details: 'PO ID: ' + purchaseOrderId + ', SO #' + salesOrderNumber + 
                         '\nError: ' + e.message + '\nStack: ' + e.stack
            });
            return false;
        }
    }

    return {
        afterSubmit: afterSubmit
    };
});
