/**
 * @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);
        
    /**
     * 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 = '';

        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 products.');
                        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 products.');
                        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 products.');
                        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);
                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 = true;
            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
        };

        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
        };


        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
        };

        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
        };

        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
        };

        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: []
        };

        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: []
        };
        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);
        }
    }

    /**
     * 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,
        status,
        continueParams = {}
    ) {
        var transferStatus = ko.utils.arrayFirst(statuses(), function (status) {
                return status.user === currentUser;
        }),
            message = $t('Done transferring text for your selected products.'),
            percent = Math.floor(completedIds.length / productIds.length * 100),
            status = 'Completed',
            _queuedIds = [],
            _completedIds = [...productIds];

        if (completedIds.length !== productIds.length) {
            if (queueIds.length == 0) {
                message = $t('Cancelled');
                status = 'Cancelled';
                _queuedIds = [...queueIds];
                _completedIds = [...completedIds];
            } else if (status === '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,
            runningIds: [],
            continueParams: continueParams,
            completed: completedIds.length,
            total: productIds.length
        };

        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,
            requestCompleted = messageEntry.encodedMsg.completed,
            requestTotal = messageEntry.encodedMsg.total,
            requestUserName = messageEntry.encodedMsg.userName,
            requestSubType = messageEntry.encodedMsg.subType,
            requestFailed = messageEntry.encodedMsg.failed,
            requestFailedIds = messageEntry.encodedMsg.failedIds,
            requestFailedTransferIds = messageEntry.encodedMsg.failedTransferIds,
            requestType = messageEntry.encodedMsg.type,
            remaining = {},
            message = '',
            requestData = {};

        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);
        message = getMessage(messageEntry.encodedMsg, remaining, requestFailed, requestStatus);
        requestData = {
            id: requestId,
            message: message,
            percent: Math.floor(requestCompleted / requestTotal * 100),
            status: requestStatus,
            queuedIds: requestQueuedIds,
            runningIds: requestRunningIds,
            completedIds: requestCompletedIds,
            completed: requestCompleted,
            user: requestUserName,
            subType: requestSubType,
            failed: requestFailed,
            failedIds: requestFailedIds,
            total: requestTotal,
            transfer: false
        };
        
        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,
        addNewGenerateStatus: addNewGenerateStatus,
        addNewFullAutomationStatus: addNewFullAutomationStatus,
        addStreamStatus: addStreamStatus,
        addNewTransferStatus: addNewTransferStatus,
        addNewSingleGridTransferStatus: addNewSingleGridTransferStatus,
        updateToAttemptingCancelStatus: updateToAttemptingCancelStatus,
        updateTransferStatus: updateTransferStatus,
        updateTempStatusId: updateTempStatusId,
        removeTransferStatus: removeTransferStatus,
        addNewKeywordAnalysisStatus: addNewKeywordAnalysisStatus,
        addKeywordAnalysisFailedStatus: addKeywordAnalysisFailedStatus,
        addGenerateFailedStatus: addGenerateFailedStatus
    };
});
