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

define([
    'jquery',
    'ko',
    'uiComponent',
    'WriteTextAI_WriteTextAI/js/categories/model/edit/textfields',
    'WriteTextAI_WriteTextAI/js/model/edit/product',
    'WriteTextAI_WriteTextAI/js/categories/model/signalr',
    'WriteTextAI_WriteTextAI/js/model/edit',
    'WriteTextAI_WriteTextAI/js/model/edit/feedback',
    'WriteTextAI_WriteTextAI/js/model/edit/mark-reviewed',
    'WriteTextAI_WriteTextAI/js/model/edit/keywords/keyword-analysis',
    'WriteTextAI_WriteTextAI/js/model/edit/keywords/keywords',
    'WriteTextAI_WriteTextAI/js/model/edit/generate/currently-generating',
    'WriteTextAI_WriteTextAI/js/categories/model/edit/invalid-image',
    'WriteTextAI_WriteTextAI/js/grid/reload',
    'WriteTextAI_WriteTextAI/js/utils/signalr',
    'WriteTextAI_WriteTextAI/js/model/edit/audience',
    'WriteTextAI_WriteTextAI/js/model/edit/keywords/progress',
    'wtaiSignalR',
    'mage/translate'
], function (
    $,
    ko,
    Component,
    textfields,
    product,
    signalRModel,
    editData,
    feedbackData,
    markReviewed,
    keywordAnalysis,
    keywords,
    currentlyGeneratingModel,
    invalidImageModel,
    reloadGrid,
    signalrUtils,
    audience,
    keywordsProgressModel,
    signalR,
    $t
) {
    'use strict';

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

    return Component.extend({
        defaults: {
            productId: '',
            storeId: '',
            connection: null,
            retryReconnectAttempt: 0,
            enableMaxRetryAttempt: false,
            maxRetryAttempt: 10,
            accessToken: '',
            signalRUrl: '',
            singleGenerate: 0,
            fieldStatus: 2,
            optimization: 4,
            snapshot: 5,
            openAnalysis: 6,
            bulkGenerate: 1,
            message: $t('Text generation completed'),
            showMessage: false,
            messageSuccess: false,
            successMessage: $t('Text generation completed'),
            invalidImagePopupDoNotShowAgain: false,
            pageTitle: '',
            pageDescription: '',
            categoryDescription: '',
            errorMessage: '',
            statusFailed: 'Failed',
            statusCompleted: 'Completed',
            
            /** SignalR Connection Status Constants */
            connectionStatusDisconnected: 'Disconnected',
            connectionStatusConnecting: 'Connecting',
            connectionStatusConnected: 'Connected',
            connectionStatusReconnecting: 'Reconnecting',
            connectionStatusFailed: 'Failed'
        },

        isGettingNewToken: false,
        gettingData: [],
        isFetchingGeneratedData: false,

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

            self = this;
            
            /** Initialize connection status in model */
            this.updateConnectionStatus(this.connectionStatusDisconnected, '', false, 'Not initialized');

            this.connectTostream();

            product.productId.subscribe(function (productId) {
                self.productId(productId);
            });

            product.storeId.subscribe(function (storeId) {
                self.storeId(storeId);
            });

            signalRModel.showMessage.subscribe(function (show) {
                self.showMessage(show);
            });

            signalRModel.message.subscribe(function (message) {
                self.message(message);
            });

            signalRModel.isConnected.subscribe(function (isConnected) {
                if (isConnected) {
                    self.handleRefreshingData();
                }
            });
            
            signalRModel.triggerHandleUserReturn.subscribe(function (triggerHandleUserReturn) {
                if (triggerHandleUserReturn) {
                    self.handleUserReturn();
                    signalRModel.triggerHandleUserReturn(false);
                }
            });
        },

        /** @inheritdoc */
        initObservable: function () {
            this._super().observe([
                'productId',
                'storeId',
                'accessToken',
                'signalRUrl',
                'message',
                'showMessage',
                'messageSuccess',
                'errorMessage'
            ]);

            this.generateComplete = ko.computed(function () {
                var fieldsGenerated = signalRModel.fieldsGenerated(),
                    fieldsValues = Object.values(fieldsGenerated),
                    hasTrue = fieldsValues.includes(true),
                    hasFalse = fieldsValues.includes(false);

                var isGenerating = signalRModel.generating(),
                    isTextsGenerating = hasTrue || hasFalse,
                    isTextsGenerated = hasTrue && !hasFalse;

                if (isGenerating && isTextsGenerating && isTextsGenerated) {
                    return true;
                }

                return false;
            });

            this.generateComplete.subscribe(function (generateComplete) {
                if (generateComplete) {
                    /**self.catchFieldsError();
                     *self.getGenerated();
                     */
                }
                if (keywordAnalysis.refreshingData()) {
                    keywordAnalysis.refreshingData(false);
                    signalrUtils.getCredits(self);
                    self.getOptimization();
                    self.gridReload();
                }
            });

            return this;
        },
        
        /**
         * Handle returning from idle state to sync generated texts.
         */
        handleUserReturn: function () {
            if (signalRModel.toGetGenerated() && !self.isFetchingGeneratedData) {
                self.catchFieldsError();
                self.getGenerated();
            }
        },

        /**
         * Handle refreshing data.
         */
        handleRefreshingData: function () {
            if (keywordAnalysis.refreshingData()) {
                self.getOptimization();
            }
        },

        /**
         * Catch fields error.
         */
        catchFieldsError: function () {
            self.errorMessage('');
            self.messageSuccess(true);
            self.getNormalTextsError();
        },

        /**
         * Get normal texts error.
         */
        getNormalTextsError: function () {
            var errorFields = signalRModel.errorFields(),
                selectedFields = signalRModel.selectedFields(),
                htmlMessage = $t('Error encountered while generating the following text: %s')
                    .replace('%s', '<ul>' + errorFields.map(field => `<li>${field}</li>`).join('') + '</ul>');
                    
            htmlMessage += self.getImageFailedMessage();

            if (selectedFields.length > 0 && selectedFields.length === errorFields.length) {
                self.messageSuccess(false);
                if (signalRModel.errorMessage()) {
                    signalRModel.message(signalRModel.errorMessage());
                } else {
                    signalRModel.message(self.successMessage);
                    self.errorMessage(htmlMessage);
                }
            } else if (errorFields.length > 0) {
                signalRModel.message(self.successMessage);
                self.errorMessage(htmlMessage);
            } else {
                self.errorMessage(self.getImageFailedMessage());
            }
        },

        /**
         * Get image failed message.
         *
         * @returns {String}
         */
        getImageFailedMessage: function () {
            if (this.invalidImagePopupDoNotShowAgain || window.wtaiInvalidImagePopupDoNotShowAgain) {
                return '';
            }

            var subMessage = $t('Image analysis will be skipped for categories or representative products with invalid images, and image descriptions may be missing.'),
                imagesHtml = $t('Error encountered while uploading some images.%s %s')
                    .replace('%s', '<ul></ul>')
                    .replace('%s', '<span class="wtai-submessage">' + subMessage + '</span>');

            if (invalidImageModel.invalidImages().length > 0) {
                invalidImageModel.invalidImages().forEach(element => {
                    imagesHtml += '<img src="' + element + '">';
                });

                return imagesHtml;
            }
            return '';
        },

        /**
         * Hide text generation success message.
         */
        hideMessage: function () {
            signalRModel.showMessage(false);
        },

        /**
         * Connect to signalR stream.
         */
        connectTostream: function () {
            if (self.accessToken()) {
                /** Update connection status to Connecting */
                self.updateConnectionStatus(self.connectionStatusConnecting, '', false, null);
                
                self.connection = new signalR.HubConnectionBuilder()
                    .withUrl(self.signalRUrl(), {
                        accessTokenFactory: () => self.accessToken()
                    })
                    .build();
                self.connection
                    .start()
                    .then(() => self.onConnected(self.connection))
                    .catch((error) => self.onConnectionError(error));

                self.bindConnectionMessageGenerate(self.connection);

                $(window).on('focus', function () {
                    if (
                        self.connection != null &&
                        self.connection.connectionState != "Connected" &&
                        self.connection.connectionState != "Connecting"
                    ) {
                        self.updateConnectionStatus(self.connectionStatusReconnecting, self.connection.connectionState, false, null);
                        self.connection.start();
                    }
                });
            } else {
                self.updateConnectionStatus(self.connectionStatusDisconnected, '', false, 'No access token available');
            }
        },

        /**
         * Connection is established actions.
         *
         * @param {object} connection
         * @returns {void}
         */
        onConnected: function (connection) {
            /** Update connection status to Connected */
            self.updateConnectionStatus(self.connectionStatusConnected, connection.connectionState, true, null);
            /** Reset retry attempts on successful connection */
            self.retryReconnectAttempt = 0;
            signalRModel.retryAttempts(0);
            connection.send('broadcastMessage', '_SYSTEM_', 'Connected');
        },

        /**
         * Connection error actions.
         *
         * @param {object} error
         * @returns {void}
         */
        onConnectionError: function (error) {
            var doConnect = true;
            var errorMessage = error && error.message ? error.message : 'Connection error';
            
            /** Update connection status to Disconnected with error */
            self.updateConnectionStatus(self.connectionStatusDisconnected, '', false, errorMessage);

            if (error.statusCode === 401 && !self.isGettingNewToken) {
                self.getNewAccessToken();
            }

            if (self.enableMaxRetryAttempt && self.retryReconnectAttempt >= self.maxRetryAttempt) {
                doConnect = false;
                self.updateConnectionStatus(self.connectionStatusFailed, '', false, 'Max retry attempts reached');
            }

            if (doConnect) {
                /** Update retry attempts in model */
                signalRModel.retryAttempts(self.retryReconnectAttempt);
                
                if (self.retryReconnectAttempt === 0) {
                    self.connectTostream();
                } else {
                    self.updateConnectionStatus(self.connectionStatusReconnecting, '', false, 'Retrying connection in 5 seconds...');
                    setTimeout(function () {
                        self.connectTostream(); /** attempt to connect every 5 seconds */
                    }, 5000);
                }

                self.retryReconnectAttempt++;
            }
        },

        /**
         * Get new access token.
         */
        getNewAccessToken: function () {
            var url = self.getNewAccessTokenUrl;
        
            self.isGettingNewToken = true;
            $.ajax({
                url: url,
                type: 'GET',
                dataType: 'json',
                showLoader: false,
                success: function (response) {
                    if (response.success) {
                        self.accessToken(response.access_token);
                    }
                }
            }).always(function () {
                self.isGettingNewToken = false;
            });
        },

        /**
         * Update connection status in model.
         *
         * @param {String} status
         * @param {String} state
         * @param {Boolean} connected
         * @param {String} error
         * @returns {void}
         */
        updateConnectionStatus: function (status, state, connected, error) {
            signalRModel.connectionStatus(status);
            signalRModel.connectionState(state || '');
            signalRModel.isConnected(connected);
            signalRModel.connectionError(error || '');
            
            if (connected) {
                signalRModel.lastConnectionTime(new Date().toISOString());
            }
            
            console.log('Categories SignalR Connection Status Updated:', {
                status: status,
                state: state,
                connected: connected,
                error: error,
                retryAttempts: signalRModel.retryAttempts()
            });
        },

        /**
         * Bind connection messages.
         *
         * @param {Object} connection
         * @returns {void}
         */
        bindConnectionMessageGenerate: function (connection) {
            if (connection === null) {
                /* bypass */
            }

            connection.on('broadcastMessage', self.messageCallbackWrapper);
            connection.onclose(self.onConnectionError);
            
            /** Monitor connection state changes */
            if (connection.onreconnecting) {
                connection.onreconnecting((error) => {
                    self.updateConnectionStatus(self.connectionStatusReconnecting, connection.connectionState, false, error ? error.message : null);
                });
            }
            
            if (connection.onreconnected) {
                connection.onreconnected((connectionId) => {
                    self.updateConnectionStatus(self.connectionStatusConnected, connection.connectionState, true, null);
                    console.log('Categories SignalR reconnected with connection ID:', connectionId);
                });
            }
        },

        /**
         * Parse message.
         *
         * @param {String} encodedName
         * @param {String} encodedMsg
         *
         * @returns {Object}
         */
        createMessageEntry: function (encodedName, encodedMsg) {
            return {
                encodedName: encodedName,
                encodedMsg: JSON.parse(encodedMsg)
            };
        },

        /**
         * Update text fields.
         *
         * @param {String} field
         * @param {String} value
         *
         * @returns {void}
         */
        updateField: function (field, value) {
            switch (field) {
                case self.pageTitle:
                    textfields.pageTitle(value);
                    break;
                case self.pageDescription:
                    textfields.pageDescription(value);
                    break;
                case self.categoryDescription:
                    /** textfields.categoryDescription(value); */
                    break;
            }
        },

        /**
         * Get text field value.
         *
         * @param {String} field
         * @returns {String}
         */
        getField: function (field) {
            var value = '';

            switch (field) {
                case self.pageTitle:
                    value = textfields.pageTitle();
                    break;
                case self.pageDescription:
                    value = textfields.pageDescription();
                    break;
                case self.categoryDescription:
                    value = textfields.categoryDescription();
                    break;
            }

            return value;
        },

        /**
         * Get index field.
         *
         * @param {String} field
         * @returns {String}
         */
        getIndexField: function (field) {
            var value = '';

            switch (field) {
                case self.pageTitle:
                    value = 'page_title';
                    break;
                case self.pageDescription:
                    value = 'page_description';
                    break;
                case self.categoryDescription:
                    value = 'category_description';
                    break;
            }

            return value;
        },

        /**
         * Message callback wrapper
         */
        messageCallbackWrapper: function (name, message) {
            try {
                self.messageCallback(name, message);
            } catch (error) {
                console.log('Error in messageCallbackWrapper:', error);
            }
        },

        /**
         * Process stream message.
         *
         * @param {String} name
         * @param {String} message
         *
         * @returns {void}
         */
        messageCallback: function (name, message) {
            if (message === 'Connected') {
                return;
            }
            var encodedName = name,
                encodedMsg = message
                    .replace(/&/g, '&amp;')
                    .replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;'),
                messageEntry = self.createMessageEntry(encodedName, encodedMsg),
                streamRecordType = messageEntry.encodedMsg.recordType,
                streamType = messageEntry.encodedMsg.type,
                streamSubType = messageEntry.encodedMsg.subType,
                streamFieldType = messageEntry.encodedMsg.field,
                streamStop = messageEntry.encodedMsg.stop,
                streamPartialText = messageEntry.encodedMsg.partialText,
                streamRecordId = messageEntry.encodedMsg.recordId,
                streamStoreId = Number(messageEntry.encodedMsg.storeId),
                streamIndex = messageEntry.encodedMsg.index,
                streamQueuedIds = messageEntry.encodedMsg.queuedIds,
                streamRunningIds = messageEntry.encodedMsg.runningIds,
                streamFailedIds = messageEntry.encodedMsg.failedIds,
                streamFailedTransferIds = messageEntry.encodedMsg.failedTransferIds,
                streamCompletedIds = messageEntry.encodedMsg.completedIds,
                generationType = messageEntry.encodedMsg.generationType,
                indexField = '',
                streamStatus = messageEntry.encodedMsg.status,
                fieldsGenerated = {},
                streamSubType = messageEntry.encodedMsg.subType,
                productId = self.productId(),
                storeId = Number(self.storeId()),
                entityType = self.entityType;

            console.log(messageEntry.encodedMsg);
            
            switch (streamType) {
                case self.bulkGenerate:
                    if (streamRecordType === 'Product') {
                        break;
                    }
                    var requestFailedIds = [],
                        requestFailed = 0,
                        requestCompletedIds = [],
                        requestCompleted = 0;

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

                        /** Merge all failure-related arrays */
                        const allFailed = [
                            ...streamFailedIds,
                            ...streamRunningIds,
                            ...streamQueuedIds,
                            ...streamFailedTransferIds
                        ];

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

                        /** Remove all failed IDs from completed IDs */
                        const failedSet = new Set(requestFailedIds);
                        requestCompletedIds = streamCompletedIds.filter(id => !failedSet.has(id));
                        requestCompleted = requestCompletedIds.length;
                    }

                    switch (streamSubType) {
                        case 0:
                        case 1:
                            /** remove identifier from currently generating record identifiers if status is failed */
                            var identifier = entityType + '_' +  streamRecordId + '_' + streamStoreId;
                            var currentlyGeneratingRecordIdentifiers = currentlyGeneratingModel.recordIdentifiers();
                            if (currentlyGeneratingRecordIdentifiers.includes(identifier) && streamStatus === self.statusFailed) {
                                currentlyGeneratingRecordIdentifiers.splice(currentlyGeneratingRecordIdentifiers.indexOf(identifier), 1);
                                currentlyGeneratingModel.recordIdentifiers(currentlyGeneratingRecordIdentifiers);
                            }

                            /** Suppress bulk generate progress bar if keyword analysis is ongoing */
                            if (messageEntry.encodedMsg.id.includes('KeywordOptimization') || (
                                messageEntry.encodedMsg.total === 1
                            )) {
                                var queuedIds = messageEntry.encodedMsg.queuedIds;
                                queuedIds.forEach((id) => {
                                    var identifier = entityType + '_' +  id + '_' + streamStoreId;
                                    var currentlyGeneratingRecordIdentifiers = currentlyGeneratingModel.recordIdentifiers();
                                    if (!currentlyGeneratingRecordIdentifiers.includes(identifier) && streamStatus !== self.statusFailed) {
                                        currentlyGeneratingRecordIdentifiers.push(identifier);
                                        currentlyGeneratingModel.recordIdentifiers(currentlyGeneratingRecordIdentifiers);
                                    }
                                });

                                if (messageEntry.encodedMsg.subType === 0 &&
                                    storeId === streamStoreId &&
                                    currentlyGeneratingModel.recordIdentifiers().includes(
                                        entityType + '_' +  productId + '_' + streamStoreId
                                    )
                                ) {
                                    if (messageEntry.encodedMsg.status === 'Pending') {
                                        signalRModel.editTitleStatusText($t('Queued for text generation...'));
                                    } else {
                                        signalRModel.editTitleStatusText($t('Generating text...'));
                                    }
                                }
                                keywordsProgressModel.addOrUpdateStatus(messageEntry.encodedMsg);


                                if (messageEntry.encodedMsg.status === 'Failed' || messageEntry.encodedMsg.status === 'Completed'
                                    || messageEntry.encodedMsg.status === 'Cancelled' || messageEntry.encodedMsg.status === 'TimedOut'
                                ) {
                                    if (signalRModel.generating() === true &&
                                        storeId === streamStoreId &&
                                        (streamCompletedIds.includes(productId) ||
                                        streamFailedIds.includes(productId) ||
                                        streamRunningIds.includes(productId)) &&
                                        requestCompleted !== 0
                                    ) {
                                        self.catchFieldsError();
                                        self.getGenerated();
                                    }
                                    var completedId = streamCompletedIds;
                                    completedId.forEach((id) => {
                                        var identifier = entityType + '_' +  id + '_' + streamStoreId;
                                        var currentlyGeneratingRecordIdentifiers = currentlyGeneratingModel.recordIdentifiers();
                                        if (currentlyGeneratingRecordIdentifiers.includes(identifier)) {
                                            currentlyGeneratingRecordIdentifiers.splice(currentlyGeneratingRecordIdentifiers.indexOf(identifier), 1);
                                            currentlyGeneratingModel.recordIdentifiers(currentlyGeneratingRecordIdentifiers);
                                        }
                                    });
                                }
                            }

                            if (messageEntry.encodedMsg.status === 'Cancelled'
                                || messageEntry.encodedMsg.status === 'Completed'
                                || messageEntry.encodedMsg.status === 'Failed'
                            ) {
                                /** reload grid */
                                signalrUtils.getCredits(self);
                            }
                            break;
                        case 2:
                            if (messageEntry.encodedMsg.id.includes('KeywordOptimization')) {
                                /** Use the model's method to add or update status */
                                keywordsProgressModel.addOrUpdateStatus(messageEntry.encodedMsg);
                            }
                            break;
                    }
                    break;
                case self.fieldStatus:
                    if (signalRModel.generating() === false) {
                        break;
                    }

                    if (streamStatus === self.statusFailed) {
                        signalRModel.errorFields.push(streamFieldType);
                        signalRModel.errorMessage(messageEntry.encodedMsg.error);
                    }

                    if (productId === streamRecordId && storeId === streamStoreId) {
                        indexField = self.getIndexField(streamFieldType);
        
                        fieldsGenerated = signalRModel.fieldsGenerated();
                        fieldsGenerated[indexField] = true;
                        signalRModel.fieldsGenerated(fieldsGenerated);
                        var statuses = textfields.statuses();
                        switch (indexField) {
                            case 'page_title':
                                $('.wtai-page-title-loading-mask').removeClass('wtai-show');
                                statuses['pageTitleGenerateStatus'] = true;
                                break;
                            case 'page_description':
                                $('.wtai-page-description-loading-mask').removeClass('wtai-show');
                                statuses['pageDescriptionGenerateStatus'] = true;
                                break;
                            case 'category_description':
                                $('.wtai-category-description-loading-mask').removeClass('wtai-show');
                                statuses['categoryDescriptionGenerateStatus'] = true;
                                break;
                        }
                        textfields.statuses(statuses);
                    }
                    break;
                case self.singleGenerate:
                case self.optimization:
                    if (streamRecordType === 'Product') {
                        break;
                    }
                    var statuses = keywordAnalysis.statuses(),
                        storeId = Number(messageEntry.encodedMsg.storeId),
                        recordId = messageEntry.encodedMsg.recordId,
                        existingIndex = -1;

                    statuses.forEach(function (status, index) {
                        if (Number(status.storeId) === Number(storeId) && status.recordId === recordId) {
                            existingIndex = index;
                        }
                    });

                    if (existingIndex >= 0) {
                        statuses[existingIndex] = messageEntry.encodedMsg;
                    } else {
                        statuses.push(messageEntry.encodedMsg);
                    }

                    keywordAnalysis.statuses(statuses);

                    if (messageEntry.encodedMsg.status === 'Failed'
                        && self.productId() === streamRecordId && storeId === streamStoreId
                    ) {
                        if (signalRModel.generating() === true) {
                            self.repopulateTextFields();
                        }
                        /** reset optimizing id if optimization failed
                         * This will allow the generation progress bar to continue even if optimization failed
                        */
                        keywordAnalysis.optimizingId("");
                    }
                    break;
                case self.snapshot:
                    if (productId === streamRecordId && storeId === streamStoreId) {
                        keywordAnalysis.refreshingData(true);
                        if (streamStatus === self.statusCompleted) {
                            keywordAnalysis.refreshingData(false);
                            signalrUtils.getCredits(self);
                            self.getOptimization();
                            self.gridReload();
                        }
                    }
                    break;
                case self.openAnalysis:
                    /** $('.wtai-keyword-analysis').modal('openModal'); */
                    break;
            }
        },

        /**
         * Process single generate.
         *
         * @param {String} streamFieldType
         * @param {Boolean} streamStop
         * @param {String} streamPartialText
         * @param {Number} streamIndex
         *
         * @returns {void}
         */
        processSingleGenerate: function (
            streamFieldType,
            streamStop,
            streamPartialText,
            streamIndex
        ) {
            var currentValue = self.getField(streamFieldType),
                newValue = currentValue + streamPartialText;

            /* generate starts */
            if (streamIndex === 0) {
                self.updateField(streamFieldType, streamPartialText);
            }

            /* generate partial */
            if (
                streamPartialText !== null &&
                streamIndex !== null &&
                streamIndex !== 0 &&
                streamStop === false
            ) {
                self.updateField(streamFieldType, newValue);
            }
        },

        /**
         * Get generated text fields.
         */
        getGenerated: function () {
            var url = self.getGeneratedUrl,
                statuses = textfields.statuses(),
                productId = self.productId(),
                storeId = self.storeId(),
                selectedFields = signalRModel.selectedFields() || [],
                entityType = self.entityType;

            self.isFetchingGeneratedData = true;

            $.ajax({
                url: url,
                type: 'POST',
                data: {
                    category_id: productId,
                    store_id: storeId
                },
                dataType: 'json',
                showWriteTextAILoader: true,
                showPageTitleLoader: selectedFields.includes('page_title'),
                showPageDescriptionLoader: selectedFields.includes('page_description'),
                showCategoryDescriptionLoader: selectedFields.includes('category_description'),
                success: function (response) {
                    if (response.success) {
                        self.updateTextFields(response, selectedFields);

                        if (editData.opened()) {
                            signalRModel.showMessage(true);
                            self.showMessage(true);
                        }
                        signalRModel.generating(false);
                        
                        signalRModel.editTitleStatusText('');
                        signalRModel.editTitleStatusText.valueHasMutated();
                        /** remove identifier from currently generating record identifiers if done generating */
                        var currentRecordIdentifiers = currentlyGeneratingModel.recordIdentifiers();
                        var identifier = self.entityType + '_' +  productId + '_' + storeId;
                        if (currentRecordIdentifiers.includes(identifier)) {
                            currentRecordIdentifiers.splice(currentRecordIdentifiers.indexOf(identifier), 1);
                            currentlyGeneratingModel.recordIdentifiers(currentRecordIdentifiers);
                        }


                        if (typeof response.feedback !== 'undefined') {
                            feedbackData.feedbacks(response.feedback);
                        }

                        if (typeof response.reviewed !== 'undefined') {
                            markReviewed.reviewed(response.reviewed);
                        }
                    } else {
                        console.log(response.message);
                    }

                    if (keywordAnalysis.autoGenerateOrRewriteChecked() &&
                        keywordAnalysis.optimizingId() === self.productId() &&
                        keywordAnalysis.optimizingStoreId() === self.storeId()
                    ) {
                        if ($('.wtai-edit-modal.wtai-edit-main').hasClass('_show')) {
                            keywords.showProgress(false);
                            self.getOptimization();
                        }
                        keywordAnalysis.autoGenerateOrRewriteChecked(false);
                    }
                }
            }).always(function () {
                self.isFetchingGeneratedData = false;
                signalRModel.toGetGenerated(false);
            });
        },

        /**
         * Repopulate text fields.
         */
        repopulateTextFields: function () {
            var defaultSelectedFields = signalRModel.selectedFields();
            var url = self.getGeneratedUrl,
                statuses = textfields.statuses(),
                productId = self.productId(),
                storeId = self.storeId(),
                
                selectedFields = [
                    'page_title',
                    'page_description',
                    'category_description'
                ];

            if (self.gettingData['repopulateTextFieldsCategory'] !== undefined ||
                self.gettingData['repopulateTextFieldsCategory']) {
                self.gettingData['repopulateTextFieldsCategory'].abort();
            }

            self.gettingData['repopulateTextFieldsCategory'] = $.ajax({
                url: url,
                type: 'POST',
                data: {
                    category_id: productId,
                    store_id: storeId
                },
                dataType: 'json',
                showWriteTextAILoader: true,
                showPageTitleLoader: selectedFields.includes('page_title'),
                showPageDescriptionLoader: selectedFields.includes('page_description'),
                showCategoryDescriptionLoader: selectedFields.includes('category_description'),
                success: function (response) {
                    if (response.success) {
                        self.updateTextFields(response, selectedFields);
                        signalRModel.generating(false);
                        
                        /** remove identifier from currently generating record identifiers if failed */
                        var currentRecordIdentifiers = currentlyGeneratingModel.recordIdentifiers();
                        var identifier = self.entityType + '_' + productId + '_' + storeId;
                        if (currentRecordIdentifiers.includes(identifier)) {
                            currentRecordIdentifiers.splice(currentRecordIdentifiers.indexOf(identifier), 1);
                            currentlyGeneratingModel.recordIdentifiers(currentRecordIdentifiers);
                        }

                        if (typeof response.feedback !== 'undefined') {
                            feedbackData.feedbacks(response.feedback);
                        }
                    } else {
                        console.log(response.message);
                    }
                },
                error: function (xhr, status, error) {
                    if (status !== 'abort') {
                        console.log(error);
                    }
                }
            });
        },

        /**
         * Update text fields.
         *
         * @param {Object} response
         * @param {Array} selectedFields
         *
         * @returns {void}
         */
        updateTextFields: function (response, selectedFields) {
            var statuses = textfields.statuses();
            selectedFields.forEach((field) => {
                switch (field) {
                    case 'page_title':
                        if (response.generated_texts.page_title !== undefined) {
                            textfields.pageTitle(response.generated_texts.page_title);
                            textfields.originalPageTitle(response.generated_texts.page_title);
                            statuses['pageTitleGenerateStatus'] = true;
                            statuses['pageTitleTransferStatus'] = false;
                            $('#wtaiPageTitleMask').removeClass('wtai-generated');
                            $('#wtaiPageTitleMask').addClass('wtai-generated');
                        }
                        break;
                    case 'page_description':
                        if (response.generated_texts.page_description !== undefined) {
                            textfields.pageDescription(response.generated_texts.page_description);
                            textfields.originalPageDescription(response.generated_texts.page_description);
                            statuses['pageDescriptionGenerateStatus'] = true;
                            statuses['pageDescriptionTransferStatus'] = false;
                            $('#wtaiPageDescMask').removeClass('wtai-generated');
                            $('#wtaiPageDescMask').addClass('wtai-generated');
                        }
                        break;
                    case 'category_description':
                        if (response.generated_texts.category_description !== undefined) {
                            textfields.categoryDescription(response.generated_texts.category_description);
                            textfields.originalCategoryDescription(response.generated_texts.category_description);
                            statuses['categoryDescriptionGenerateStatus'] = true;
                            statuses['categoryDescriptionTransferStatus'] = false;
                            $('#wtaiCategoryDescMask + .mce-tinymce').removeClass('wtai-generated');
                            $('#wtaiCategoryDescMask + .mce-tinymce').addClass('wtai-generated');
                            $('#wtaiCategoryDescMask + .tox-tinymce').removeClass('wtai-generated');
                            $('#wtaiCategoryDescMask + .tox-tinymce').addClass('wtai-generated');
                            $('#wtaiCategoryDescMask + .tox-hugerte').removeClass('wtai-generated');
                            $('#wtaiCategoryDescMask + .tox-hugerte').addClass('wtai-generated');
                        }
                        break;
                }
                textfields.statuses(statuses);
            });
        },

        /**
         * Get optimization.
         *
         * @returns {void}
         */
        getOptimization: function (openModal = false) {
            $.ajax({
                url: self.getOptimizationUrl,
                type: 'POST',
                data: {
                    store_id : self.storeId(),
                    record_id: self.productId(),
                    entity_type: self.entityType
                },
                dataType: 'json',
                showLoader: false,
                success: function (response) {
                    keywordAnalysis.optimizing(false);
                    keywordAnalysis.optimized(true);
                    keywordAnalysis.optimizationData(response.api_response);
                    keywordAnalysis.snapshotData(response.api_response.snapshots || {});

                    if (keywordAnalysis.refreshingData()) {
                        var isRefreshOngoing = response.api_response.refreshStatus === 'InProgress';
                        keywordAnalysis.refreshingData(isRefreshOngoing);
                    }
                    
                    if (openModal && keywordAnalysis.optimizationData() && keywordAnalysis.optimizationData().status === 'Completed') {
                        $('.wtai-keyword-analysis').modal('openModal');
                    }
                }
            }).always(function () {
                keywords.showProgress(false);
            });
        },

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

            reloadGrid.reloadUIComponent(
                'wtai_categories_grid_listing.wtai_categories_grid_listing_data_source',
                showLoader,
                debounce
            );
        }
    });
});
