// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

/**
 * Extend the default List controller to handle the batch label creation.
 */
//{namespace name="backend/viison_shipping_common_order/order"}
Ext.define('Shopware.apps.ViisonShippingCommonOrder.controller.ListDispatch', {

    extend: 'Ext.app.Controller',

    /**
     * Contains all custom snippets for the view component.
     */
    snippets: {
        messages: {
            defaultError: '{s name="list/messages/default_error"}{/s}',
            productMappingMissing: '{s name="list/messages/product_mapping_missing"}{/s}'
        },
        notifications: {
            noTrackingCodesError: {
                title: '{s name="detail/notifications/no_tracking_codes_error/title"}{/s}',
                message: '{s name="detail/notifications/no_tracking_codes_error/message"}{/s}'
            }
        }
    },

    /**
     * Adds additional event handlers and initializes the necessary stores.
     */
    init: function() {
        var me = this;

        me.callParent(arguments);

        // Add new event handlers
        me.control({
            'order-list-main-window order-list': {
                viisonBatchLabelCreation: me.onBatchLabelCreation,
                viisonShowDetailDispatchServiceProviderTab: me.onShowDetailDispatchServiceProviderTab,
                viisonTrackShipments: me.onTrackShipments
            },
            'order-viison-shipping-common-batch-label-window': {
                confirmBatchLabelCreation: me.onConfirmBatchLabelCreation,
                close: me.onCloseBatchStatusWindow,
                showJoinedBatchLabels: me.onShowJoinedBatchLabels,
                showJoinedExportDocuments: me.onShowJoinedExportDocuments
            }
        });
        // Init stores
        me.labelStore = Ext.create('Shopware.apps.ViisonShippingCommonOrder.store.ShippingLabel');

        me.batchStore = Ext.create('Shopware.apps.ViisonShippingCommonOrder.store.BatchLabelStatus');
    },

    /**
     * Add a plugin specific list dispatch actions controller.
     *
     * @param listDispatchActionsControllerClass
     */
    addListDispatchActionsControllerClass: function(listDispatchActionsControllerClass) {
        this.listDispatchActionsControllers = this.listDispatchActionsControllers || [];
        this.listDispatchActionsControllers.push(this.subApplication.getController(listDispatchActionsControllerClass));
    },

    /**
     * Add a plugin specific list dispatch actions controller.
     *
     * @param tabControllerClass
     */
    addDispatchServiceProviderTabControllerClass: function(tabControllerClass) {
        this.dispatchServiceProviderTabControllers = this.dispatchServiceProviderTabControllers || [];
        this.dispatchServiceProviderTabControllers.push(this.subApplication.getController(tabControllerClass));
    },

    /**
     * Creates and shows a new window showing the status information about the
     * process of creating one shipping label for each record, which is checked in the given grid.
     *
     * @param grid The grid showing all orders and containing the selection model used to determine the records of the batch.
     */
    onBatchLabelCreation: function(grid) {
        this.orderGrid = grid;

        // Get the checked records
        this.orderRecords = grid.getSelectionModel().getSelection();

        // Create and show the status window
        this.createBatchStatusWindow(grid.orderStatusStore);

        // Disable the 'show all labels' button
        this.batchStatusWindow.showJoinedLabelsButton.setDisabled(true);
        this.batchStatusWindow.showJoinedExportDocumentsButton.setDisabled(true);
    },

    /**
     * Starts the process of creating a new shipping label for each order.
     * For each request a new entry is added and all orders, whose request was successful,
     * are set to the selected order status, using the default batch processing functionality.
     */
    onConfirmBatchLabelCreation: function() {
        var me = this;
        var newStatus = this.batchStatusWindow.statusSelectionBox.getValue();

        // Increase AJAX timeout from default 30 seconds to 60 seconds (sometimes, DHL is extremely slow)
        Ext.override(Ext.data.proxy.Ajax, { timeout: 60000 });

        // Display the window's loading mask
        me.batchStatusWindow.loadMask.show();

        // Enable the 'show all labels' button
        me.batchStatusWindow.showJoinedLabelsButton.setDisabled(false);
        me.batchStatusWindow.showJoinedExportDocumentsButton.setDisabled(false);

        // Define a callback, which is triggered only after all requests are finished
        var overallStatus = 0;
        var successfulOrders = [];
        var hasExportDocuments = false;
        var afterFinish = me.after(this.orderRecords.length, function() {
            this.batchStatusWindow.loadMask.hide();
            // Update the icon of the 'show joined labels' and 'show joined export documents' buttons and their states
            var icons = me.batchStatusWindow.listPanel.statusIcons;
            var batchButtonIconCls = icons[overallStatus] + ' primary-button-icon-left-middle';
            this.batchStatusWindow.showJoinedLabelsButton.setIconCls(batchButtonIconCls);
            if (hasExportDocuments) {
                this.batchStatusWindow.showJoinedExportDocumentsButton.setIconCls(batchButtonIconCls);
            }
            this.batchStatusWindow.showJoinedExportDocumentsButton.setDisabled(!hasExportDocuments);

            if (successfulOrders.length == 0) {
                this.batchStatusWindow.showJoinedLabelsButton.disable();
            }

            if (newStatus !== 100) {
                // Create a new batch store to sync the respective orders
                var store = this.createOrderBatchStoreForOrderUpdating();
                successfulOrders = this.updateOrdersAfterLabelCreation(successfulOrders, newStatus);

                store.add(successfulOrders);
                store.sync({
                    scope: this,
                    callback: function() {
                        // Reload all orders to fetch the new tracking codes and states
                        this.reloadOrderStore();
                    }
                });
            } else {
                this.batchStatusWindow.loadMask.hide();
                // Reload all orders to fetch the new tracking codes and states
                this.reloadOrderStore();
            }

            // Reset AJAX timeout to the default (30 seconds)
            Ext.override(Ext.data.proxy.Ajax, { timeout: 30000 });
        }).bind(this);

        var orderNumbers = {};
        var dispatchName = '';
        // Add labels for all (selected) orders in the batch
        Ext.each(this.orderRecords, function(orderRecord, index) {
            orderNumbers[orderRecord.get('id')] = orderRecord.data.number;

            var shippingLabelClass = null;
            var currentListDispatchActionsController = null;
            var dispatchRecord = (orderRecord && orderRecord.getDispatch()) ? orderRecord.getDispatch().first() : null;

            if (dispatchRecord && dispatchRecord.get('name')) {
                dispatchName = dispatchRecord.get('name');
            }

            // go through the dispatch service provider plugins and choose the shipping label class of the first
            // plugin that has a product mapping for the dispatch type of the order (first come, first serve)
            Ext.each(me.listDispatchActionsControllers, function(listDispatchActionsController) {
                if (listDispatchActionsController.hasProductMapping(orderRecord)) {
                    shippingLabelClass = listDispatchActionsController.getShippingLabelClass();
                    currentListDispatchActionsController = listDispatchActionsController;
                    return false; // break the Ext.each loop
                }
            });

            // Create a new status object
            var status = Ext.create('Shopware.apps.ViisonShippingCommonOrder.model.BatchLabelStatus', {
                orderId: orderRecord.get('id'),
                orderNumber: orderNumbers[orderRecord.get('id')]
            });
            me.batchStore.add(status);

            if (shippingLabelClass == null) {
                // Determine message
                var message = me.fillSnippet(me.snippets.messages.productMappingMissing, dispatchName);
                // Update status instance
                status.set('success', false);
                status.set('message', message);
                overallStatus = 1;

                // Mark this request as finished
                afterFinish();
                return true; // continue the Ext.each loop
            }

            // Create new label
            var label = Ext.create(shippingLabelClass, {
                orderId: orderRecord.get('id'),
                useDetails: false
            });

            var callback = {
                callback: Ext.pass(function(status, record, operation) {
                    // Handle the operation status
                    if (operation.success === true && record.get('creationSuccess') === true) {
                        status.set('warningMessage', record.get('warningMessageForCashOnDelivery') || '');

                        // Add the label to its store and associate it with the status object
                        me.labelStore.add(record);
                        status.setLabel(record);
                        var trackingCode = record.get('trackingCode');
                        status.set('trackingCode', trackingCode);
                        // Save the order record in the list of successful label creations
                        Ext.each(me.orderRecords, function(orderRecord) {
                            if (orderRecord.get('id') == record.get('orderId')) {
                                successfulOrders.push(orderRecord);
                                if (record.get('exportDocumentUrl') != '') {
                                    hasExportDocuments = true;
                                }
                                return false; // break the Ext.each loop
                            }
                        });
                        // Download the created shipping label
                        Ext.Ajax.request({
                            url: currentListDispatchActionsController.downloadDocumentsURL,
                            method: 'GET',
                            params: {
                                labelIdentifier: trackingCode
                            }
                        });
                    } else {
                        // Determine message
                        var message = (operation.success && record.get('message') && record.get('message').length > 0) ? record.get('message') : me.snippets.messages.defaultError;
                        // Update status instance
                        status.set('success', false);
                        status.set('message', message);
                        overallStatus = 1;
                    }

                    // Mark this request as finished
                    afterFinish();
                }, status)
            };

            // Add label to the database
            setTimeout(function () {
                label.save(callback)
            }, index * 500);
        });
    },

    /**
     * @returns { Shopware.apps.Order.store.Batch }
     */
    createOrderBatchStoreForOrderUpdating: function () {
        var store = Ext.create('Shopware.apps.Order.store.Batch');
        store.getProxy().extraParams.autoSend = this.batchStatusWindow.sendMailCheckbox.getValue();

        return store;
    },

    /**
     * Update the status of each successful order, for which a new label was created.
     *
     * @param { array } successfulOrders
     * @param { int } newStatus
     * @returns { array }
     */
    updateOrdersAfterLabelCreation: function (successfulOrders, newStatus) {
        Ext.each(successfulOrders, function(order) {
            order.set('status', newStatus);
            order.setDirty();
        });

        return successfulOrders;
    },

    /*
     * Reload all orders to fetch the new tracking codes and states. The active selection of orders gets cleared
     * by the reload. However, we want to retain the old selection, so we reapply the previous selection after
     * the reload.
     */
    reloadOrderStore: function() {
        var me = this;
        var orderStore = me.subApplication.getStore('Order');
        orderStore.reload({
            callback: function() {
                var newOrderRecords = [];
                Ext.each(me.orderRecords, function(orderRecord) {
                    newOrderRecords.push(orderStore.getById(orderRecord.getId()));
                });
                me.orderGrid.getSelectionModel().select(newOrderRecords, false, false);
            }
        });
    },

    /**
     * Deletes the batch status window and removes all BatchLabelStatus instances from
     * the batch store.
     */
    onCloseBatchStatusWindow: function() {
        // Close and destroy old window
        if (this.batchStatusWindow) {
            delete this.batchStatusWindow;
        }
    },

    /**
     * Retrieves all tracking codes from the batch label store and returns them as an associative
     * array so that they can be used as GET parameters in an ajax request.
     *
     * @returns array|object
     */
    getBatchTrackingCodeParams: function() {
        var me = this;

        var documentIdentifiers = [];
        var shipments = {};

        me.labelStore.each(function (record) {
            if(record.get('documents')) {
                var key = record.get('entityName');

                shipments[key] = (shipments[key]) ? shipments[key] : [];
                shipments[key].push(record.get('id'))
            } else if (record.get('trackingCode')) {
                // @deprecated
                documentIdentifiers.push(record.get('trackingCode'));
            }
        });

        return {
            'documentIdentifiers': Ext.JSON.encode(documentIdentifiers), // @deprecated
            'shipments' : Ext.JSON.encode(shipments)
        };
    },

    /**
     * Fetches all tracking codes of the current batch and opens a new browser window/tab
     * requesting the merged PDF of all label files listed in the batch store.
     */
    onShowJoinedBatchLabels: function() {
        this.openPDFInNewTab('{url controller="ViisonShippingCommonOrder" action="getMergedLabels"}?');
    },

    /**
     * Fetches all tracking codes of the current batch and opens a new browser window/tab
     * requesting the merged PDF of all export documents files.
     */
    onShowJoinedExportDocuments: function() {
        this.openPDFInNewTab('{url controller="ViisonShippingCommonOrder" action="getMergedExportDocuments"}?');
    },

    /**
     * Open Merged documents as PDF in new tab
     *
     * @param response
     */
    openPDFInNewTab: function(url) {
        var batchParams = this.getBatchTrackingCodeParams();
        Object.keys(batchParams).forEach(function(key) {
            // Concat the url params to get the form KEY=VALUE&
            url += key + '=' + batchParams[key] + '&';
        });

        window.open(url, '_blank');
    },

    /**
     * Check if string is JSON string
     *
     * @param string str
     * @returns boolean
     */
    isJSONString: function(str) {
            try {
                JSON.parse(str);
            } catch (e) {
                return false;
            }
            return true;
    },

    /**
     * First destroys and deletes the old batch status window to finally create
     * and show a new one.
     *
     * @param orderStatusStore The store containing all available order states.
     */
    createBatchStatusWindow: function(orderStatusStore) {
        var me = this;

        // Destroy old window
        if (me.batchStatusWindow) {
            me.batchStatusWindow.destroy();
            delete me.batchStatusWindow;
        }

        // Clear the batch status store and the label store
        this.batchStore.removeAll();
        this.labelStore.removeAll();

        // Copy the order status store with all its items
        var orderStatusStoreCopy = Ext.create('Shopware.apps.Base.store.OrderStatus');
        orderStatusStoreCopy.add(orderStatusStore.getRange());

        // Create new window
        me.batchStatusWindow = Ext.create('Shopware.apps.ViisonShippingCommonOrder.view.BatchLabel.Window', {
            labelStore: me.labelStore,
            batchStore: me.batchStore,
            orderStatusStore: orderStatusStoreCopy,
            style: {
                'background-color': '#ebedef'
            },
            bodyStyle: {
                'background-color': '#ebedef',
                'border': 'none !important'
            }
        }).show(undefined, function() {
            this.subApplication = me.subApplication;
        });
    },

    /**
     * This function is called when the user clicks on the 'add label' button in the order list.
     *
     * @param record
     */
    onShowDetailDispatchServiceProviderTab: function(record) {
        // Open tracking in new tab/window
        Ext.each(this.listDispatchActionsControllers, function(listDispatchActionsController) {
            // use the first dispatch service provider that has a product mapping (first come, first serve)
            if (listDispatchActionsController.hasProductMapping(record)) {
                Shopware.app.Application.addSubApplication({
                    name: 'Shopware.apps.Order',
                    params: {
                        orderId: record.get('id'),
                        focus: listDispatchActionsController.tabPanelAlias,
                        showLabelConfirmWindow: true
                    }
                });
                return false; // break the Ext.each loop
            }
        });
    },

    /**
     * Opens a new window showing status information for the tracking codes of this record.
     *
     * @param record The record whose details window has been opened.
     */
    onTrackShipments: function(record) {
        var me = this;

        if (record.raw.trackingCode.length == 0) {
            var notification = this.snippets.notifications.noTrackingCodesError;
            Shopware.Notification.createGrowlMessage(notification.title, notification.message, this.snippets.notifications.growlMessage);
            return;
        }

        this.openTrackingWindows(record.raw.trackingCode);
    },

    openTrackingWindows: function(trackingCodes) {
        Ext.Ajax.request({
            url: '{url controller=ViisonShippingCommonOrder action=getTrackingUrls}',
            params: {
                trackingCodes: trackingCodes
            },
            success: function(response){
                // Decode the response
                var responseData = Ext.JSON.decode(response.responseText, true);

                if (!responseData) {
                    return;
                }

                Ext.each(responseData.trackingCodeUrls, function (url) {
                    window.open(url, '_blank');
                });
            }
        });
    },

    /**
     * @source http://underscorejs.org/docs/underscore.html
     */
    after: function(times, func) {
        return function() {
            if (--times < 1) {
                return func.apply(this, arguments);
            }
        };
    },

    /**
     * Replaces occurrences of '[0]' with the name of the dispatch service provider (e.g. 'DHL').
     *
     * @param snippet string
     * @return string
     */
    fillSnippet: function(snippet, name) {
        return Ext.String.format(snippet, name);
    }
});
