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

define([
    'jquery',
    'underscore',
    'mage/translate',
    'WriteTextAI_WriteTextAI/js/model/edit/keywords/keyword-analysis',
    'WriteTextAI_WriteTextAI/js/model/edit/keywords/error-messages',
    'Magento_Ui/js/modal/confirm',
    'WriteTextAI_WriteTextAI/js/edit/keywords/keyword-pipelines/base'
], function (
    $,
    _,
    $t,
    keywordAnalysis,
    errorMessagesModel,
    confirm,
    base
) {
    'use strict';

    /**
     * Count not available keywords above the current level
     *
     * @param {*} self
     * @param {*} originalPipeline
     *
     * @returns {void}
     */
    function countNotAvailableKeywords(self, originalPipeline)
    {
        var countNotAvailable = 0;
        var countMissing = 0;
        var totalDifficultiesAfterCurr = 0;

        var currentLevelIndex = self.pipelineDifficulties.indexOf(originalPipeline.currentLevel);
        /** count difficulties above the current level including the current level */
        var totalDifficultiesAboveInclCurr = self.pipelineDifficulties.slice(0, currentLevelIndex + 1).length;

        /** count difficulties below the current level that are not set */
        var countMissingAfter = self.pipelineDifficulties
                .slice(currentLevelIndex + 1)
                .filter(difficulty => !originalPipeline.keywords.hasOwnProperty(difficulty))
                .length;
        
        totalDifficultiesAfterCurr = self.pipelineDifficulties.slice(currentLevelIndex + 1).length - countMissingAfter;
        /** Total keywords - keywords that are not editable. Check if it's less or equal to 1 */
        return Object.keys(self.editKeywordPipeline().keywords).length - totalDifficultiesAfterCurr;
    }

    /**
     * Save edit keyword pipeline
     *
     * @param {Object} self
     *
     * @returns {void}
     */
    function saveEditPipeline(self)
    {
        if (Object.keys(self.editKeywordPipeline().keywords).length > 0) {
            saveEditPipelineRequest(self);
        } else {
            confirm({
                content: $t('Are you sure you want to delete this pipeline?'),
                modalClass: 'wtai-modal wtai-confirm-modal wtai-delete-pipeline-modal',
                overlayClass: 'modals-overlay wtai-delete-pipeline-overlay',
                actions: {
                    confirm: function () {
                        self.deletePipelineRequest(self.editKeywordPipeline().id, true);
                    },
                    cancel: function () {
                        $('.modals-overlay.wtai-delete-pipeline-overlay').remove();
                    },
                    always: function () {
                        $('.modals-overlay.wtai-delete-pipeline-overlay').remove();
                    }
                },
                buttons: [{
                    text: $t('Cancel'),
                    class: 'action-secondary action-dismiss',
                    click: function (event) {
                        $('.wtai-delete-pipeline-overlay').remove();
                        this.closeModal(event);
                    }
                }, {
                    text: $t('Delete'),
                    class: 'action-primary action-accept',
                    click: function (event) {
                        $('.wtai-delete-pipeline-overlay').remove();
                        this.closeModal(event, true);
                    }
                }],
                closed: function () {
                    $('.wtai-delete-pipeline-overlay').remove();
                }
            });
        }
    }

    /**
     * Save edit pipeline request
     *
     * @param {Object} self
     *
     * @returns {void}
     */
    function saveEditPipelineRequest(self)
    {
        var isClustered = self.editKeywordPipeline().autogenerated;
        var pipelineRequest = self.editKeywordPipeline();
        pipelineRequest.keywords = Object.fromEntries(
            Object.entries(pipelineRequest.keywords).map(([difficulty, keywordData]) => {
                const { serpItems, ...rest } = keywordData;
                return [difficulty, rest];
            })
        );
        $.ajax({
            url: self.saveEditPipelineUrl,
            type: 'POST',
            data: {
                'store_id': self.storeId(),
                'record_id': self.recordId(),
                'entity_type': self.entityType,
                'pipeline': pipelineRequest
            },
            dataType: 'json',
            showWriteTextAILoader: true,
            showEditPipelineLoader: true,
            success: function (response) {
                if (response.success) {
                    /** revert edit back to its position before update */
                    var editPipelineContents = $(self.selectors.editPipelineContainer);
                    if (!editPipelineContents.parent().is(self.selectors.editPipelineWrapper)) {
                        editPipelineContents.appendTo($(self.selectors.editPipelineWrapper));
                    }

                    var updatedPipeline = response.api_response.pipeline;
                    var optimizationData = keywordAnalysis.optimizationData();
                    var pipelines = optimizationData.pipelines;
                    var index = pipelines.findIndex(pipeline => pipeline.id === updatedPipeline.id);
                    if (index === -1) {
                        pipelines.push(updatedPipeline);
                    } else {
                        if (isClustered) {
                            pipelines.splice(index, 1);
                            pipelines.push(updatedPipeline);
                        } else {
                            pipelines[index] = updatedPipeline;
                        }
                    }
                    if (typeof response.api_response.availableClustersToRestore !== 'undefined') {
                        optimizationData.availableClustersToRestore = response.api_response.availableClustersToRestore;
                    }
                    optimizationData.pipelines = pipelines;
                    keywordAnalysis.optimizationData(optimizationData);
                    self.closeEditPipeline();
                    self.getOptimization();
                } else {
                    if (typeof response.error !== 'undefined') {
                        errorMessagesModel.messages.push(response.error);
                    }
                }
            }
        });
    }

    /**
     * Update keyword on edit
     *
     * @param {Object} self
     * @param {Object} data
     * @param {Object} event
     * @param {string} difficulty
     *
     * @returns {boolean}
     */
    function updateKeyword(self, data, event, difficulty)
    {
        var keyword = event.currentTarget.value.toLowerCase();

        /** Prevent typing if adding this character would exceed max length */
        if (keyword.length > self.rules.maxKeywordLength) {
            /** Revert the input to the previous valid state */
            event.currentTarget.value = keyword.slice(0, self.rules.maxKeywordLength);
            keyword = event.currentTarget.value;
            /** Prevent the default behavior to stop the character from being added */
            event.preventDefault();
            return false;
        }

        event.target.value = keyword;
        var activeInputKeyword = self.newActiveKeywordsInput();
        activeInputKeyword[difficulty] = keyword;
        self.newActiveKeywordsInput(activeInputKeyword);

        validateInvalidCharacters(self, data, event);

        self.newKeywordLength(keyword.length);

        if (event.keyCode === 13) {
            let currentKeyword = self.getKeywordToEdit(difficulty);

            if (keyword !== currentKeyword) {
                base.processUpdateKeyword(self, data, event, difficulty);
            } else {
                $(event.currentTarget).closest('.wtai-edit-keyword-item').removeClass('wtai-input-active');
                self.newKeywordLength(0);
                self.inputActive(false);
            }
        }

        return true;
    }

    /**
     * Validate invalid characters
     *
     * @param {Object} self
     * @param {Object} data
     * @param {Object} event
     *
     * @returns {void}
     */
    function validateInvalidCharacters(self, data, event)
    {
        var errorElem = $(event.currentTarget)
            .closest('.wtai-edit-keyword-item')
            .find('.wtai-input-keywords-error');
        var invalidChars = /[,.!@%^*()={};~`<>?\\|]/g;
        var keyword = event.currentTarget.value.toLowerCase().trim();

        if (event.keyCode !== 17) {
            const isCtrlV = event.keyCode === 86 && (event.ctrlKey || event.metaKey); /** Check for 'Ctrl + V' or 'Cmd + V' */
            const isVKey = event.key === 'v' || event.key === 'V';
            if (isCtrlV) {
                /** Do nothing if paste using 'Ctrl + V' */
            } else if (isVKey) {
                /** If 'v' is typed, remove the error and clear text */
                errorElem.removeClass('wtai-active').text('');
            } else {
                /** For any other key, clear the error */
                errorElem.removeClass('wtai-active').text('');
            }
        }

        var invalidCharList = [];
        var matchedChars = keyword.match(invalidChars) || [];
        if (matchedChars.length > 0) {
            matchedChars.forEach(function (char) {
                if (!invalidCharList.includes(char)) {
                    invalidCharList.push(char);
                }
            });
            if (invalidCharList.length === 1) {
                errorElem.addClass('wtai-active')
                    .text($t('"%s" is an invalid character.').replace('%s', invalidCharList[0]));
            } else {
                errorElem.addClass('wtai-active')
                    .text($t('"%s" are invalid characters.').replace('%s', invalidCharList.join(' ')));
            }
            event.currentTarget.value = event.currentTarget.value.replace(invalidChars, '');
        }
    }

    /**
     * Arrange edit pipeline
     *
     * @param {Object} self
     * @param {string} arrangeBy
     * @param {boolean} excludeCurrent
     *
     * @returns {void}
     */
    function arrangePipeline(self, arrangeBy, excludeCurrent = false)
    {
        var editKeywordPipeline = self.editKeywordPipeline();
        var keywords = editKeywordPipeline.keywords;
        var arrangementRef = ['Low', 'LowMid', 'Mid', 'MidHigh', 'High'];
        var levelMapping = {
            'High': 4,
            'MidHigh': 3,
            'Mid': 2,
            'LowMid': 1,
            'Low': 0
        };
        var currentLevel = editKeywordPipeline.currentLevel || 'Low';
        var currentLevelValue = levelMapping[currentLevel];
        var sortedKeywords = [];
        
        var fixedKeywords = Object.entries(keywords)
            .filter(([key, value]) => {
                return excludeCurrent
                        ? levelMapping[key] <= currentLevelValue
                        : levelMapping[key] < currentLevelValue
            })
            .sort((a, b) => levelMapping[b[0]] - levelMapping[a[0]]);
        /** get the number of keywords to be fixed including blank/skipped after the current level */
        var startIndex = self.pipelineDifficulties.indexOf(currentLevel);
        var fixedKeywordsCount = fixedKeywords.length;
        if (startIndex !== -1) {
            if (excludeCurrent) {
                fixedKeywordsCount = self.pipelineDifficulties.slice(startIndex).length;
            } else {
                fixedKeywordsCount = self.pipelineDifficulties.slice(startIndex + 1).length;
            }
        }
        
        const rangeOrder = ["Low", "LowMid", "Mid", "MidHigh", "High"];
        if (arrangeBy === 'difficulty') {
            sortedKeywords = Object.entries(keywords)
                .filter(([key, value]) => {
                    return excludeCurrent
                        ? levelMapping[key] > currentLevelValue
                        : levelMapping[key] >= currentLevelValue;
                })
                .sort((a, b) => {
                    var aRangeDifficulty = getDifficulty(self, a[1].difficulty);
                    var bRangeDifficulty = getDifficulty(self, b[1].difficulty);
                    var aVolume = a[1].searchVolume || 0;
                    var bVolume = b[1].searchVolume || 0;
                    
                    /* Sort by difficulty (descending) */
                    if (bRangeDifficulty !== aRangeDifficulty) {
                        return rangeOrder.indexOf(bRangeDifficulty) - rangeOrder.indexOf(aRangeDifficulty);
                    }

                    /* Sort by search volume (descending) */
                    if (bVolume !== aVolume) {
                        return bVolume - aVolume;
                    }
                    
                    /* Sort by keyword length (ascending) */
                    if (a[1].keyword.length !== b[1].keyword.length) {
                        return a[1].keyword.length - b[1].keyword.length;
                    }

                    /* Sort alphabetically (ascending) */
                    return a[1].keyword.localeCompare(b[1].keyword);
                });
        }

        if (arrangeBy === 'volume') {
            sortedKeywords = Object.entries(keywords)
                .filter(([key, value]) => {
                    return excludeCurrent
                        ? levelMapping[key] > currentLevelValue
                        : levelMapping[key] >= currentLevelValue;
                })
                .sort((a, b) => {
                    var aRangeDifficulty = getDifficulty(self, a[1].difficulty);
                    var bRangeDifficulty = getDifficulty(self, b[1].difficulty);
                    var aVolume = a[1].searchVolume || 0;
                    var bVolume = b[1].searchVolume || 0;

                    /* Sort by search volume (descending) */
                    if (bVolume !== aVolume) {
                        return bVolume - aVolume;
                    }

                    /* Sort by difficulty (descending) */
                    if (bRangeDifficulty !== aRangeDifficulty) {
                        return rangeOrder.indexOf(bRangeDifficulty) - rangeOrder.indexOf(aRangeDifficulty);
                    }

                    /* Sort by keyword length (ascending) */
                    if (a[1].keyword.length !== b[1].keyword.length) {
                        return a[1].keyword.length - b[1].keyword.length;
                    }
                    
                    /* Sort alphabetically (ascending) */
                    return a[1].keyword.localeCompare(b[1].keyword);
                });
        }
        
        var updatedKeys = {};
        fixedKeywords.forEach((keyword, index) => {
            updatedKeys[keyword[0]] = keyword[1];
        });
        sortedKeywords.reverse();
        var startIndex = excludeCurrent ? currentLevelValue + 1 : currentLevelValue;
        sortedKeywords.forEach((keyword, index) => {
            updatedKeys[arrangementRef[startIndex]] = keyword[1];
            startIndex++;
        });
        editKeywordPipeline.keywords = updatedKeys;
        self.editKeywordPipeline([]);
        self.editKeywordPipeline(editKeywordPipeline);
        self.closeArrangeDropdown();
    }

    /**
     * Is current and uneditable
     *
     * @param {Object} self
     * @param {string} difficulty
     * @param {string} currentLevel
     * @param {string} pipelineId
     *
     * @returns {boolean}
     */
    function isCurrentAndUneditable(self, difficulty, currentLevel, pipelineId)
    {
        if (!self.isCurrent(difficulty, currentLevel)) {
            return false;
        }
        var keywords = keywordAnalysis.optimizationData().pipelines;
        var pipeline = keywords.find(pipeline => pipeline.id === pipelineId);
        if (pipeline === undefined) {
            return false;
        }
        var keywords = pipeline.keywords;
        switch (currentLevel) {
            case 'High':
                return true;
            case 'MidHigh':
                let isHighNone = keywords['High'] === undefined;
                return isHighNone;
            case 'Mid':
                let isHighNone1 = keywords['High'] === undefined;
                let isMidHighNone1 = keywords['MidHigh'] === undefined;
                return isHighNone1 && isMidHighNone1;
            case 'LowMid':
                let isHighNone2 = keywords['High'] === undefined;
                let isMidHighNone2 = keywords['MidHigh'] === undefined;
                let isMidNone2 = keywords['Mid'] === undefined;
                return isHighNone2 && isMidHighNone2 && isMidNone2;
            case 'Low':
                let isHighNone3 = keywords['High'] === undefined;
                let isMidHighNone3 = keywords['MidHigh'] === undefined;
                let isMidNone3 = keywords['Mid'] === undefined;
                let isLowMidNone3 = keywords['LowMid'] === undefined;
                return isHighNone3 && isMidHighNone3 && isMidNone3 && isLowMidNone3;
        }
    }

    /**
     * Close input keyword
     *
     * @param {Object} self
     * @param {*} data
     * @param {*} event
     * @param {string} difficulty
     *
     * @returns {void}
     */
    function closeInputKeyword(self, data, event, difficulty)
    {
        /**
         * this is for manually added keyword
         * if the keyword is manually added and the input is empty
         * remove the keyword from the pipeline to revert back to placeholder
         */
        var editKeywordPipeline = self.editKeywordPipeline();
        let keywordReplaced = editKeywordPipeline.keywords[difficulty];
        if (keywordReplaced.manually_added) {
            
            /**
             * this is for manually added keyword
             * if the keyword is manually added and the input is empty
             * remove the keyword from the pipeline to revert back to placeholder
             */
            self.removeKeyword(difficulty);
        }
        event.currentTarget.value = keywordReplaced.keyword || '';
        $(event.currentTarget).closest('.wtai-edit-keyword-item').removeClass('wtai-input-active');
        self.newKeywordLength(0);
        self.inputActive(false);
    }

    /**
     * Add keyword item manually
     *
     * @param {Object} self
     * @param {*} data
     * @param {*} event
     * @param {string} difficulty
     *
     * @returns {void}
     */
    function addKeywordItemManually(self, data, event, difficulty)
    {
        if (self.inputActive()) {
            return;
        }
        
        var keywordEditPipeline = self.editKeywordPipeline();
        keywordEditPipeline.keywords[difficulty] = {
            keyword: '',
            manually_added: true
        };
        self.editKeywordPipeline(keywordEditPipeline);
        
        /** find previous element with class wtai-edit-keyword-item */
        var previousElement = $(event.currentTarget).prev('.wtai-edit-keyword-item');
        base.editkeywordItemInitialProcess(self,previousElement, difficulty);
    }

    /**
     * Get difficulty in text.
     *
     * @param {Object} self
     * @param {int} difficulty
     * @param {boolean} getLabel
     *
     * @returns {string}
     */
    function getDifficulty(self, difficulty, getLabel = false)
    {
        if (!self.ranges()) {
            return '';
        }
        
        var range = self.ranges().find(function (range) {
            return difficulty >= range.min && difficulty <= range.max;
        });

        if (!range) {
            return difficulty;
        }
        if (getLabel) {
            var label = '';
            switch (range.type) {
                case 'MidHigh':
                    label = $t('MID HIGH');
                    break;
                case 'LowMid':
                    label = $t('LOW MID');
                    break
                default:
                    label = range.type;
                    break;
            }
            return label.toUpperCase();
        }

        return range.type;
    }

    return {
        countNotAvailableKeywords: countNotAvailableKeywords,
        saveEditPipeline: saveEditPipeline,
        updateKeyword: updateKeyword,
        arrangePipeline: arrangePipeline,
        validateInvalidCharacters: validateInvalidCharacters,
        isCurrentAndUneditable: isCurrentAndUneditable,
        closeInputKeyword: closeInputKeyword,
        addKeywordItemManually: addKeywordItemManually,
        getDifficulty: getDifficulty
    };
});
