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

define([
    'jquery',
    'ko',
    'uiComponent',
    'WriteTextAI_WriteTextAI/js/utils/edit/textfields',
    'WriteTextAI_WriteTextAI/js/model/grid/prompt',
    'mage/translate',
    'mage/mage',
    'mage/tabs',
    'mage/validation',
    'jquery/ui'
], function (
    $,
    ko,
    Component,
    textfieldsUtils,
    prompt,
    $t
) {
    'use strict';

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

    return Component.extend({
        defaults: {
            tooltipSelector: '.wtai-bulk-generate-form .admin__field-tooltip-action',
            selectedFields: [],
            fields: [],
            descMax: '',
            descMin: '',
            excMax: '',
            excMin: '',
            disallowedCombinations: [],
            tonesSelected: [],
            stylesSelected: '',
            audienceSelected: [],
            wordsAllowed: false,
            specialInstructions: '',
            attributesSelected: [],
            isLoading: true,
            isRestoreGlobalLoading: false,
            customTone: '',
            customStyle: '',
            tones: [],
            styles: [],
            audiences: [],
            attributes: [],
            rules: {},
            attrAllowed: false,
            toneStyleAllowed: false,
            wordsAllowed: false,
            currentTemplateSettings: {},
            getTemplateSettings: true,
            
        },

        isSpinning: false,
        typingTimer: null,
        isReopening: false,
        isRestoring: false,
        isTonesSelectedChanged: false,
        savingPreferences: {},
        
        /** @inheritdoc */
        initialize: function () {
            this._super();

            $('.wtai-bulk-generate-form-container > *').applyBindings(this);

            self = this;

            self.attributesSelected.subscribe(function (attributesSelected) {
                if (!self.isReopening) {
                    self.savePreferences('attributes_selected', attributesSelected);
                    self.savePreferences('thumbnail', attributesSelected.includes('thumbnail') ? true : false, 'product', null);
                    self.savePreferences('wtai_product_research_data', attributesSelected.includes('wtai_product_research_data') ? true : false, '', null);
                }
            });
            self.selectedFields.subscribe(function (selectedFields) {
                if (!self.isReopening) {
                    self.savePreferences('generate', selectedFields);
                }
            });

            self.tonesSelected.subscribe(
                function (changes) {
                    if (!self.isTonesSelectedChanged) {
                        self.isTonesSelectedChanged = true;

                        _.each(changes, function (change) {
                            if (
                                change.status === 'added' &&
                                change.value === 'custom'
                            ) {
                                self.tonesSelected.remove(self.isNotCustom);
                            }
                            if (
                                change.status === 'added' &&
                                change.value !== 'custom'
                            ) {
                                self.tonesSelected.remove('custom');
                                self.customTone('');
                            }
                        });

                        self.isTonesSelectedChanged = false;
                    }
                    
                    if (!self.isReopening) {
                        self.saveTonesAndStyles();
                    }
                },
                this,
                'arrayChange'
            );

            self.customTone.subscribe(function (customTone) {
                if (customTone) {
                    self.tonesSelected(['custom']);
                }
            });

            self.stylesSelected.subscribe(function (stylesSelected) {
                if (stylesSelected !== 'custom') {
                    self.customStyle('');
                }
                if (!self.isReopening) {
                    self.saveTonesAndStyles();
                }
            });

            self.customStyle.subscribe(function (customStyle) {
                if (customStyle) {
                    self.stylesSelected('custom');
                }
            });

            self.audienceSelected.subscribe(function (audienceSelected) {
                if (!self.isReopening) {
                    self.savePreferences('audiences', audienceSelected);
                }
            });

            self.descMin.subscribe(function (descMin) {
                if (!self.isSpinning) {
                    self.savePreferences('desc_min', descMin);
                }
            });

            self.descMax.subscribe(function (descMax) {
                if (!self.isSpinning) {
                    self.savePreferences('desc_max', descMax);
                }
            });

            self.excMax.subscribe(function (excMax) {
                if (!self.isSpinning) {
                    self.savePreferences('exc_max', excMax);
                }
            });

            self.excMin.subscribe(function (excMin) {
                if (!self.isSpinning) {
                    self.savePreferences('exc_min', excMin);
                }
            });

            prompt.templateSettings.subscribe(function (templateSettings) {
                self.currentTemplateSettings(templateSettings);
            }, this);

            prompt.fieldsTemplateSettings.subscribe(function (fieldsTemplateSettings) {
                self.fields(fieldsTemplateSettings);
            }, this);

            self.getSettings();
        },

        /** @inheritdoc */
        initObservable: function () {
            this._super().observe([
                'fields',
                'selectedFields',
                'descMax',
                'descMin',
                'excMax',
                'excMin',
                'disallowedCombinations',
                'tonesSelected',
                'stylesSelected',
                'audienceSelected',
                'specialInstructions',
                'attributesSelected',
                'isLoading',
                'isRestoreGlobalLoading',
                'customTone',
                'customStyle',
                'tones',
                'styles',
                'audiences',
                'attributes',
                'rules',
                'attrAllowed',
                'toneStyleAllowed',
                'wordsAllowed',
                'currentTemplateSettings',
                'getTemplateSettings'
            ]);
            
            this.allFieldsSelected = ko.computed({
                read: function () {
                    const selectedFields = this.selectedFields() || [];
                    const allFields = [...this.fields().map(field => field.value), 'alt_text'] || [];
                    return selectedFields.length === allFields.length;
                }.bind(this),
                write: function (value) {
                    const allFields = [...this.fields().map(field => field.value), 'alt_text'] || [];
                    const selectedFields = value ? allFields : [];
                    this.selectedFields(selectedFields);
                }.bind(this)
            }, this);
            
            this.allTonesSelected = ko.computed({
                read: function () {
                    const tones = this.tonesSelected() || [];
                    const allTones = [...this.tones().map(tone => tone.id)] || [];
            
                    /* If style is 'Internet-speak', remove 'Formal' from the comparison */
                    let tonesToCompare = allTones;
                    if (this.stylesSelected() === 'Internet-speak') {
                        tonesToCompare = allTones.filter(tone => tone !== 'Formal');
                    }
            
                    return tones.length === tonesToCompare.length &&
                           tonesToCompare.every(tone => tones.includes(tone));
                }.bind(this),
            
                write: function (value) {
                    let tones = value ? [...this.tones().map(tone => tone.id)] : [];
            
                    if (value && this.stylesSelected() === 'Internet-speak') {
                        tones = tones.filter(tone => tone !== 'Formal');
                    }
            
                    this.tonesSelected(tones);
                }.bind(this)
            }, this);
            
            this.allAudienceSelected = ko.computed({
                read: function () {
                    const audience = this.audienceSelected() || [];
                    const allAudience = [...this.audiences().map(audience => audience.id)] || [];
                    return audience.length === allAudience.length;
                }.bind(this),
                write: function (value) {
                    const allAudience = [...this.audiences().map(audience => audience.id)]|| [];
                    const audience = value ? allAudience : [];
                    this.audienceSelected(audience);
                }.bind(this)
            }, this);
            
            this.allAttributesSelected = ko.computed({
                read: function () {
                    const attributes = this.attributesSelected() || [];
                    const allAttributes = [...this.attributes().map(attr => attr.value)] || [];
                    return attributes.length === allAttributes.length;
                }.bind(this),
                write: function (value) {
                    const allAttributes = [...this.attributes().map(attr => attr.value)]|| [];
                    const attributes = value ? allAttributes : [];
                    this.attributesSelected(attributes);
                }.bind(this)
            }, this);

            this.getOtherProductDetailsLength = ko.computed(function () {
                return this.specialInstructions().length + '/' + this.rules().maxOtherDetailsLength + ' ' +  $t('Char');
            }, this);

            return this;
        },

        /**
         * Get settings
         */
        getSettings: function () {
            self.showLoader();
            var getTemplateSettings = false;
            /*if (Object.keys(prompt.templateSettings()).length > 0) {
                getTemplateSettings = false;
            }*/
            $.ajax({
                url: self.getSettingsUrl,
                type: 'GET',
                data: {
                    getTemplateSettings: getTemplateSettings
                },
                dataType: 'json',
                showLoader: false,
                success: function (response) {
                    if (!response.success) {
                        console.log(response);
                    } else {
                        console.log(response);

                        self.isReopening = true;
                        self.tones(response.data.tones);
                        self.styles(response.data.styles);
                        self.audiences(response.data.audiences);
                        self.attributes(response.data.attributes);
                        self.attributesSelected(response.data.attributes_selected);
                        self.customTone(response.data.custom_tone);
                        self.customStyle(response.data.custom_style);
                        self.tonesSelected(response.data.tones_selected);
                        self.stylesSelected(response.data.styles_selected);
                        self.audienceSelected(response.data.audience_selected);
                        self.descMin(response.data.desc_min);
                        self.descMax(response.data.desc_max);
                        self.excMin(response.data.exc_min);
                        self.excMax(response.data.exc_max);
                        self.specialInstructions(response.data.special_instructions);
                        self.selectedFields(response.data.selected_fields);
                        self.rules(response.data.rules);
                        self.disallowedCombinations(response.data.disallowedCombinations);
                        self.attrAllowed(response.data.attrAllowed);
                        self.toneStyleAllowed(response.data.toneStyleAllowed);
                        self.wordsAllowed(response.data.wordsAllowed);
                        if (response.data.currentTemplateSettings) {
                            prompt.templateSettings(response.data.currentTemplateSettings);
                            prompt.fieldsTemplateSettings(response.data.fields);
                        } else {
                            self.currentTemplateSettings(prompt.templateSettings());
                            self.fields(prompt.fieldsTemplateSettings());
                        }
                        self.isReopening = false;

                        self.initBulkTabs();
                        self.initTooltip();
                        self.initValidations();
                        self.initTriggerUpdate();
                        self.initSpinner();
                    }
                }
            }).always(function () {
                self.removeLoader();
            });
        },

        /**
         * Initialize update trigger
         */
        initTriggerUpdate: function () {
            $('#wtaiPrompt').trigger('contentUpdated');
        },
        
        /**
         * Show loader
         */
        showLoader: function () {
            self.isLoading(true);
        },

        /**
         * Show restore global loader
         */
        showRestoreGlobalLoader: function () {
            self.isRestoreGlobalLoading(true);
        },

        /**
         * Remove restore global loader
         */
        removeRestoreGlobalLoader: function () {
            setTimeout(function () {
                self.isRestoreGlobalLoading(false);
            }, 100);
        },

        /**
         * Remove loader
         */
        removeLoader: function () {
            setTimeout(function () {
                self.isLoading(false);
            }, 100);
        },

        /**
         * Restore user preferences
         */
        restore: function () {
            self.abortPendingRequests();
            self.showRestoreGlobalLoader();
            $.ajax({
                url: self.restoreUrl,
                type: 'GET',
                dataType: 'json',
                showLoader: false,
                success: function (response) {
                    if (!response.success) {
                        console.log(response);
                    } else {
                        console.log(response);
                        self.isRestoring = true;
                        self.descMin(response.data.desc_min);
                        self.descMax(response.data.desc_max);
                        self.excMin(response.data.exc_min);
                        self.excMax(response.data.exc_max);
                        self.selectedFields(response.data.fields);
                        self.tonesSelected(response.data.tones);
                        self.stylesSelected(response.data.styles);
                        self.customTone(response.data.custom_tone);
                        self.customStyle(response.data.custom_style);
                        self.audienceSelected(response.data.audience);
                        self.specialInstructions(response.data.special_instructions);
                        self.attributesSelected([]);
                        self.attributesSelected([...response.data.attributes]);
                        self.isRestoring = false;
                    }
                }
            }).always(function () {
                self.removeRestoreGlobalLoader();
            });
        },

        /**
         * Handle custom tone and style typing
         */
        handleCustomToneStyle: function () {
            clearTimeout(self.typingTimer);

            self.typingTimer = setTimeout(function () {
                self.saveTonesAndStyles();
            }, 2000);
        },

        /**
         * Save user preferences for tones and styles
         */
        saveTonesAndStyles: function () {
            var url = self.saveUrl,
                tones = self.tonesSelected(),
                customTone = self.customTone(),
                styles = self.stylesSelected(),
                customStyle = self.customStyle();
            
            if (tones.length <= 0) {
                tones = '';
            }

            if (self.isRestoring || self.isReopening) {
                return;
            }

            $.ajax({
                url: url,
                type: 'POST',
                data: {
                    tones: tones,
                    style: styles,
                    custom_tone: customTone,
                    custom_style: customStyle,
                    entity_type: '',
                    scope: 'edit'
                },
                dataType: 'json',
                showWriteTextAILoader: false,
                success: function (response) {
                    if (!response.success) {
                        console.log(response);
                    }
                }
            });
        },

        /**
         * Save user preferences
         *
         * @param {String} field
         * @param {String} selected
         * @param {String} entityType
         * @param {String} scope
         *
         * @return {void}
         */
        savePreferences: function (field, selected, entityType = 'product', scope = 'grid') {
            var value = selected;
            
            if (value === null || value.length <= 0) {
                value = '';
            }

            if (self.isRestoring || self.isReopening) {
                return;
            }

            /* Create a unique key for this request */
            var requestKey = field + '_' + entityType + '_' + scope;

            /* Abort previous request with the same key */
            if (self.savingPreferences[requestKey] !== null && self.savingPreferences[requestKey] !== undefined) {
                self.savingPreferences[requestKey].abort();
            }

            self.savingPreferences[requestKey] = $.ajax({
                url: self.saveUrl,
                type: 'POST',
                data: {
                    [field]: value,
                    entity_type: entityType,
                    scope: scope
                },
                dataType: 'json',
                showLoader: false,
                success: function (response) {
                    if (!response.success) {
                        console.log(response);
                    }
                    /* Clean up the reference after successful completion */
                    delete self.savingPreferences[requestKey];
                },
                error: function (xhr, status, error) {
                    if (status !== 'abort') {
                        console.log(error);
                    }
                    /* Clean up the reference after error (except abort) */
                    delete self.savingPreferences[requestKey];
                }
            });
        },


        /**
         * Save product research data selected.
         *
         * @param {Object} data
         * @param {Event} event
         *
         * @returns {void}
         */
        saveProductResearchDataSelected: function (data, event) {
            if (self.isRestoring || self.isReopening) {
                return;
            }
            $.ajax({
                url: self.saveUrl,
                type: 'POST',
                data: {
                    wtai_product_research_data: event.target.checked,
                    entity_type: '',
                },
                dataType: 'json',
                showLoader: false,
                success: function (response) {
                    if (!response.success) {
                        console.log(response);
                    }
                }
            });
        },

        /**
         * Validate number.
         *
         * @param {Object} data
         * @param {Object} event
         *
         * @return {Boolean}
         */
        ensureNumerical: function (data, event) {
            return textfieldsUtils.ensureNumerical(event.key);
        },

        /**
         * Handle input special instructions.
         */
        handleInputSpecialInstructions: function () {
            clearTimeout(self.typingTimer);

            self.typingTimer = setTimeout(function () {
                self.savePreferences('special_instructions', self.specialInstructions());
            }, 2000);

            return true;
        },

        /**
         * Initialize spinner
         */
        initSpinner: function () {
            setTimeout(
                function () {
                    $('.wtai-bulk-generate-popup .wtai-cluster-count').spinner({
                        numberFormat: 'n',
                        min: self.rules().minClusterCount,
                        max: self.rules().maxClusterCount,
                        spin: function (event, ui) {
                            self.isSpinning = true;
                            self.clusterCount(ui.value);
                            self.isSpinning = false;
                        },
                        change: function (event) {
                            self.clusterCount(event.target.value);
                        }
                    });
                    $('.wtai-bulk-generate-popup .wtai-input-number').spinner({
                        numberFormat: 'n',
                        min: self.rules().minOutputWords,
                        max: self.rules().maxOutputWords,
                        disabled: !self.wordsAllowed,
                        spin: function (event, ui) {
                            var value = ui.value,
                                name = $(event.target).attr('name');
                            
                            self.isSpinning = true;
                            switch (name) {
                                case 'description_min':
                                    self.descMin(value);
                                    break;
                                case 'description_max':
                                    self.descMax(value);
                                    break;
                                case 'excerpt_min':
                                    self.excMin(value);
                                    break;
                                case 'excerpt_max':
                                    self.excMax(value);
                                    break;
                            }
                            self.isSpinning = false;
                        },
                        change: function (event) {
                            var value = event.target.value,
                                name = $(event.target).attr('name');
                            
                            switch (name) {
                                case 'description_min':
                                    self.descMin(value);
                                    break;
                                case 'description_max':
                                    self.descMax(value);
                                    break;
                                case 'excerpt_min':
                                    self.excMin(value);
                                    break;
                                case 'excerpt_max':
                                    self.excMax(value);
                                    break;
                            }
                        }
                    });
                }.bind(this),
                100
            );
        },

        /**
         * Initialize tooltip
         */
        initTooltip: function () {
            $(self.tooltipSelector).dropdown({
                parent: '.admin__field-tooltip',
                autoclose: true
            });
        },

        /**
         * Initialize validations
         */
        initValidations: function () {
            $('#wtaiBulkGenerateForm').mage('validation', {
                rules: {
                    'fields[]': {
                        required: true
                    },
                    'tones[]': {
                        required: {
                            param: true
                        }
                    },
                    styles: {
                        required: {
                            param: true
                        }
                    },
                    'custom_tone': {
                        required: {
                            param: true,
                            depends: function () {
                                return self.tonesSelected().indexOf('custom') !== -1;
                            }
                        },
                        'validate-no-empty': {
                            depends: function () {
                                return self.tonesSelected().indexOf('custom') !== -1;
                            }
                        }
                    },
                    'custom_style': {
                        required: {
                            param: true,
                            depends: function () {
                                return self.stylesSelected() === 'custom';
                            }
                        },
                        'validate-no-empty': {
                            depends: function () {
                                return self.stylesSelected() === 'custom';
                            }
                        }
                    }
                },
                messages: {
                    'fields[]': {
                        required: $t('Please select at least one text type.')
                    },
                    'tones[]': {
                        required: $t('Please select at least one tone.')
                    },
                    styles: {
                        required: $t('Please select at least one style.')
                    },
                    'custom_tone': {
                        required: $t('Write your specific tone...'),
                        'validate-no-empty': $t('Write your specific tone...')
                    },
                    'custom_style': {
                        required: $t('Write your specific style...'),
                        'validate-no-empty': $t('Write your specific style...')
                    }
                },
                errorPlacement: function (error, element) {
                    $('.wtai-tabs-error-message').html(error);
                },
                invalidHandler: function (form, validator) {
                    if (!validator.numberOfInvalids()) {
                        return;
                    }
                    var lastErrorElement = $(validator.errorList[validator.errorList.length - 1].element);
                    var tabPane = lastErrorElement.closest('.item.content');
                    if (tabPane.length) {
                        if (tabPane.css('display') === 'none') {
                            var tabTrigger = tabPane.prev('.item.title').find('a.switch');
                            if (tabTrigger.length) {
                                tabTrigger.trigger('click');
                            }
                        }
                    }
                    setTimeout(function () {
                        validator.focusInvalid();
                    }, 50);
                }
            });
        },

        /**
         * Initialize bulk tabs
         */
        initBulkTabs: function () {
            setTimeout(function () {
                $('.wtai-tabs').tabs({
                    active: 0
                });
            }, 100);
        },

        /**
         * Check if combination is not allowed
         *
         * @param {String} id
         * @param {String} type
         *
         * @return {Boolean}
         */
        isCombinationDisallowed: function (id, type) {
            var result,
                isStyleChecked = false,
                isToneChecked = false,
                isAudienceChecked = false,
                combinationHasAudience = false;

            self = this;

            try {
                /* Check if combination is available */
                result = self
                    .disallowedCombinations()
                    .find(function (combination) {
                        return combination.combination.some(function (obj) {
                            return obj.type === type && obj.id === id;
                        });
                    });

                if (!(self.disallowedCombinations() && result)) {
                    return false;
                }

                switch (type) {
                    case 'Tone':
                        isStyleChecked = result.combination.some(
                            function (obj) {
                                return (
                                    obj.type === 'Style' &&
                                    self.stylesSelected() === obj.id
                                );
                            }
                        );
                        combinationHasAudience =
                            result.combination.some(function (obj) {
                                return obj.type === 'Audience';
                            });

                        if (combinationHasAudience) {
                            isAudienceChecked =
                                self
                                    .audienceSelected()
                                    .indexOf(combinationHasAudience.id) !==
                                -1;

                            return isStyleChecked && isAudienceChecked;
                        }
                        return isStyleChecked;

                    case 'Style':
                        isToneChecked = result.combination.some(
                            function (obj) {
                                return (
                                    obj.type === 'Tone' &&
                                    self.tonesSelected().indexOf(obj.id) !==
                                        -1
                                );
                            }
                        );
                        combinationHasAudience =
                            result.combination.some(function (obj) {
                                return obj.type === 'Audience';
                            });

                        if (combinationHasAudience) {
                            isAudienceChecked =
                                self
                                    .audienceSelected()
                                    .indexOf(combinationHasAudience.id) !==
                                -1;

                            return isToneChecked && isAudienceChecked;
                        }
                        return isToneChecked;

                    case 'Audience':
                        isToneChecked = result.combination.some(
                            function (obj) {
                                return (
                                    obj.type === 'Tone' &&
                                    self.tonesSelected().indexOf(obj.id) !==
                                        -1
                                );
                            }
                        );
                        isStyleChecked = result.combination.some(
                            function (obj) {
                                return (
                                    obj.type === 'Style' &&
                                    self.stylesSelected() === obj.id
                                );
                            }
                        );

                        return isStyleChecked && isToneChecked;

                    default:
                        return false;
                }
            } catch (e) {
                return false;
            }
        },

        /**
         * Get tooltip text based on disallowed combination
         *
         * @param {String} id
         * @param {String} type
         *
         * @return {String}
         */
        getToolTipCombinationDisallowed: function (id, type) {
            if (this.isCombinationDisallowed(id, type)) {
                return this.getTooltipCombinationDisallowedText(id, type);
            }
            return '';
        },

        /**
         * Get tooltip text based on disallowed combination
         *
         * @param {String} id
         * @param {String} type
         *
         * @return {String}
         */
        getTooltipCombinationDisallowedText: function (id, type) {
            var result,
                isStyleChecked = false,
                isToneChecked = false,
                textBase = '';

            self = this;

            try {
                /* Check if combination is available */
                result = self
                    .disallowedCombinations()
                    .find(function (combination) {
                        return combination.combination.some(function (obj) {
                            return obj.type === type && obj.id === id;
                        });
                    });

                if (!result) {
                    return '';
                }

                switch (type) {
                    case 'Tone':
                        isStyleChecked = result.combination.some(
                            function (obj) {
                                if (obj.type === 'Style' && self.stylesSelected() === obj.id) {
                                    textBase = self.processToolTip(obj.id, 'Style');
                                    return true;
                                }
                                return false;
                            }
                        );
                        return textBase;

                    case 'Style':
                        isToneChecked = result.combination.some(
                            function (obj) {
                                if ( obj.type === 'Tone' &&
                                    self.tonesSelected().indexOf(obj.id) !== -1) {
                                    textBase = self.processToolTip(obj.id, 'Tone');
                                    return true;
                                }
                                return false;
                            }
                        );
                        return textBase;

                    default:
                        return '';
                }
            } catch (e) {
                return '';
            }
        },

        /**
         * Process tooltip content
         *
         * @param {String} selector
         * @param {String} stringValue
         *
         * @return {String}
         */
        processToolTip: function (stringValue, type) {
            var textBase = '';
            switch (type) {
                case 'Tone':
                    textBase = $t("Unavailable when the '%1' tone is selected.");
                    break;
                case 'Style':
                    textBase = $t("Unavailable when the '%1' style is selected.");
                    break;
                default:
                    textBase = $t("Unavailable when the '%1' audience is selected.");
                    break;
            }
            return $t(textBase).replace('%1', stringValue);
        },

        /**
         * Validate min and max fields.
         *
         * @param {Object} data
         * @param {Object} event
         *
         * @return {void}
         */
        validateMinMax: function (data, event) {
            var target = event.target,
                value = Number(target.value),
                min = Number(target.min),
                max = Number(target.max),
                name = target.name;

            const mapping = {
                description_min: self.descMin,
                description_max: self.descMax,
                excerpt_min: self.excMin,
                excerpt_max: self.excMax,
                cluster_count: self.clusterCount
            };

            if (value < min || value > max) {
                const func = mapping[name],
                    val = value < min ? min : max;

                $(target).val(val);
                func(val);
            }
        },

        /**
         * Get custom tone length.
         *
         * @return {String}
         */
        getCustomToneLength: function () {
            var length = this.customTone() ? this.customTone().length : 0;
            return length + '/' + this.rules().maxCustomToneLength + ' ' + $.mage.__('Char');
        },

        /**
         * Get custom style length.
         *
         * @return {String}
         */
        getCustomStyleLength: function () {
            var length = this.customStyle() ? this.customStyle().length : 0;
            return length + '/' + this.rules().maxCustomStyleLength + ' ' + $.mage.__('Char');
        },

        /**
         * Check if not custom
         *
         * @param {string} value
         * @returns {boolean}
         */
        isNotCustom: function (value) {
            return value !== 'custom';
        },

        /**
         * Abort all pending AJAX requests
         */
        abortPendingRequests: function () {
            for (var key in self.savingPreferences) {
                if (self.savingPreferences.hasOwnProperty(key) && self.savingPreferences[key] !== null) {
                    self.savingPreferences[key].abort();
                    delete self.savingPreferences[key];
                }
            }
            self.savingPreferences = {};
        }
    });
});
