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

define([
    'jquery',
    'ko',
    'uiComponent',
    'WriteTextAI_WriteTextAI/js/model/signalr',
    'WriteTextAI_WriteTextAI/js/model/grid/notifications',
    'WriteTextAI_WriteTextAI/js/model/grid/transfer',
    'WriteTextAI_WriteTextAI/js/model/edit/keywords/keyword-analysis',
    'WriteTextAI_WriteTextAI/js/model/grid/reference-products',
    'WriteTextAI_WriteTextAI/js/utils/signalr',
    'mage/translate'
], function (
    $,
    ko,
    Component,
    signalRModel,
    notifications,
    transfer,
    keywordAnalysis,
    referenceProducts,
    signalrUtils,
    $t
) {
    'use strict';

    /**
     * @var {Component} self
     */
    var self;

    return Component.extend({
        defaults: {
            gridName: 'wtai_products_grid_listing.wtai_products_grid_listing_data_source',
            entityType: 'Product',
            statusCompleted: 'Completed',
            statusTimedout: 'TimedOut',
            statusCancelling: 'Cancelling',
            statusCancelled: 'Cancelled',
            statusRunning: 'Running',
            statusPending: 'Pending',
            statusPreparing: 'Preparing',
            retryLabel: $t('Try Again'),
            opened: false,
            statuses: [],
            user: {},
            tempId: notifications.tempId,
            transferTempId: notifications.transferTempId,
            koTempId: notifications.koTempId,
            cancelBulkTransfer: false,
            isCronRunning: false,
            getCreditsUrl: '',
            getBulkRequestsUrl: ''
        },

        isOpenedChanged: false,

        lastCreditsRefreshAt: 0,

        bulkKoSnapshot: '',

        refreshCredits: function (force) {
            try {
                if (!self || !self.getCreditsUrl) {
                    return;
                }

                var now = Date.now();
                if (!force && this.lastCreditsRefreshAt && (now - this.lastCreditsRefreshAt) < 1500) {
                    return;
                }

                this.lastCreditsRefreshAt = now;
                signalrUtils.getCredits(self);
            } catch (e) {
                // no-op
            }
        },

        refreshBulkRequests: function (callback) {
            if (!self || !self.getBulkRequestsUrl) {
                return;
            }

            return $.ajax({
                url: self.getBulkRequestsUrl,
                type: 'GET',
                dataType: 'json',
                showLoader: false,
                success: function (response) {
                    if (response && response.success && response.bulk_requests && response.bulk_requests.all_statuses) {
                        notifications.restoreStatusesFromBulkRequests(response.bulk_requests.all_statuses);
                    }

                    if (typeof callback === 'function') {
                        callback(response);
                    }
                }
            });
        },

        getBulkKoStatuses: function (statuses) {
            var entityType = String(this.entityType || '').toLowerCase();

            return (statuses || []).filter(function (status) {
                return status &&
                    Number(status.subType) === 2 &&
                    Number(status.total || 0) > 1 &&
                    (!status.recordType || String(status.recordType).toLowerCase() === entityType);
            });
        },

        hasActiveBulkKoStatus: function (statuses) {
            return statuses.some(function (status) {
                return [
                    this.statusPreparing,
                    this.statusPending,
                    this.statusRunning,
                    this.statusCancelling
                ].includes(status.status);
            }, this);
        },

        hasTerminalBulkKoStatus: function (statuses) {
            return statuses.some(function (status) {
                return [
                    this.statusCompleted,
                    this.statusCancelled,
                    this.statusTimedout,
                    'Failed'
                ].includes(status.status);
            }, this);
        },

        buildBulkKoSnapshot: function (statuses) {
            var terminalStatuses = [
                this.statusCompleted,
                this.statusCancelled,
                this.statusTimedout,
                'Failed'
            ];
            var normalizeIds = function (ids) {
                return (Array.isArray(ids) ? ids : []).map(function (id) {
                    return String(id);
                }).sort();
            };

            return JSON.stringify(this.getBulkKoStatuses(statuses).map(function (status) {
                return {
                    id: String(status.id),
                    status: terminalStatuses.includes(status.status) ? status.status : '',
                    completed: Number(status.completed || 0),
                    failed: Number(status.failed || 0),
                    completedIds: normalizeIds(status.completedIds),
                    failedIds: normalizeIds(status.failedIds),
                    skippedIds: normalizeIds(status.skippedIds)
                };
            }).sort(function (left, right) {
                return left.id.localeCompare(right.id);
            }));
        },

        reloadGridForBulkKoChange: function () {
            this.gridReload();
        },

        syncBulkKoGridReload: function (statuses) {
            var bulkKoStatuses = this.getBulkKoStatuses(statuses),
                nextSnapshot = this.buildBulkKoSnapshot(statuses),
                hasActive = this.hasActiveBulkKoStatus(bulkKoStatuses),
                hasTerminal = this.hasTerminalBulkKoStatus(bulkKoStatuses),
                hasSnapshot = !!this.bulkKoSnapshot,
                changed = hasSnapshot && this.bulkKoSnapshot !== nextSnapshot;

            if (!bulkKoStatuses.length) {
                this.bulkKoSnapshot = '';
                return;
            }

            if ((!hasSnapshot || changed) && (hasActive || hasTerminal)) {
                this.reloadGridForBulkKoChange();
            }

            this.bulkKoSnapshot = nextSnapshot;
        },

        /** @inheritdoc */
        initialize: function () {
            this._super();

            self = this;

            try {
                notifications.setScope(this.entityType);
            } catch (e) {
                // no-op
            }

            /** 1. Update statuses model */
            notifications.statuses(this.statuses());
            transfer.isCronRunning(this.isCronRunning());

            self.statuses.subscribe(function (statuses) {
                if (statuses.length === 0) {
                    notifications.opened(false);
                } else {
                    this.refreshCredits(false);
                }
            }, this);

            /** 2. Listen to statuses model */
            notifications.statuses.subscribe(function (statuses) {
                /** Apply statuses to the view model */
                try {
                    var entityType = String(this.entityType || '').toLowerCase();
                    var filtered = (statuses || []).filter(function (status) {
                        if (!status || !status.recordType) {
                            return true;
                        }

                        return String(status.recordType).toLowerCase() === entityType;
                    }, this);
                    this.statuses(filtered);
                } catch (e) {
                    this.statuses(statuses);
                }

                this.syncBulkKoGridReload(this.statuses());
                
                this.dismissFinishedCancelledStatuses();
            }, this);

            notifications.opened.subscribe(function (opened) {
                this.isOpenedChanged = true;
                this.opened(opened);
                this.isOpenedChanged = false;

                if (opened) {
                    this.refreshCredits(false);
                }
            }, this);

            transfer.cancelBulkTransfer.subscribe(function (cancelBulkTransfer) {
                this.cancelBulkTransfer(cancelBulkTransfer);
            }, this);

            this.opened.subscribe(function (opened) {
                if (!this.isOpenedChanged) {
                    notifications.opened(opened);
                }
            }, this);

            notifications.triggerDismissAll.subscribe(function (triggerDismissAll) {
                if (triggerDismissAll) {
                    this.dismissMessages();
                    notifications.triggerDismissAll(false);
                }
            }, this);

            /** 3. Add transfer statuses to model */
            this.initStatuses();
            this.dismissFinishedCancelledStatuses();

            this.syncBulkKoGridReload(this.statuses());

            if (this.statuses().length) {
                this.refreshCredits(true);
            }
        },

        /** @inheritdoc */
        initObservable: function () {
            this._super().observe(['opened', 'statuses', 'user', 'cancelBulkTransfer', 'isCronRunning']);

            this.checkingIsSingleRequest = ko.computed(function () {
                return signalRModel.checkingIsSingleRequest();
            }, this);

            this.allDone = ko.computed(
                () =>
                this.statuses().every(status => {
                    if (this.isCancelling(status)) {
                        return false;
                    }
                    return [this.statusCompleted, this.statusCancelled].includes(status.status);
                })
            );

            this.showDismissAll = ko.computed(function () {
                return this.statuses().length > 1 && this.statuses().some((status) => {
                    return status.status === this.statusCompleted;
                }, this);
            }, this);

            this.timedOut = ko.computed(function () {
                return this.statuses().some((status) => {
                    return status.status === this.statusTimedout;
                }, this);
            }, this);

            this.allProductsFailed = ko.computed(function () {
                return this.statuses().length > 0 && this.statuses().every((status) => {
                    var total = Number(status.total || 0);
                    var failed = Number(status.failed || 0);
                    var skippedCount = Array.isArray(status.skippedIds) ? status.skippedIds.length : 0;

                    return (failed === total && failed > 0) || (total > 0 && skippedCount === total);
                }, this);
            }, this);

            this.showGenerateComment = ko.computed(function () {
                if (this.allProductsFailed()) {
                    return false;
                }

                var hasOngoing = this.statuses().some((status) => {
                    return [
                        this.statusPending,
                        this.statusRunning,
                        this.statusPreparing,
                        this.statusCancelled,
                        this.statusTimedout
                    ].includes(status.status);
                }, this);
                /** return false if at least 1 failed status */
                if (!hasOngoing) {
                    return false;
                }
                return this.statuses().length > 0 && this.statuses().every((status) => {
                    return [undefined, 0, 3].includes(status.subType);
                }, this);
            }, this);

            this.showKOComment = ko.computed(function () {
                if (this.allProductsFailed()) {
                    return false;
                }

                var hasOngoing = this.statuses().some((status) => {
                    return [
                        this.statusPending,
                        this.statusRunning,
                        this.statusPreparing,
                        this.statusCancelled,
                        this.statusTimedout
                    ].includes(status.status);
                }, this);
                /** return false if at least 1 failed status */
                if (!hasOngoing) {
                    return false;
                }
                return this.statuses().length > 0 && this.statuses().some((status) => {
                    return [2, 1].includes(status.subType);
                }, this);
            }, this);

            this.isBulkKeywordAnalysis = ko.computed(function () {
                return this.statuses().some((status) => {
                    return status.subType === 2;
                }, this);
            }, this);

            this.showTransferPendingComment = ko.computed(function () {
                if (this.allProductsFailed()) {
                    return false;
                }

                return this.statuses().length > 0 && this.statuses().some((status) => {
                    return status.transfer === true && status.status === 'Pending';
                }, this);
            }, this);

            this.showManualTransferRunningComment = ko.computed(function () {
                if (this.allProductsFailed()) {
                    return false;
                }

                var isTransferRunning = this.statuses().length > 0 && this.statuses().some((status) => {
                    return status.transfer === true && status.status === 'Running';
                }, this);

                if (isTransferRunning && (this.isCronRunning() === false)) {
                    return true;
                }
                return false;

            }, this);

            this.showTabOpenComment = ko.computed(function () {
                return notifications.currentBulkRequestId() === notifications.transferTempId;
            }, this);

            return this;
        },

        /**
         * Reload grid.
         */
        gridReload: function () {
            var showLoader = false,
                debounce = true;

            if (!this.gridName) {
                return;
            }

            require([
                'WriteTextAI_WriteTextAI/js/grid/reload'
            ], function (reloadGrid) {
                reloadGrid.reloadUIComponent(this.gridName, showLoader, debounce);
            }.bind(this));
        },

        dismissFinishedCancelledStatuses: function () {
            var cancelledStatuses = this.statuses().filter(function (status) {
                return String(status.status || '').toLowerCase() === String(this.statusCancelled).toLowerCase() && !this.isCancelling(status);
            }, this);

            if (cancelledStatuses.length) {
                    this.gridReload();
                cancelledStatuses.forEach(function (status) {
                    this.dismissMessage(status);
                }, this);
            }
        },

        /**
         * Initialize statuses.
         */
        initStatuses: function () {
            var transferStatuses = this.transferStatuses;

            for (var i = 0; i < transferStatuses.length; i++) {
                let queueIds = transferStatuses[i].queue_ids ?? [];
                let completedIds = transferStatuses[i].completed_ids ?? [];

                notifications.updateTransferStatus(
                    [...queueIds, ...completedIds],
                    transferStatuses[i].user,
                    queueIds,
                    completedIds,
                    transferStatuses[i].status,
                    transferStatuses[i]
                );
            }
        },

        /**
         * Check if the status is cancelling.
         *
         * @param {Object} status
         * @return {Boolean}
         */
        isCancelling: function (status) {
            return signalrUtils.isCancelling(status);
        },

        /**
         * Get the message.
         *
         * @param {Object} status
         * @return {String}
         */
        getMessage: function (status) {
            var isProductOptimizing = keywordAnalysis.statuses().find(function (data) {
                return Number(data.storeId) === Number(referenceProducts.storeId()) &&
                    status?.queuedIds?.includes(data.recordId) &&
                    data.status === 'Running';
            });

            if (isProductOptimizing) {
                return $t('Keyword analysis currently in progress');
            }

            var isTransfer = status && status.transfer === true;
            var total = Number(status?.total ?? 0);
            var completedIdsCount = status?.completedIds?.length ?? 0;
            var skippedCount = status?.skippedIds?.length ?? 0;
            var failedCount = Number(status?.failed ?? 0);

            if (isTransfer && total > 0 && skippedCount === total && status.status === 'Completed') {
                var entityType = String(status?.recordType || this.entityType || '').toLowerCase();
                var noGeneratedTextMessage = entityType === 'category' ?
                    $t('Transfer failed because there are no generated texts yet for the categories selected.') :
                    $t('Transfer failed because there are no generated texts yet for the products selected.');

                return noGeneratedTextMessage + '</br> ' + $t('Generate text first before transferring.');
            }

            return status.message || status.status;
        },

        /**
         * Get the failed count.
         *
         * @param {Object} status
         * @return {String}
         */
        getFailedCount: function (status) {
            var failedCount = status.failed;
            
            if (failedCount > 0) {
                return $t('%s failed').replace('%s', failedCount).toLowerCase();
            }

            return "";
        },
        /**
         * Get the skipped count.
         *
         * @param {Object} status
         * @return {String}
         */
        getSkippedCount: function (status) {
            var skippedCount = status.skippedIds?.length ?? 0;
            var failedCount = status.failed || 0;
            
            var processStatus = status.status;

            if (processStatus === 'Running' || processStatus === 'Pending') {
                return "";
            }
            
            if (skippedCount > 0) {
                if (failedCount > 0) {
                    return $t('%s skipped, and ').replace('%s', skippedCount).toLowerCase();
                } else {
                    return $t('%s skipped').replace('%s', skippedCount).toLowerCase();
                }
            }

            return "";
        },

        /**
         * getSummaryMessage
         *
         * @param {Object} status
         * @return {String}
         */
        getSummaryMessage: function (status) {
            var processStatus = status.status,
                totalCount = status.total,
                completedCount = status.completed,
                completedIdsCount = status.completedIds?.length ?? 0,
                failedCount = status.failed;

            var skippedCount = status?.skippedIds?.length ?? 0;

            if (failedCount === totalCount) {
                return "";
            }

            /** Running or Pending */
            if (processStatus === 'Running' || processStatus === 'Pending') {
                if (completedIdsCount > 0) {
                    var processedCount = completedIdsCount;
                } else {
                    var processedCount = completedCount;
                }

                var processedMsg = "";
                processedCount = Math.max(processedCount - failedCount, 0);
                if (failedCount > 0) {
                    processedMsg = $t('%s/%s processed and ')
                    .replace('%s', processedCount)
                    .replace('%s', totalCount);
                } else {
                    processedMsg = $t('%s/%s processed')
                    .replace('%s', processedCount)
                    .replace('%s', totalCount);
                }

                return processedMsg;
            }

            /** Done (Completed/Failed/Other) */
            if (completedIdsCount > 0) {
                let adjustedSuccess = Math.max(completedCount - skippedCount, 0);
      
                if (adjustedSuccess === 0) {
                    return "";
                } else if (failedCount > 0 && skippedCount > 0) {
                    return $t('%s successful,').replace('%s', adjustedSuccess);
                } else if (skippedCount > 0) {
                    return $t('%s successful and').replace('%s', adjustedSuccess);
                } else if (failedCount > 0) {
                    return $t('%s successful and').replace('%s', adjustedSuccess);
                } else {
                    return $t('%s successful').replace('%s', adjustedSuccess);
                }
            }
            return "";
        },

        /**
         * Expand the notification bar.
         */
        seeMore: function () {
            self.opened(true);
        },

        /**
         * Collapse the notification bar.
         */
        seeLess: function () {
            self.opened(false);
        },

        /**
         * Check if the transfer is cancelled.
         */
        isCancelledTransfer: function () {
            return self.cancelBulkTransfer();
        },

        /**
         * Dismiss the notification.
         *
         * @param {Object} item
         * @return {void}
         */
        dismissMessage: function (item) {
            var data = {
                request_id: item.id
            };

            if (item.transfer) {
                data = {
                    transfer_user: item.user,
                    type: 'transfer'
                };
            }

            if (item.subType === 0) {
                data = {
                    request_id: item.id,
                    type: 'generate'
                };
            }

            if (item.subType === 1) {
                data = {
                    request_id: item.id,
                    type: 'full_automation'
                };
            }

            if (item.subType === 2) {
                data = {
                    request_id: item.id,
                    type: 'keyword_analysis'
                };
            }

            /** Remove from source model so it won't be re-applied via subscription/localStorage */
            try {
                notifications.statuses.remove(function (status) {
                    if (!status) {
                        return false;
                    }

                    if (item.recordType && status.recordType && String(item.recordType) !== String(status.recordType)) {
                        return false;
                    }

                    if (item.transfer) {
                        return status.transfer === true && status.user === item.user;
                    }

                    return String(status.id) === String(item.id);
                });
            } catch (e) {
                // no-op
            }

            /** Remove from local view list (filtered statuses observable) */
            this.statuses.remove(function (status) {
                if (!status) {
                    return false;
                }

                if (item.recordType && status.recordType && String(item.recordType) !== String(status.recordType)) {
                    return false;
                }

                if (item.transfer) {
                    return status.transfer === true && status.user === item.user;
                }

                return String(status.id) === String(item.id);
            });
        },

        /**
         * Dismiss all notifications.
         */
        dismissMessages: function () {
            var self = this;

            try {
                notifications.statuses.remove(function (item) {
                    return item && item.status === self.statusCompleted;
                });
            } catch (e) {
                // no-op
            }

            this.statuses.remove(function (item) {
                return item && item.status === self.statusCompleted;
            });
        },

        /**
         * Cancel the generate.
         *
         * @param {Number} id
         * @return {void}
         */
        cancelBulk: function (id) {
            notifications.opened(true);
            notifications.updateToAttemptingCancelStatus(id);
            $.ajax({
                url: self.cancelUrl,
                type: 'POST',
                data: {
                    id: id
                },
                dataType: 'json',
                showLoader: true,
                success: function (response) {
                    if (!response.success) {
                        console.log(response.message);
                        return;
                    }

                    self.gridReload();

                    if (self.getCreditsUrl) {
                        signalrUtils.getCredits(self);
                        setTimeout(function () {
                            signalrUtils.getCredits(self);
                        }, 1000);
                    }
                }
            });
        },

        /**
         * Cancel the transfer.
         *
         * @param {Number} id
         * @return {void}
         */
        cancelTransfer: function (id) {
            if (transfer.cancelRequest()) {
                transfer.cancelRequest().abort();
            }
            var curUser = self.user();
            $.ajax({
                url: self.transferCancelUrl,
                type: 'POST',
                data: {
                    id: id,
                    user: curUser.email
                },
                dataType: 'json',
                showLoader: true,
                success: function (response) {
                    if (!response.success) {
                        console.log(response.message);
                    } else if (self.getCreditsUrl) {
                        signalrUtils.getCredits(self);
                        setTimeout(function () {
                            signalrUtils.getCredits(self);
                        }, 1000);
                    }
                    transfer.cancelBulkTransfer(true);
                }
            });
        },

        /**
         * Retry the bulk generate.
         *
         * @param {Number} id
         * @return {void}
         */
        retry: function (id) {
            var url = self.retryUrl;

            $.ajax({
                url: url,
                type: 'POST',
                data: {
                    id: id
                },
                dataType: 'json',
                showLoader: true,
                success: function (response) {
                    if (!response.success) {
                        console.log(response.message);
                        return;
                    }

                    if (self.getCreditsUrl) {
                        signalrUtils.getCredits(self);
                        setTimeout(function () {
                            signalrUtils.getCredits(self);
                        }, 1000);
                    }
                }
            });
        }
    });
});
