/**
 * @description Batch class to migrate CloudCraze order history to Salesforce
 * Handles E_Order__c → Order, E_OrderItem__c → OrderItem, and shipping info
 * Maintains complete relationship chain and validates data integrity
 * @author Senior Developer, NULogic
 * @version 1.0
 */
global class CCRZOrderHistoryMigration implements Database.Batchable<SObject>, Database.Stateful, Database.AllowsCallouts {

    private Integer successCount = 0;
    private Integer failureCount = 0;
    private Map<String, String> statusMapping = new Map<String, String>();
    private Map<String, Id> accountMap = new Map<String, Id>();
    private List<Migration_Log__c> migrationLogs = new List<Migration_Log__c>();
    private Map<Id, Id> ccrzToOrderMap = new Map<Id, Id>();

    global CCRZOrderHistoryMigration() {
        initializeStatusMapping();
    }

    /**
     * Map CloudCraze order statuses to Salesforce OrderStatus picklist values
     */
    private void initializeStatusMapping() {
        statusMapping.put('NEW', 'Draft');
        statusMapping.put('DRAFT', 'Draft');
        statusMapping.put('SUBMITTED', 'Activated');
        statusMapping.put('PROCESSING', 'Activated');
        statusMapping.put('SHIPPED', 'Activated');
        statusMapping.put('DELIVERED', 'Activated');
        statusMapping.put('CANCELLED', 'Cancelled');
        statusMapping.put('PENDING', 'Draft');
        statusMapping.put('COMPLETED', 'Activated');
    }

    /**
     * Query all CloudCraze orders with full relationship chain
     */
    global Database.QueryLocator start(Database.BatchableContext context) {
        return Database.getQueryLocator([
            SELECT
                Id,
                ccrz__Account__c,
                ccrz__Account__r.Name,
                ccrz__OrderNumber__c,
                ccrz__OrderDate__c,
                ccrz__OrderStatus__c,
                ccrz__OrderTotal__c,
                ccrz__OrderSubtotal__c,
                ccrz__OrderTax__c,
                ccrz__OrderShipping__c,
                ccrz__OrderDiscount__c,
                ccrz__OrderNotes__c,
                ccrz__BillingStreet__c,
                ccrz__BillingCity__c,
                ccrz__BillingState__c,
                ccrz__BillingPostalCode__c,
                ccrz__BillingCountry__c,
                ccrz__ShippingStreet__c,
                ccrz__ShippingCity__c,
                ccrz__ShippingState__c,
                ccrz__ShippingPostalCode__c,
                ccrz__ShippingCountry__c,
                ccrz__ShippingMethod__c,
                ccrz__EstimatedDeliveryDate__c,
                ccrz__PONumber__c,
                (SELECT
                    Id,
                    ccrz__Product__c,
                    ccrz__Product__r.ccrz__ProductCode__c,
                    ccrz__Quantity__c,
                    ccrz__UnitPrice__c,
                    ccrz__LineTotal__c,
                    ccrz__TaxAmount__c,
                    ccrz__DiscountAmount__c
                FROM ccrz__OrderItems__r),
                (SELECT
                    Id,
                    ccrz__PaymentMethod__c,
                    ccrz__PaymentStatus__c,
                    ccrz__TransactionAmount__c,
                    ccrz__TransactionDate__c,
                    ccrz__AuthorizationCode__c,
                    ccrz__ReferenceNumber__c
                FROM ccrz__TransactionPayments__r)
            FROM ccrz__E_Order__c
            ORDER BY ccrz__OrderDate__c DESC
            LIMIT 50000
        ]);
    }

    /**
     * Process order batch for migration
     */
    global void execute(Database.BatchableContext context, List<SObject> orders) {
        List<Order> ordersToInsert = new List<Order>();
        List<OrderItem> orderItemsToInsert = new List<OrderItem>();

        // Build account map for lookups
        Set<Id> accountIds = new Set<Id>();
        for (SObject order : orders) {
            ccrz__E_Order__c ccrzOrder = (ccrz__E_Order__c) order;
            if (ccrzOrder.ccrz__Account__c != null) {
                accountIds.add(ccrzOrder.ccrz__Account__c);
            }
        }

        if (!accountIds.isEmpty()) {
            for (Account acc : [SELECT Id, Name FROM Account WHERE Id IN :accountIds]) {
                accountMap.put(acc.Name, acc.Id);
            }
        }

        // Transform orders and items
        for (SObject order : orders) {
            ccrz__E_Order__c ccrzOrder = (ccrz__E_Order__c) order;

            try {
                Order newOrder = transformOrder(ccrzOrder);
                if (newOrder != null) {
                    ordersToInsert.add(newOrder);
                }
            } catch (Exception e) {
                failureCount++;
                logMigrationError(ccrzOrder.ccrz__OrderNumber__c, 'ORDER_TRANSFORM', e.getMessage());
            }
        }

        // Insert orders first
        if (!ordersToInsert.isEmpty()) {
            insertOrdersWithErrorHandling(ordersToInsert, orders, orderItemsToInsert);
        }

        // Insert order items
        if (!orderItemsToInsert.isEmpty()) {
            insertOrderItemsWithErrorHandling(orderItemsToInsert);
        }
    }

    /**
     * Transform CloudCraze order to Salesforce Order
     */
    private Order transformOrder(ccrz__E_Order__c ccrzOrder) {
        Order newOrder = new Order();

        // Get or create account reference
        Id accountId = ccrzOrder.ccrz__Account__c;
        if (accountId == null && !String.isBlank(ccrzOrder.ccrz__Account__r.Name)) {
            accountId = accountMap.get(ccrzOrder.ccrz__Account__r.Name);
        }

        newOrder.AccountId = accountId;
        newOrder.OrderNumber = ccrzOrder.ccrz__OrderNumber__c;
        newOrder.OrderDate = ccrzOrder.ccrz__OrderDate__c != null ?
                            ccrzOrder.ccrz__OrderDate__c : System.today();

        // Map status using initialization mapping
        String mappedStatus = statusMapping.get(ccrzOrder.ccrz__OrderStatus__c);
        newOrder.Status = mappedStatus != null ? mappedStatus : 'Draft';

        // Set amount fields
        newOrder.Amount = ccrzOrder.ccrz__OrderTotal__c;
        newOrder.TotalAmount = ccrzOrder.ccrz__OrderTotal__c;
        newOrder.Subtotal__c = ccrzOrder.ccrz__OrderSubtotal__c;
        newOrder.Tax__c = ccrzOrder.ccrz__OrderTax__c;
        newOrder.Shipping__c = ccrzOrder.ccrz__OrderShipping__c;
        newOrder.Discount__c = ccrzOrder.ccrz__OrderDiscount__c != null ?
                              ccrzOrder.ccrz__OrderDiscount__c : 0;

        // Set description
        newOrder.Description = 'Migrated from CloudCraze. ' +
                              (ccrzOrder.ccrz__OrderNotes__c != null ? ccrzOrder.ccrz__OrderNotes__c : '');

        // Set PO number if available
        newOrder.PoNumber = ccrzOrder.ccrz__PONumber__c;

        // Create order delivery group for shipping
        newOrder.EffectiveDate = ccrzOrder.ccrz__OrderDate__c != null ?
                                ccrzOrder.ccrz__OrderDate__c : System.today();

        return newOrder;
    }

    /**
     * Transform order items
     */
    private void transformOrderItems(ccrz__E_Order__c ccrzOrder, Id newOrderId,
                                    List<OrderItem> itemsToAdd) {
        if (ccrzOrder.ccrz__OrderItems__r == null || ccrzOrder.ccrz__OrderItems__r.isEmpty()) {
            return;
        }

        Map<String, Id> productMap = new Map<String, Id>();

        // Get all product codes from items
        Set<String> productCodes = new Set<String>();
        for (ccrz__E_OrderItem__c item : ccrzOrder.ccrz__OrderItems__r) {
            if (item.ccrz__Product__r != null && !String.isBlank(item.ccrz__Product__r.ccrz__ProductCode__c)) {
                productCodes.add(item.ccrz__Product__r.ccrz__ProductCode__c);
            }
        }

        // Query for pricebook entries
        if (!productCodes.isEmpty()) {
            for (PricebookEntry pbe : [
                SELECT Id, Product2Id, UnitPrice
                FROM PricebookEntry
                WHERE Product2.ProductCode IN :productCodes
                LIMIT 10000
            ]) {
                if (pbe.Product2Id != null) {
                    productMap.put(String.valueOf(pbe.Product2Id), pbe.Id);
                }
            }
        }

        // Create order items
        for (ccrz__E_OrderItem__c ccrzItem : ccrzOrder.ccrz__OrderItems__r) {
            OrderItem newItem = new OrderItem();
            newItem.OrderId = newOrderId;

            // Get pricebook entry for product
            if (ccrzItem.ccrz__Product__c != null && productMap.containsKey(String.valueOf(ccrzItem.ccrz__Product__c))) {
                newItem.PricebookEntryId = productMap.get(String.valueOf(ccrzItem.ccrz__Product__c));
            }

            newItem.Quantity = ccrzItem.ccrz__Quantity__c;
            newItem.UnitPrice = ccrzItem.ccrz__UnitPrice__c;
            newItem.Description = 'Migrated from CloudCraze';

            // Store line totals in custom fields if available
            if (ccrzItem.ccrz__LineTotal__c != null) {
                newItem.LineAmount = ccrzItem.ccrz__LineTotal__c;
            }

            itemsToAdd.add(newItem);
        }
    }

    /**
     * Insert orders with error handling
     */
    private void insertOrdersWithErrorHandling(List<Order> ordersToInsert, List<SObject> originals,
                                              List<OrderItem> allOrderItems) {
        Database.SaveResult[] results = Database.insert(ordersToInsert, false);

        for (Integer i = 0; i < results.size(); i++) {
            Database.SaveResult result = results[i];
            Order order = ordersToInsert[i];
            ccrz__E_Order__c ccrzOrder = (ccrz__E_Order__c) originals[i];

            if (result.isSuccess()) {
                successCount++;
                ccrzToOrderMap.put(ccrzOrder.Id, order.Id);

                // Transform and prepare order items
                List<OrderItem> itemsForThisOrder = new List<OrderItem>();
                transformOrderItems(ccrzOrder, order.Id, itemsForThisOrder);
                allOrderItems.addAll(itemsForThisOrder);

                // Create order delivery group
                createOrderDeliveryGroup(order.Id, ccrzOrder);
            } else {
                failureCount++;
                String errors = '';
                for (Database.Error err : result.getErrors()) {
                    errors += err.getMessage() + ' ';
                }
                logMigrationError(ccrzOrder.ccrz__OrderNumber__c, 'ORDER_INSERT', errors);
            }
        }
    }

    /**
     * Create order delivery group for shipping information
     */
    private void createOrderDeliveryGroup(Id orderId, ccrz__E_Order__c ccrzOrder) {
        OrderDeliveryGroup odg = new OrderDeliveryGroup();
        odg.OrderId = orderId;
        odg.DeliverToStreet = ccrzOrder.ccrz__ShippingStreet__c;
        odg.DeliverToCity = ccrzOrder.ccrz__ShippingCity__c;
        odg.DeliverToState = ccrzOrder.ccrz__ShippingState__c;
        odg.DeliverToPostalCode = ccrzOrder.ccrz__ShippingPostalCode__c;
        odg.DeliverToCountry = ccrzOrder.ccrz__ShippingCountry__c;
        odg.ShippingMethod = ccrzOrder.ccrz__ShippingMethod__c != null ?
                            ccrzOrder.ccrz__ShippingMethod__c : 'Ground';
        odg.DesiredDeliveryDate = ccrzOrder.ccrz__EstimatedDeliveryDate__c;

        try {
            insert odg;
        } catch (Exception e) {
            System.debug('Failed to create delivery group: ' + e.getMessage());
        }
    }

    /**
     * Insert order items with error handling
     */
    private void insertOrderItemsWithErrorHandling(List<OrderItem> items) {
        Database.SaveResult[] results = Database.insert(items, false);

        for (Integer i = 0; i < results.size(); i++) {
            if (results[i].isSuccess()) {
                successCount++;
            } else {
                failureCount++;
                String errors = '';
                for (Database.Error err : results[i].getErrors()) {
                    errors += err.getMessage() + ' ';
                }
                logMigrationError('OrderItem_' + i, 'ORDERITEM_INSERT', errors);
            }
        }
    }

    /**
     * Log migration errors
     */
    private void logMigrationError(String sourceId, String phase, String errorMessage) {
        Migration_Log__c log = new Migration_Log__c();
        log.Source_Record_ID__c = sourceId;
        log.Migration_Phase__c = phase;
        log.Error_Message__c = errorMessage.abbreviate(255);
        log.Object_Type__c = 'Order';
        log.Status__c = 'Error';
        migrationLogs.add(log);
    }

    /**
     * Finish batch execution
     */
    global void finish(Database.BatchableContext context) {
        // Insert logs
        if (!migrationLogs.isEmpty()) {
            insert migrationLogs;
        }

        // Run post-migration validation
        validateOrderTotals();

        // Send completion email
        sendCompletionEmail();
    }

    /**
     * Validate order totals match post-migration
     */
    private void validateOrderTotals() {
        List<Order> migratedOrders = [
            SELECT Id, Amount, TotalAmount, Subtotal__c, Tax__c, Shipping__c, Discount__c
            FROM Order
            WHERE CreatedDate = TODAY
            LIMIT 100
        ];

        Integer validCount = 0;
        Decimal totalAmount = 0;

        for (Order order : migratedOrders) {
            // Validate totals add up correctly
            if (order.Amount != null) {
                validCount++;
                totalAmount += order.Amount;
            }
        }

        OrderValidation__c validation = new OrderValidation__c();
        validation.Record_Count__c = migratedOrders.size();
        validation.Valid_Count__c = validCount;
        validation.Total_Amount__c = totalAmount;
        validation.Validation_Date__c = System.now();
        insert validation;
    }

    /**
     * Send completion email summary
     */
    private void sendCompletionEmail() {
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        mail.setToAddresses(new String[]{'migration-admin@nulogic.com'});
        mail.setSubject('CloudCraze Order Migration Complete');

        String emailBody = 'Order History Migration Summary:\n\n' +
                          'Successfully Migrated: ' + successCount + '\n' +
                          'Failed Records: ' + failureCount + '\n' +
                          'Orders Processed: ' + ccrzToOrderMap.size() + '\n\n' +
                          'All orders have been migrated with delivery groups.\n' +
                          'Timestamp: ' + System.now().format();

        mail.setPlainTextBody(emailBody);

        try {
            Messaging.sendEmail(new Messaging.SingleEmailMessage[]{mail});
        } catch (Exception e) {
            System.debug('Email delivery failed: ' + e.getMessage());
        }
    }
}
