/**
 * @author 1902 Software
 * @copyright Copyright © 2023 1902 Software (https://1902software.com/magento/)
 * @package WriteTextAI_WriteTextAI
 */

define([
    'ko',
    'underscore',
    'mage/translate',
    'domReady!'
], function (
    ko,
    _,
    $t
) {
    'use strict';

    /**
     * @var {Array} statuses
     * @var {String} tempId
     * @var {String} transferTempId
     * @var {String} koTempId
     */
    var statuses = ko.observableArray([]),
        tempId = 'temp',
        transferTempId = 'transferTemp',
        koTempId = 'koTemp',
        opened = ko.observable(false),
        triggerDismissAll = ko.observable(false),
        currentBulkRequestId = ko.observable(null);

    var storageBaseKey = 'wtai_notifications_state_v2';
    var scope = 'product';
    var tabId = String(Date.now()) + '_' + String(Math.random()).slice(2);
    var isApplyingStorageState = false;
    var isUserCollapsed = false;
    var keepCollapsedAfterReload = isReloadNavigation();
    var hasRestoredScopeState = {};
    var hasCollapsedServerRestoreAfterReload = {};

    function isReloadNavigation()
    {
        try {
            if (window.performance && typeof window.performance.getEntriesByType === 'function') {
                var navEntries = window.performance.getEntriesByType('navigation');
                if (Array.isArray(navEntries) && navEntries[0] && navEntries[0].type) {
                    return navEntries[0].type === 'reload';
                }
            }
        } catch (e) {
            // no-op
        }

        try {
            /** Legacy fallback */
            if (window.performance && window.performance.navigation) {
                return window.performance.navigation.type === 1;
            }
        } catch (e) {
            // no-op
        }

        return false;
    }

    function ensureDefaultOpened(stateStatuses)
    {
        if (isApplyingStorageState) {
            return;
        }

        if (keepCollapsedAfterReload && Array.isArray(stateStatuses) && stateStatuses.length > 0) {
            opened(false);
            isUserCollapsed = true;
            return;
        }

        if (Array.isArray(stateStatuses) && stateStatuses.length > 0 && !isUserCollapsed) {
            opened(true);
        }
    }

    function getStorageKey()
    {
        return storageBaseKey + '_' + String(scope).toLowerCase();
    }

    function getCurrentRecordType()
    {
        var normalized = String(scope || '').toLowerCase();
        if (normalized === 'category') {
            return 'Category';
        }

        return 'Product';
    }

    function setScope(newScope)
    {
        var normalized = String(newScope || '').toLowerCase();
        if (!normalized) {
            normalized = 'product';
        }

        if (scope === normalized) {
            return;
        }

        scope = normalized;
        isUserCollapsed = false;

        try {
            var nextState = safeParseJson(window.localStorage.getItem(getStorageKey()));
            if (nextState) {
                applyStateFromStorage(nextState);
            } else {
                isApplyingStorageState = true;
                statuses([]);
                opened(false);
                currentBulkRequestId(null);
                isApplyingStorageState = false;
            }
        } catch (e) {
            // no-op
        }
    }

    function safeParseJson(json)
    {
        try {
            return JSON.parse(json);
        } catch (e) {
            return null;
        }
    }

    function persistState(stateStatuses)
    {
        try {
            window.localStorage.setItem(getStorageKey(), JSON.stringify({
                ts: Date.now(),
                tabId: tabId,
                statuses: stateStatuses,
                currentBulkRequestId: currentBulkRequestId()
            }));
        } catch (e) {
            // no-op
        }
    }

    function removeFinishedCancelledStatuses(stateStatuses)
    {
        return (Array.isArray(stateStatuses) ? stateStatuses : []).filter(function (status) {
            return !status || status.status !== 'Cancelled';
        });
    }

    function applyStateFromStorage(state)
    {
        if (!state || typeof state !== 'object') {
            return;
        }

        if (!Array.isArray(state.statuses)) {
            return;
        }

        var isFirstRestoreForScope = !hasRestoredScopeState[scope];
        hasRestoredScopeState[scope] = true;

        isApplyingStorageState = true;
        state.statuses = removeFinishedCancelledStatuses(state.statuses);
        statuses(state.statuses);
        currentBulkRequestId(state.currentBulkRequestId || null);

        if (isFirstRestoreForScope && isReloadNavigation()) {
            /** Keep notifications collapsed on refresh (user can expand via View) */
            opened(false);
            isUserCollapsed = Array.isArray(state.statuses) && state.statuses.length > 0;
        } else {
            ensureDefaultOpened(state.statuses);
        }
        isApplyingStorageState = false;
    }

    function restoreStatusesFromBulkRequests(bulkStatuses)
    {
        var nextStatuses = removeFinishedCancelledStatuses(bulkStatuses),
            shouldCollapseAfterReload = isReloadNavigation() && !hasCollapsedServerRestoreAfterReload[scope];

        isApplyingStorageState = true;
        statuses(nextStatuses);
        persistState(nextStatuses);

        if (shouldCollapseAfterReload) {
            hasCollapsedServerRestoreAfterReload[scope] = true;
            opened(false);
            isUserCollapsed = nextStatuses.length > 0;
            isApplyingStorageState = false;
            return;
        }

        isApplyingStorageState = false;
        ensureDefaultOpened(nextStatuses);
    }

    try {
        var initialState = safeParseJson(window.localStorage.getItem(getStorageKey()));
        applyStateFromStorage(initialState);
    } catch (e) {
        // no-op
    }

    statuses.subscribe(function (newStatuses) {
        if (isApplyingStorageState) {
            return;
        }

        ensureDefaultOpened(newStatuses);

        try {
            window.localStorage.setItem(getStorageKey(), JSON.stringify({
                ts: Date.now(),
                tabId: tabId,
                statuses: newStatuses,
                currentBulkRequestId: currentBulkRequestId()
            }));
        } catch (e) {
            // no-op
        }
    });

    opened.subscribe(function () {
        if (isApplyingStorageState) {
            return;
        }

        if (opened() === true) {
            keepCollapsedAfterReload = false;
            isUserCollapsed = false;
        } else {
            isUserCollapsed = Array.isArray(statuses()) && statuses().length > 0;
        }

        try {
            window.localStorage.setItem(getStorageKey(), JSON.stringify({
                ts: Date.now(),
                tabId: tabId,
                statuses: statuses(),
                currentBulkRequestId: currentBulkRequestId()
            }));
        } catch (e) {
            // no-op
        }
    });

    currentBulkRequestId.subscribe(function () {
        if (isApplyingStorageState) {
            return;
        }
        try {
            window.localStorage.setItem(getStorageKey(), JSON.stringify({
                ts: Date.now(),
                tabId: tabId,
                statuses: statuses(),
                currentBulkRequestId: currentBulkRequestId()
            }));
        } catch (e) {
            // no-op
        }
    });

    window.addEventListener('storage', function (event) {
        if (!event || event.key !== getStorageKey() || !event.newValue) {
            return;
        }
        var incoming = safeParseJson(event.newValue);
        if (!incoming || incoming.tabId === tabId) {
            return;
        }
        applyStateFromStorage(incoming);
    });
        
    /**
     * Check if there is an intersection between the arrays
     *
     * @param {Array} array1
     * @param {Array} array2
     * @param {Array} array3
     *
     * @returns {Boolean}
     */
    function checkArraysIntersection(array1, array2 = [], array3)
    {
        for (let i = 0; i < array1.length; i++) {
            if (array3.includes(array1[i])) {
                return true;
            }
        }

        for (let i = 0; i < array2.length; i++) {
            if (array3.includes(array2[i])) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get the status of the request
     *
     * @param {Object} messageEntry
     * @param {Object} remaining
     * @param {Number} failed
     * @param {String} status
     *
     * @returns {String}
     */
    function getMessage(messageEntry, remaining, failed, status)
    {
        var message = '',
            text = '';

        var recordType = messageEntry && messageEntry.recordType ? String(messageEntry.recordType) : '';
        var scopeLabel = recordType === 'Category' ? $t('categories') : $t('products');

        switch (messageEntry.subType) {
            case 0:
                if (messageEntry.failed === messageEntry.total && messageEntry.failed > 0) {
                    return $t('Error encountered');
                }
                switch (status) {
                    case 'Preparing':
                        message = $t('Preparing for text generation...');
                        break;
                    case 'Pending':
                        message = $t('Queued for text generation...')
                            + '<br />'
                            + $t('Waiting for other processes to finish before starting.');
                        break;
                    case 'Running':
                        if (remaining.remaining === 0) {
                            message = $t('Generating text...');
                            break;
                        }
                        
                        if (remaining.is_minutes) {
                            text = $t('Generating text... (Estimated time remaining %s minute/s)');
                        } else {
                            text = $t('Generating text... (Estimated time remaining %s second/s)');
                        }
                        message = text.replace('%s', remaining.remaining);
                        break;
                    case 'Completed':
                        message = $t('WriteText.ai is done generating text for your selected %1.').replace('%1', scopeLabel);
                        if (failed > 0) {
                            message = $t('Error encountered');
                        }
                        break;
                    case 'AttemptingCancel':
                        message = $t('Attempting to cancel bulk action…')
                            + '<br />'
                            + $t('Pending items will be cancelled. Items already in progress will continue.');
                        break;
                    case 'Cancelling':
                        message = $t('Cancelling pending items only...')
                            + '<br />'
                            + $t('Items already in progress will continue.');
                        break;
                    case 'Cancelled':
                        message = $t('This process has been cancelled.');
                        break;
                    case 'TimedOut':
                        message = $t('WriteText.ai is currently experiencing a high volume of requests and has timed out.');
                        break;
                    case 'Failed':
                        message = $t('Error encountered');
                        break;
                    default:
                        message = $t('Generating text...');
                        break;
                }
                break;
            case 1:
                if (messageEntry.failed === messageEntry.total && messageEntry.failed > 0) {
                    return $t('Error encountered');
                }
                switch (status) {
                    case 'Preparing':
                        message = $t('Preparing for full automation...');
                        break;
                    case 'Pending':
                        message = $t('Queued for full automation...')
                            + '<br />'
                            + $t('Waiting for other processes to finish before starting.');
                        break;
                    case 'Running':
                        if (remaining.remaining === 0) {
                            message = $t('Running full automation...');
                            break;
                        }
                        
                        if (remaining.is_minutes) {
                            text = $t('Running full automation... (Estimated time remaining %s minute/s)');
                        } else {
                            text = $t('Running full automation... (Estimated time remaining %s second/s)');
                        }
                        message = text.replace('%s', remaining.remaining);
                        break;
                    case 'Completed':
                        message = $t('WriteText.ai has completed full automation for your selected %1.').replace('%1', scopeLabel);
                        if (failed > 0) {
                            message = $t('Error encountered');
                        }
                        break;
                    case 'AttemptingCancel':
                        message = $t('Attempting to cancel bulk action…')
                            + '<br />'
                            + $t('Pending items will be cancelled. Items already in progress will continue.');
                        break;
                    case 'Cancelling':
                        message = $t('Cancelling pending items only...')
                            + '<br />'
                            + $t('Items already in progress will continue.');
                        break;
                    case 'Cancelled':
                        message = $t('This process has been cancelled.');
                        break;
                    case 'TimedOut':
                        message = $t('WriteText.ai is currently experiencing a high volume of requests and has timed out.');
                        break;
                    case 'Failed':
                        message = $t('Error encountered');
                        break;
                    default:
                        message = $t('Running full automation...');
                        break;
                }
                break;
            case 2:
                if (messageEntry.failed === messageEntry.total && messageEntry.failed > 0) {
                    return $t('Error encountered');
                }
                switch (status) {
                    case 'Preparing':
                        message = $t('Preparing for keyword analysis...');
                        break;
                    case 'Pending':
                        message = $t('Queued for keyword analysis...')
                            + '<br />'
                            + $t('Waiting for other processes to finish before starting.');
                        break;
                    case 'Completed':
                        message = $t('Keyword analysis completed');
                        if (failed > 0) {
                            message = $t('Error encountered');
                        }
                        break;
                    case 'AttemptingCancel':
                        message = $t('Attempting to cancel bulk action…')
                            + '<br />'
                            + $t('Pending items will be cancelled. Items already in progress will continue.');
                        break;
                    case 'Cancelling':
                        message = $t('Cancelling pending items only...')
                            + '<br />'
                            + $t('Items already in progress will continue.');
                        break;
                    case 'Cancelled':
                        $t('Keyword analysis cancelled');
                        break;
                    case 'TimedOut':
                        message = $t('Keyword analysis timed out');
                        break;
                    case 'Running':
                        message = $t('Keyword analysis currently in progress');
                        break;
                    case 'Failed':
                        message = $t('Error encountered');
                        break;
                    default:
                        message = $t('Queued for keyword analysis');
                        break;
                }
                break;
            case 3:
                if (messageEntry.failed === messageEntry.total && messageEntry.failed > 0) {
                    return $t('Error encountered');
                }
                switch (status) {
                    case 'Preparing':
                        message = $t('Preparing for transfer...');
                        break;
                    case 'Pending':
                        message = $t('Queued for transfer...')
                            + '<br />'
                            + $t('Waiting for other processes to finish before starting.');
                        break;
                    case 'Completed':
                        message = $t('Done transferring text for your selected %1.').replace('%1', scopeLabel);
                        if (failed > 0) {
                            message = $t('Error encountered');
                        }
                        break;
                    case 'AttemptingCancel':
                        message = $t('Attempting to cancel bulk action…')
                            + '<br />'
                            + $t('Pending items will be cancelled. Items already in progress will continue.');
                        break;
                    case 'Cancelling':
                        message = $t('Cancelling pending items only...')
                            + '<br />'
                            + $t('Items already in progress will continue.');
                        break;
                    case 'Cancelled':
                        $t('Transfer cancelled');
                        break;
                    case 'TimedOut':
                        message = $t('Transfer timed out');
                        break;
                    case 'Running':
                        message = $t('Transferring text...');
                        break;
                    case 'Failed':
                        message = $t('Error encountered');
                        break;
                    default:
                        message = $t('Transferring text...');
                        break;
                }
                break;
            default:
                break;
        }

        return message;
    }

    /**
     * Calculate the remaining time
     *
     * @param {String} estimatedEndTime
     * @param {String} startTime
     * @returns {Object}
     */
    function calculateRemainingTime(estimatedEndTime, startTime)
    {
        var remaining = 0,
            isMinutes = false,
            result = {},
            currentDatetime,
            interval;

        if (estimatedEndTime !== null && startTime !== null) {
            estimatedEndTime = new Date(estimatedEndTime);
            startTime = new Date(startTime);
            interval = estimatedEndTime.getTime() - startTime.getTime();
            remaining = Math.ceil(interval / 1000); /* Convert milliseconds to seconds */

            if (remaining >= 60) {
                remaining = Math.ceil(remaining / 60); /* Convert seconds to minutes if it's more than or equal to 60 seconds */
                isMinutes = true;
            }
        }
        result.remaining = remaining;
        result.is_minutes = isMinutes;

        return result;
    }

    /**
     * Add notification to the status list
     *
     * @param {Object} notification
     * @param {Array} requestQueuedIds
     * @param {Array} requestCompletedIds
     * @param {String} requestId
     *
     * @returns {void}
     */
    function addNotification(
        notification,
        requestQueuedIds,
        requestCompletedIds,
        requestId
    ) {
        var noStatusMatched = true;

        for (const status of statuses()) {
            if (
                status.id === koTempId &&
                checkArraysIntersection(
                    requestQueuedIds,
                    requestCompletedIds,
                    status.queuedIds
                ) &&
                status.user === notification.user
            ) {
                statuses.replace(status, notification);
                noStatusMatched = false;
                break;
            }
            if (
                status.id === tempId &&
                checkArraysIntersection(
                    requestQueuedIds,
                    requestCompletedIds,
                    status.queuedIds
                ) &&
                status.user === notification.user
            ) {
                statuses.replace(status, notification);
                noStatusMatched = false;
                break;
            }
            if (status.id === requestId) {
                notification.user = status.user;
                statuses.replace(status, notification);
                if (['AttemptingCancel', 'Cancelling'].includes(notification.status)) {
                    opened(true);
                }
                noStatusMatched = false;
                break;
            }
        }

        if (noStatusMatched) {
            const index = statuses().findIndex(status => status.user === notification.user);
            const hasOngoing = statuses().some(status => status.user === notification.user && status.status === 'Running');
            const hasCompleted = statuses().some(status => status.user === notification.user && status.status === 'Completed');

            var toOpen = notification.status !== 'Cancelled';
            if (index !== -1) {
                if (notification.total === 1 && notification.subType === 1 &&
                    (hasOngoing || (hasCompleted && notification.tempId !== tempId))
                ) {
                    /** Suppress notification from edit page */
                    toOpen = false;
                } else {
                    console.log("replace");
                    statuses.splice(index, 1, notification);
                }
            } else {
                statuses.push(notification);
            }
            
            if (toOpen) {
                opened(true);
            }
        }
    }
    
    function addKeywordAnalysisFailedStatus(currentUser, messageData = {})
    {
        var keywordAnalysisStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.user === currentUser.email;
        }),
            newStatus = {
                id: messageData.id,
                message: $t('Error encountered'),
                user: currentUser.email,
                percent: 0,
                status: 'Completed',
                queuedIds: messageData.queueIds,
                completedIds: messageData.completedIds,
                completed: messageData.completed,
                subType: messageData.subType,
                failed: 1,
                failedIds: messageData.completedIds,
                total: 1,
                runningIds: messageData.runningIds,
                recordType: messageData.recordType || getCurrentRecordType()
        };

        if (keywordAnalysisStatus) {
            statuses.replace(keywordAnalysisStatus, newStatus);
        } else {
            statuses.push(newStatus);
            opened(true);
        }
    }

    /**
     * Add generate failed status
     *
     * @param {Object} currentUser
     * @param {Object} messageData
     *
     * @returns {void}
     */
    function addGenerateFailedStatus(messageData = {})
    {
        var failedMessageCount = messageData.failed;
        if (messageData.failed === 0 && messageData.completed === 1
            && messageData.total === 1
            && messageData.status === 'Failed'
        ) {
            failedMessageCount = 1;
        }
        var completedCount = messageData.completed !== messageData.failed ? messageData.completed - messageData.failed : messageData.completed;
        var successTotal = messageData.completed - failedMessageCount;
        if (failedMessageCount === messageData.completed) {
            successTotal = failedMessageCount;
        }
        var generateStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.id === messageData.id;
        }),
            newStatus = {
                id: messageData.id,
                message: $t('Error encountered'),
                user: messageData.userName,
                percent: 100,
                status: 'Completed',
                queuedIds: messageData.queueIds,
                completedIds: messageData.completedIds,
                completed: completedCount,
                subType: messageData.subType,
                failed: failedMessageCount,
                failedIds: messageData.failedIds,
                total: messageData.total,
                runningIds: messageData.runningIds,
                recordType: messageData.recordType || getCurrentRecordType()
        };


        if (generateStatus) {
            statuses.replace(generateStatus, newStatus);
        } else {
            statuses.push(newStatus);
            opened(true);
        }
    }

    /**
     * Add new keyword analysis status
     *
     * @param {Array} productIds
     * @param {Object} currentUser
     * @param {Object} data
     *
     * @returns {void}
     */
    function addNewKeywordAnalysisStatus(productIds, currentUser, data = {})
    {
        var initialLength = productIds.length;
        if (data) {
            initialLength = data.excludeMode ? data.total : initialLength;
        }
        var keywordAnalysisStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.user === currentUser.email;
        }),
            newStatus = {
                id: koTempId,
                message: $t('Preparing for keyword analysis...'),
                user: currentUser.email,
                percent: 0,
                status: 'Preparing',
                queuedIds: productIds,
                runningIds: [],
                completedIds: [],
                completed: 0,
                subType: 2,
                total: initialLength,
                recordType: data.recordType || getCurrentRecordType()
        };

        if (keywordAnalysisStatus) {
            statuses.replace(keywordAnalysisStatus, newStatus);
        } else {
            statuses.push(newStatus);
            opened(true);
        }
    }

    /**
     * Add new generate status
     *
     * @param {Array} productIds
     * @param {Object} currentUser
     * @param {Object} data
     *
     * @returns {void}
     */
    function addNewGenerateStatus(productIds, currentUser, data = {})
    {
        var initialLength = productIds.length;
        if (data) {
            initialLength = data.excludeMode ? data.total : initialLength;
        }
        var generateStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.user === currentUser.email;
        }),
            newStatus = {
                id: tempId,
                message: $t('Preparing for text generation...'),
                user: currentUser.email,
                percent: 0,
                status: 'Preparing',
                queuedIds: productIds,
                completedIds: [],
                completed: 0,
                runningIds: [],
                subType: data.params.auto_optimize_keywords ? 1 : 0,
                total: initialLength,
                recordType: data.recordType || getCurrentRecordType()
        };

        if (generateStatus) {
            statuses.replace(generateStatus, newStatus);
        } else {
            statuses.push(newStatus);
            opened(true);
        }
    }

    /**
     * Add new full automation status
     *
     * @param {Array} productIds
     * @param {Object} currentUser
     * @param {Object} data
     *
     * @returns {void}
     */
    function addNewFullAutomationStatus(productIds, currentUser, data = {})
    {
        var initialLength = productIds.length;
        if (data) {
            initialLength = data.excludeMode ? data.total : initialLength;
        }
        var generateStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.user === currentUser.email;
        }),
            newStatus = {
                id: tempId,
                message: $t('Preparing for full automation...'),
                user: currentUser.email,
                percent: 0,
                status: 'Preparing',
                queuedIds: productIds,
                completedIds: [],
                completed: 0,
                runningIds: [],
                subType: 1,
                total: initialLength,
                recordType: data.recordType || getCurrentRecordType()
        };

        if (generateStatus) {
            statuses.replace(generateStatus, newStatus);
        } else {
            statuses.push(newStatus);
            opened(true);
        }
    }

    /**
     * Add new transfer status
     *
     * @param {Array} productIds
     * @param {Object} currentUser
     *
     * @returns {void}
     */
    function addNewTransferStatus(productIds, currentUser)
    {
        var transferStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.user === currentUser.email;
        }),
            newStatus = {
                id: transferTempId,
                failed: 0,
                message: $t('Preparing for transfer...'),
                user: currentUser.email,
                percent: 0,
                status: 'Preparing',
                transfer: true,
                queuedIds: productIds,
                completedIds: [],
                completed: 0,
                total: productIds.length,
                runningIds: [],
                recordType: getCurrentRecordType()
        };

        if (transferStatus) {
            statuses.replace(transferStatus, newStatus);
        } else {
            statuses.push(newStatus);
            opened(true);
        }
    }

    /**
     * Add new transfer status
     *
     * @param {Array} productIds
     * @param {Object} currentUser
     *
     * @returns {void}
     */
    function addNewSingleGridTransferStatus(productIds, currentUser)
    {
        var transferStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.user === currentUser.email;
        }),
            newStatus = {
                id: transferTempId,
                failed: 0,
                message: $t('Transferring text...'),
                user: currentUser.email,
                percent: 0,
                status: 'Running',
                transfer: true,
                queuedIds: productIds,
                completedIds: [],
                completed: 0,
                total: productIds.length,
                runningIds: [],
                recordType: getCurrentRecordType()
        };
        console.log('newStatus', newStatus)

        if (transferStatus) {
            statuses.replace(transferStatus, newStatus);
        } else {
            statuses.push(newStatus);
            opened(true);
        }
    }

    /**
     * Update cancelling to attempting-cancel status
     *
     * @param {String} requestId
     *
     * @returns {void}
     */
    function updateToAttemptingCancelStatus(requestId)
    {
        var cancelStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.id === requestId;
        });
        if (cancelStatus) {
            console.log('cancelStatus', cancelStatus);
            var newStatus = {...cancelStatus};
            newStatus.status = 'AttemptingCancel';
            newStatus.message = $t('Attempting to cancel bulk action…')
                            + '<br />'
                            + $t('Pending items will be cancelled. Items already in progress will continue.');
            console.log('newStatus', newStatus);
            statuses.replace(cancelStatus, newStatus);
            opened(true);
        }
    }

    /**
     * Update transfer status
     *
     * @param {Array} productIds
     * @param {Object} currentUser
     * @param {Array} queueIds
     * @param {Array} completedIds
     * @param {Object} continueParams
     *
     * @returns {void}
     */
    function updateTransferStatus(
        productIds,
        currentUser,
        queueIds,
        completedIds,
        incomingStatus,
        continueParams = {}
    ) {
        var scopeLabel = (continueParams.recordType || getCurrentRecordType()) === 'Category' ? 'categories' : 'products',
            transferStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.user === currentUser;
            }),
            message = $t('Done transferring text for your selected %1.').replace('%1', scopeLabel),
            percent = Math.floor(completedIds.length / productIds.length * 100),
            status = 'Completed',
            _queuedIds = [],
            _completedIds = [...productIds];

        if (incomingStatus === 'Completed' && productIds.length === 0) {
            return;
        }

        if (completedIds.length !== productIds.length) {
            if (queueIds.length == 0) {
                message = $t('Cancelled');
                status = 'Cancelled';
                _queuedIds = [...queueIds];
                _completedIds = [...completedIds];
            } else if (incomingStatus === 'Pending') {
                message = $t('Preparing for transfer...');
                status = 'Running';
                _queuedIds = [...queueIds];
                _completedIds = [...completedIds];
            } else {
                message = $t('Transferring text...');
                status = 'Running';
                _queuedIds = [...queueIds];
                _completedIds = [...completedIds];
            }
        }

        var newStatus = {
            id: transferTempId,
            failed: 0,
            message: message,
            user: currentUser,
            percent: percent,
            status: status,
            transfer: true,
            queuedIds: _queuedIds,
            completedIds: _completedIds,
            skippedIds: [...new Set(continueParams.skipped_ids || continueParams.skippedIds || [])],
            runningIds: [],
            continueParams: continueParams,
            completed: completedIds.length,
            total: productIds.length,
            recordType: continueParams.recordType || getCurrentRecordType()
        };

        if (transferStatus) {
            statuses.replace(transferStatus, newStatus);
        } else {
            statuses.push(newStatus);
            opened(true);
        }
    }

    /**
     * Process the notification
     *
     * @param {Object} messageEntry
     * @returns {void}
     */
    function processNotification(messageEntry)
    {
        var requestId = messageEntry.encodedMsg.id,
            requestStatus = messageEntry.encodedMsg.status,
            requestEstimatedEndTime = messageEntry.encodedMsg.estimatedEndTime,
            requestStartTime = messageEntry.encodedMsg.startTime,
            requestQueuedIds = messageEntry.encodedMsg.queuedIds,
            requestRunningIds = messageEntry.encodedMsg.runningIds,
            requestCompletedIds = messageEntry.encodedMsg.completedIds,
            requestSkippedIds = messageEntry.encodedMsg.skippedIds,
            requestCompleted = messageEntry.encodedMsg.completed,
            requestTotal = messageEntry.encodedMsg.total,
            requestUserName = messageEntry.encodedMsg.userName,
            requestSubType = Number(messageEntry.encodedMsg.subType),
            requestFailed = messageEntry.encodedMsg.failed,
            requestFailedIds = messageEntry.encodedMsg.failedIds,
            requestFailedTransferIds = messageEntry.encodedMsg.failedTransferIds,
            requestType = messageEntry.encodedMsg.type,
            requestRecordType = messageEntry.encodedMsg.recordType,
            remaining = {},
            message = '',
            requestData = {};

        if (!Array.isArray(requestSkippedIds)) {
            requestSkippedIds = [];
        }

        if (['Completed', 'Failed', 'TimedOut'].includes(requestStatus)) {

            /** Merge all failure-related arrays */
            const allFailed = [
                ...requestFailedIds,
                ...requestQueuedIds,
                ...requestRunningIds,
                ...requestFailedTransferIds
            ];

            /** Deduplicate failed IDs */
            requestFailedIds = [...new Set(allFailed)];
            requestFailed = requestFailedIds.length;

            /** Remove all failed IDs from completed IDs */
            const failedSet = new Set(requestFailedIds);
            requestCompletedIds = requestCompletedIds.filter(id => !failedSet.has(id));
            requestCompleted = requestCompletedIds.length;
        }
        if (requestStatus === 'Failed' && requestFailed === 0) {
            /**
             * Treat as completed if no failed items
             */
            requestStatus = 'Completed';
        }
        /** var completed = requestCompleted !== requestFailed ? requestCompleted - requestFailed : requestCompleted; */

        remaining = calculateRemainingTime(requestEstimatedEndTime, requestStartTime);
        var normalizedMessageEntry = Object.assign({}, messageEntry.encodedMsg, {
            subType: requestSubType
        });
        message = getMessage(normalizedMessageEntry, remaining, requestFailed, requestStatus);
        requestData = {
            id: requestId,
            message: message,
            percent: Math.floor(requestCompleted / requestTotal * 100),
            status: requestStatus,
            queuedIds: requestQueuedIds,
            runningIds: requestRunningIds,
            completedIds: requestCompletedIds,
            skippedIds: [...new Set(requestSkippedIds)],
            completed: requestCompleted,
            user: requestUserName,
            subType: requestSubType,
            failed: requestFailed,
            failedIds: requestFailedIds,
            total: requestTotal,
            transfer: false,
            recordType: requestRecordType
        };
        
        if (requestType === 1 && requestSubType === 3 ) {
            requestData.transfer = true;
        }

        addNotification(
            requestData,
            requestQueuedIds,
            requestCompletedIds,
            requestId
        );
    }

    /**
     * Add stream status
     *
     * @param {Object} messageEntry
     * @returns {void}
     */
    function addStreamStatus(
        messageEntry
    ) {
        processNotification(messageEntry);
    }

    /**
     * Update temporary status' ID from temp to the provided request ID
     *
     * @param {String} requestId - The request ID to replace the temp ID
     * @returns {void}
     */
    function updateTempStatusId(requestId, placeholderId)
    {
        var statusWithTempId = ko.utils.arrayFirst(statuses(), function (status) {
            return status.id === placeholderId;
        });

        if (statusWithTempId) {
            var updatedStatus = Object.assign({}, statusWithTempId, { id: requestId });
            statuses.replace(statusWithTempId, updatedStatus);
        }
    }

    function removeTransferStatus(user)
    {
        var statusWithTempId = ko.utils.arrayFirst(statuses(), function (status) {
            return status.user === user;
        });

        if (statusWithTempId) {
            console.log('remove');
            statuses.remove(statusWithTempId);
        }
    }

    return {
        statuses: statuses,
        tempId: tempId,
        transferTempId: transferTempId,
        koTempId: koTempId,
        opened: opened,
        triggerDismissAll: triggerDismissAll,
        currentBulkRequestId: currentBulkRequestId,
        setScope: setScope,
        restoreStatusesFromBulkRequests: restoreStatusesFromBulkRequests,
        addNewGenerateStatus: addNewGenerateStatus,
        addNewFullAutomationStatus: addNewFullAutomationStatus,
        addStreamStatus: addStreamStatus,
        addNewTransferStatus: addNewTransferStatus,
        addNewSingleGridTransferStatus: addNewSingleGridTransferStatus,
        updateToAttemptingCancelStatus: updateToAttemptingCancelStatus,
        updateTransferStatus: updateTransferStatus,
        updateTempStatusId: updateTempStatusId,
        removeTransferStatus: removeTransferStatus,
        addNewKeywordAnalysisStatus: addNewKeywordAnalysisStatus,
        addKeywordAnalysisFailedStatus: addKeywordAnalysisFailedStatus,
        addGenerateFailedStatus: addGenerateFailedStatus
    };
});
