/**
 * JavaScript principal admin - SlideMind
 *
 * @package SlideMind
 * @since 2.0.0
 */

(function($) {
    'use strict';

    // Namespace
    window.SM = window.SM || {};

    /**
     * Utilitaires
     */
    SM.Utils = {
        /**
         * Copie un texte dans le presse-papiers
         */
        copyToClipboard: function(text, callback) {
            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(text).then(function() {
                    if (callback) callback(true);
                }).catch(function() {
                    SM.Utils.fallbackCopy(text, callback);
                });
            } else {
                SM.Utils.fallbackCopy(text, callback);
            }
        },

        fallbackCopy: function(text, callback) {
            var textarea = document.createElement('textarea');
            textarea.value = text;
            textarea.style.position = 'fixed';
            textarea.style.opacity = '0';
            document.body.appendChild(textarea);
            textarea.focus();
            textarea.select();

            try {
                document.execCommand('copy');
                if (callback) callback(true);
            } catch (err) {
                if (callback) callback(false);
            }

            document.body.removeChild(textarea);
        },

        /**
         * Affiche un message toast
         */
        showToast: function(message, type) {
            type = type || 'success';

            var $toast = $('<div class="sm-toast sm-toast-' + type + '">' + message + '</div>');

            $('body').append($toast);

            setTimeout(function() {
                $toast.addClass('show');
            }, 10);

            setTimeout(function() {
                $toast.removeClass('show');
                setTimeout(function() {
                    $toast.remove();
                }, 300);
            }, 3000);
        },

        /**
         * Affiche un prompt de mise à niveau
         */
        showUpgradePrompt: function(response) {
            var msg = (response.data && response.data.message) || slidemindAdmin.strings.proFeatureRequired;
            var url = (response.data && response.data.upgrade_url) || slidemindAdmin.upgradeUrl;
            if (url) {
                msg += ' <a href="' + url + '" style="color:#fff;text-decoration:underline;">' + slidemindAdmin.strings.viewPlans + '</a>';
            }
            SM.Utils.showToast(msg, 'error');
        },

        /**
         * Gère une erreur AJAX avec détection de limite de plan
         */
        handleAjaxError: function(response) {
            if (response.data && response.data.content_limit) {
                SM.Utils.showUpgradePrompt(response);
            } else {
                var msg = (response.data && response.data.message) || slidemindAdmin.strings.error;
                SM.Utils.showToast(msg, 'error');
            }
        },

        /**
         * Effectue une requête AJAX
         */
        sprintf: function(template) {
            var args = Array.prototype.slice.call(arguments, 1);
            var i = 0;
            return template.replace(/%(\d+\$)?[sd]/g, function(match, pos) {
                if (pos) {
                    return args[parseInt(pos) - 1];
                }
                return args[i++];
            });
        },

        /**
         * Formats model pricing as human-readable "~$X.XXX / interaction"
         *
         * Assumes a typical interaction: 800 prompt tokens + 400 completion tokens.
         * Pricing object comes from OpenRouter: { prompt: $/token, completion: $/token }
         *
         * @param {object} pricing - { prompt: string|number, completion: string|number }
         * @returns {string|null} Formatted text or null if free/unavailable
         */
        formatModelPricing: function(pricing) {
            if (!pricing) return null;

            var promptPerToken = parseFloat(pricing.prompt || 0);
            var completionPerToken = parseFloat(pricing.completion || 0);

            if (promptPerToken <= 0 && completionPerToken <= 0) {
                return slidemindAdmin.strings.pricingFree || null;
            }

            // Typical interaction: 800 prompt tokens + 400 completion tokens
            var costPerInteraction = (promptPerToken * 800) + (completionPerToken * 400);

            var formatted;
            if (costPerInteraction < 0.0001) {
                formatted = '<$0.0001';
            } else if (costPerInteraction < 0.01) {
                formatted = '$' + costPerInteraction.toFixed(4);
            } else {
                formatted = '$' + costPerInteraction.toFixed(3);
            }

            return SM.Utils.sprintf(
                slidemindAdmin.strings.pricingPerInteraction || '~%s / interaction',
                formatted
            );
        },

        /**
         * Calculates cumulative chat cost projection
         *
         * Chat interactions are cumulative: each turn sends full history.
         * Total prompt tokens for N turns: ~800 × N×(N+1)/2
         * Total completion tokens for N turns: ~400 × N
         *
         * @param {object} pricing - { prompt: $/token, completion: $/token }
         * @param {number} turns - Number of turns (0 = unlimited)
         * @returns {object} { totalCost: number, formatted: string }
         */
        calculateChatCostProjection: function(pricing, turns) {
            if (!pricing || turns <= 0) return { totalCost: 0, formatted: '' };

            var promptPerToken = parseFloat(pricing.prompt || 0);
            var completionPerToken = parseFloat(pricing.completion || 0);

            var totalPromptTokens = 800 * turns * (turns + 1) / 2;
            var totalCompletionTokens = 400 * turns;

            var totalCost = (promptPerToken * totalPromptTokens) + (completionPerToken * totalCompletionTokens);

            var formatted;
            if (totalCost < 0.0001) {
                formatted = '<$0.0001';
            } else if (totalCost < 0.01) {
                formatted = '$' + totalCost.toFixed(4);
            } else {
                formatted = '$' + totalCost.toFixed(3);
            }

            return { totalCost: totalCost, formatted: formatted };
        },

        /**
         * Renders chat cost projection into a container
         *
         * @param {jQuery} $container
         * @param {object} pricing - { prompt: $/token, completion: $/token }
         * @param {number} turns
         */
        renderChatCostProjection: function($container, pricing, turns) {
            if (!$container || !$container.length) return;

            turns = parseInt(turns) || 0;

            if (turns <= 0) {
                $container.html(
                    '<div class="sm-cost-projection sm-cost-projection--warning">' +
                    '<span class="dashicons dashicons-warning"></span> ' +
                    (slidemindAdmin.strings.costUnlimitedWarning || '') +
                    '</div>'
                ).show();
                return;
            }

            if (!pricing) {
                $container.empty().hide();
                return;
            }

            var result = this.calculateChatCostProjection(pricing, turns);

            $container.html(
                '<div class="sm-cost-projection">' +
                '<div class="sm-cost-projection-value">' +
                SM.Utils.sprintf(slidemindAdmin.strings.costEstimated || '', turns, result.formatted) +
                '</div>' +
                '<div class="sm-cost-projection-help">' +
                (slidemindAdmin.strings.costCumulativeExplanation || '') +
                '</div>' +
                '</div>'
            ).show();
        },

        _titleToWords: function(title) {
            if (!title) return null;
            var slug = title.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
            slug = slug.replace(/[^a-zA-Z0-9\s\-_]/g, '');
            var words = slug.split(/[\s\-_]+/).filter(Boolean).slice(0, 4);
            return words.length ? words : null;
        },

        generateEditorVarNames: function(title, slideNumber) {
            var words = this._titleToWords(title);
            if (!words) return null;
            var camel = words.map(function(w, i) {
                w = w.toLowerCase();
                return i === 0 ? w : w.charAt(0).toUpperCase() + w.slice(1);
            }).join('');
            if (camel.length > 30) camel = camel.substring(0, 30);
            var prefix = slideNumber ? 's' + slideNumber + '_' : '';
            return {
                input:  'userMsg_' + prefix + camel,
                output: 'aiResp_'  + prefix + camel
            };
        },

        generateMemoryKeyNames: function(title, slideNumber) {
            var words = this._titleToWords(title);
            if (!words) return null;
            var snake = words.map(function(w) { return w.toLowerCase(); }).join('_');
            if (snake.length > 30) snake = snake.substring(0, 30);
            var prefix = slideNumber ? 's' + slideNumber + '_' : '';
            return {
                input:    'input_' + prefix + snake,
                response: 'response_' + prefix + snake
            };
        },

        ajax: function(action, data, callback, errorCallback) {
            data = data || {};
            data.action = action;
            data.nonce = slidemindAdmin.nonce;

            $.ajax({
                url: slidemindAdmin.ajaxUrl,
                type: 'POST',
                data: data,
                success: function(response) {
                    if (callback) callback(response);
                },
                error: function(xhr, status, error) {
                    console.error('SM AJAX Error:', action, error, xhr.responseText);
                    SM.Utils.showToast(slidemindAdmin.strings.error, 'error');
                    if (errorCallback) errorCallback(xhr, status, error);
                }
            });
        },

        filterTemplateKeysByMode: function(keys, templates, filterMode) {
            if (!filterMode || filterMode === 'capture') return keys;
            return keys.filter(function(slug) {
                if (slug === 'custom') return true;
                var t = templates[slug];
                return !t.widget_mode || t.widget_mode === filterMode;
            });
        },

        esc: function(str) {
            if (str == null) return '';
            return String(str)
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;');
        }
    };

    /**
     * Gestionnaire de copie
     */
    SM.CopyHandler = {
        init: function() {
            $(document).on('click', '.sm-copyable-btn, .copy-slug-button', function(e) {
                e.preventDefault();

                var $btn = $(this);
                var text;

                // Slides list: generate full integration code
                if ($btn.hasClass('sm-copy-integration') && $btn.data('config-uid')) {
                    text = SM.CodeGenerator.buildForList({
                        configUid:  $btn.data('config-uid'),
                        widgetMode: $btn.data('widget-mode') || 'qa',
                        inputVar:   $btn.data('editor-input-var') || 'userMessage',
                        outputVar:  $btn.data('editor-output-var') || 'aiResponse',
                        hasMemory:  $btn.data('has-memory') === 1 || $btn.data('has-memory') === '1',
                        memoryVars: $btn.data('memory-variables') || [],
                        restUrl:    slidemindAdmin.restUrl,
                        siteUrl:    slidemindAdmin.siteUrl,
                        authoringTool: (slidemindAdmin.defaults && slidemindAdmin.defaults.editor) || 'storyline'
                    });
                } else {
                    text = $btn.data('slug') || $btn.siblings('.sm-copyable-text').text() || $btn.closest('.sm-copyable').find('code').text();
                }

                SM.Utils.copyToClipboard(text, function(success) {
                    if (success) {
                        var originalText = $btn.text();
                        $btn.text(slidemindAdmin.strings.copied);

                        setTimeout(function() {
                            $btn.text(originalText);
                        }, 2000);
                    }
                });
            });
        }
    };

    /**
     * Gestionnaire des modales
     */
    SM.Modal = {
        init: function() {
            $(document).on('click', '[data-sm-modal]', function(e) {
                e.preventDefault();
                var modalId = $(this).data('sm-modal');
                SM.Modal.open(modalId);
            });

            $(document).on('click', '.sm-modal-overlay', function(e) {
                if ($(e.target).hasClass('sm-modal-overlay') && !$(this).data('custom-close')) {
                    SM.Modal.close();
                }
            });

            $(document).on('click', '.sm-modal-close', function(e) {
                e.preventDefault();
                if (!$(this).closest('.sm-modal-overlay').data('custom-close')) {
                    SM.Modal.close();
                }
            });

            $(document).on('keydown', function(e) {
                if (e.key === 'Escape') {
                    var $active = $('.sm-modal-overlay.active');
                    if ($active.length && !$active.data('custom-close')) {
                        SM.Modal.close();
                    }
                }
            });
        },

        open: function(modalId) {
            $('#' + modalId).addClass('active');
            $('body').css('overflow', 'hidden');
        },

        close: function() {
            $('.sm-modal-overlay').removeClass('active');
            $('body').css('overflow', '');
        }
    };

    /**
     * Gestionnaire des tabs
     */
    SM.Tabs = {
        init: function() {
            $(document).on('click', '.sm-tab', function(e) {
                e.preventDefault();

                var $tab = $(this);
                var target = $tab.data('tab');
                var $container = $tab.closest('.sm-tabs-container');

                // Mise à jour des tabs
                $container.find('.sm-tab').removeClass('active');
                $tab.addClass('active');

                // Mise à jour du contenu
                $container.find('.sm-tab-content').removeClass('active');
                $container.find('[data-tab-content="' + target + '"]').addClass('active');

                // Show search field only on learners tab
                $('#sm-learners-search').toggle(target === 'learners');

                // Hide save button on license and data tabs (they have their own AJAX)
                $('#sm-save-settings-wrap').toggle(target !== 'license' && target !== 'data');
            });
        }
    };

    /**
     * Settings handler
     */
    SM.Settings = {
        _initialState: null,
        _isDirty: false,

        init: function() {
            // Capture initial form state after page load
            var self = this;
            var $form = $('#sm-settings-form');
            if ($form.length) {
                self._initialState = $form.serialize();
                self._isDirty = false;

                // Track changes on all form inputs
                $form.on('change input', 'input, select, textarea', function() {
                    var currentState = $form.serialize();
                    self._isDirty = (currentState !== self._initialState);
                });

                // Warn before leaving with unsaved changes
                $(window).on('beforeunload.smSettings', function() {
                    if (self._isDirty) {
                        return slidemindAdmin.strings.unsavedChanges;
                    }
                });
            }

            // Test de la clé API
            $(document).on('click', '#sm-test-api-key', function(e) {
                e.preventDefault();
                SM.Settings.testApiKey();
            });

            // Sauvegarde des réglages
            $(document).on('submit', '#sm-settings-form', function(e) {
                e.preventDefault();
                SM.Settings.save();
            });

            // Tout cocher / décocher par section profil
            $(document).on('click', '.sm-profile-toggle-all', function(e) {
                e.preventDefault();
                var $section = $(this).closest('.sm-collapsible-content');
                var $cb = $section.find('input[type="checkbox"]');
                var allChecked = $cb.length === $cb.filter(':checked').length;
                $cb.prop('checked', !allChecked);
                var $parent = $(this).closest('.sm-profile-section');
                var total = $cb.length;
                var checked = $cb.filter(':checked').length;
                $parent.find('.sm-profile-badge').text(checked + '/' + total);
            });

            // Mise à jour badge au changement de checkbox profil
            $(document).on('change', '.sm-profile-section input[type="checkbox"]', function() {
                var $section = $(this).closest('.sm-profile-section');
                var total = $section.find('input[type="checkbox"]').length;
                var checked = $section.find('input[type="checkbox"]:checked').length;
                $section.find('.sm-profile-badge').text(checked + '/' + total);
            });

            // Toggle alias input via pencil icon
            $(document).on('click', '.sm-profile-edit-alias', function() {
                var $wrapper = $(this).closest('.sm-profile-field-row').find('.sm-profile-alias-wrapper');
                $wrapper.toggleClass('sm-alias-visible');
                if ($wrapper.hasClass('sm-alias-visible')) {
                    $wrapper.find('.sm-profile-alias-input').focus();
                }
            });

            // Live update on alias input
            $(document).on('input', '.sm-profile-alias-input', function() {
                var $row = $(this).closest('.sm-profile-field-row');
                var originalKey = $row.data('field-key');
                var originalLabel = $row.data('original-label');
                var raw = $(this).val().toLowerCase().replace(/[^a-z0-9_]/g, '').replace(/_{2,}/g, '_');

                if (raw !== $(this).val()) {
                    $(this).val(raw);
                }

                var varName = raw || originalKey;
                $row.find('.sm-profile-var-name').text(varName);

                if (raw) {
                    $row.addClass('sm-alias-active');
                    $row.find('.sm-profile-display-name').text(raw);
                    var $orig = $row.find('.sm-profile-original-name');
                    if (!$orig.length) {
                        $row.find('.sm-profile-display-name').after('<span class="sm-profile-original-name">(' + originalLabel + ')</span>');
                    } else {
                        $orig.show();
                    }
                } else {
                    $row.removeClass('sm-alias-active');
                    $row.find('.sm-profile-display-name').text(originalLabel);
                    $row.find('.sm-profile-original-name').hide();
                }
            });
        },

        testApiKey: function() {
            // Delegate to SM.Providers for the active provider
            var provider = $('#sm-global-provider').val() || 'openrouter';
            SM.Providers.testProviderKey(provider);
        },

        save: function() {
            var $form = $('#sm-settings-form');
            var $btn = $form.find('[type="submit"]');
            var data = $form.serializeArray();
            var settings = {};

            data.forEach(function(item) {
                var match;
                if (item.name.indexOf('[]') !== -1) {
                    // name[] — tableau indexé
                    var arrKey = item.name.replace(/\[\]$/, '');
                    if (!settings[arrKey]) {
                        settings[arrKey] = [];
                    }
                    settings[arrKey].push(item.value);
                } else if ((match = item.name.match(/^([^\[]+)\[([^\]]+)\]$/))) {
                    // name[key] — objet associatif
                    if (!settings[match[1]]) {
                        settings[match[1]] = {};
                    }
                    settings[match[1]][match[2]] = item.value;
                } else {
                    settings[item.name] = item.value;
                }
            });

            $btn.prop('disabled', true).text(slidemindAdmin.strings.saving);

            SM.Utils.ajax('slidemind_save_settings', { settings: settings }, function(response) {
                $btn.prop('disabled', false).text(slidemindAdmin.strings.saved);

                setTimeout(function() {
                    $btn.text(slidemindAdmin.strings.saveButton);
                }, 2000);

                if (response.success) {
                    SM.Utils.showToast(response.data.message, 'success');
                    SM.Autocomplete.invalidateCache();
                    // Reset dirty state — saved data is now the new baseline
                    SM.Settings._initialState = $form.serialize();
                    SM.Settings._isDirty = false;
                } else {
                    SM.Utils.showToast(response.data.message, 'error');
                }
            });
        }
    };

    /**
     * Gestionnaire des configurations
     */
    SM.Configurations = {
        init: function() {
            // Suppression
            $(document).on('click', '.sm-delete-config', function(e) {
                e.preventDefault();

                if (!confirm(slidemindAdmin.strings.confirmDelete)) {
                    return;
                }

                var $btn = $(this);
                var configId = $btn.data('config-id');

                SM.Utils.ajax('slidemind_delete_configuration', { config_id: configId }, function(response) {
                    if (response.success) {
                        $btn.closest('tr').fadeOut(300, function() {
                            $(this).remove();
                        });
                        SM.Utils.showToast(response.data.message, 'success');
                    } else {
                        SM.Utils.showToast(response.data.message, 'error');
                    }
                });
            });

            // Initialise Select2 sur les sélecteurs de modèle
            if ($.fn.select2) {
                $('.sm-model-select').select2({
                    placeholder: slidemindAdmin.strings.selectModelPlaceholder,
                    width: '100%'
                });
            }

            // Toggle des champs conditionnels
            $(document).on('change', '#sm-usage-scope', function() {
                SM.Configurations.toggleScopeFields($(this).val());
            });

            // Sauvegarde de configuration
            $(document).on('submit', '#sm-config-form', function(e) {
                e.preventDefault();
                SM.Configurations.save();
            });
        },

        toggleScopeFields: function(scope) {
            var $moduleField = $('#sm-module-field');
            var $slideField = $('#sm-slide-field');

            $moduleField.hide();
            $slideField.hide();

            if (scope === 'module' || scope === 'slide') {
                $moduleField.show();
            }

            if (scope === 'slide') {
                $slideField.show();
            }
        },

        save: function() {
            var $form = $('#sm-config-form');
            var $btn = $form.find('[type="submit"]');
            var data = $form.serializeArray();
            var config = {};

            data.forEach(function(item) {
                config[item.name] = item.value;
            });

            $btn.prop('disabled', true).text(slidemindAdmin.strings.saving);

            SM.Utils.ajax('slidemind_save_configuration', { configuration: config }, function(response) {
                $btn.prop('disabled', false).text(slidemindAdmin.strings.saveButton);

                if (response.success) {
                    SM.Utils.showToast(response.data.message, 'success');

                    // Met à jour l'ID Configuration affiché
                    if (response.data.config_uid) {
                        $('#sm-config-uid-display').text(response.data.config_uid);
                    }

                    // Redirige si nouvelle config
                    if (!config.id && response.data.config_id) {
                        window.location.href = window.location.href.replace('slidemind-new', 'slidemind-configurations');
                    }
                } else {
                    SM.Utils.showToast(response.data.message, 'error');
                }
            });
        }
    };

    /**
     * Gestionnaire des courses
     */
    SM.Courses = {
        init: function() {
            // Suppression
            $(document).on('click', '.sm-delete-course', function(e) {
                e.preventDefault();

                var $btn = $(this);
                var courseId = $btn.data('course-id');
                var modulesCount = parseInt($btn.data('modules-count') || 0);

                if (modulesCount > 0) {
                    SM.Utils.showToast(SM.Utils.sprintf(slidemindAdmin.strings.cannotDeleteModulesLinked, modulesCount), 'error');
                    return;
                }

                if (!confirm(slidemindAdmin.strings.confirmDeleteCourse)) {
                    return;
                }

                SM.Utils.ajax('slidemind_delete_course', { course_id: courseId }, function(response) {
                    if (response.success) {
                        $btn.closest('tr').fadeOut(300, function() {
                            $(this).remove();
                        });
                        SM.Utils.showToast(response.data.message, 'success');
                    } else {
                        SM.Utils.showToast(response.data.message, 'error');
                    }
                });
            });

            // Sauvegarde
            $(document).on('submit', '#sm-course-form', function(e) {
                e.preventDefault();
                SM.Courses.save();
            });
        },

        save: function() {
            var $form = $('#sm-course-form');
            var $btn = $form.find('[type="submit"]');
            var data = $form.serializeArray();
            var course = {};

            data.forEach(function(item) {
                course[item.name] = item.value;
            });

            $btn.prop('disabled', true).text(slidemindAdmin.strings.saving);

            SM.Utils.ajax('slidemind_save_course', { course: course }, function(response) {
                $btn.prop('disabled', false).html('<span class="dashicons dashicons-saved"></span> ' + (course.id ? slidemindAdmin.strings.updateButton : slidemindAdmin.strings.createCourse));

                if (response.success) {
                    SM.Utils.showToast(response.data.message, 'success');

                    // Redirige si nouvelle course
                    if (!course.id && response.data.course_id) {
                        window.location.href = 'admin.php?page=slidemind-courses';
                    }
                } else {
                    SM.Utils.handleAjaxError(response);
                }
            });
        }
    };

    /**
     * Gestionnaire des modules
     */
    SM.Modules = {
        init: function() {
            // Suppression
            $(document).on('click', '.sm-delete-module', function(e) {
                e.preventDefault();

                var $btn = $(this);
                var moduleId = $btn.data('module-id');
                var slidesCount = parseInt($btn.data('slides-count') || 0);

                if (slidesCount > 0) {
                    SM.Utils.showToast(SM.Utils.sprintf(slidemindAdmin.strings.cannotDeleteSlidesLinked, slidesCount), 'error');
                    return;
                }

                if (!confirm(slidemindAdmin.strings.confirmDeleteModule)) {
                    return;
                }

                SM.Utils.ajax('slidemind_delete_module', { module_id: moduleId }, function(response) {
                    if (response.success) {
                        $btn.closest('tr').fadeOut(300, function() {
                            $(this).remove();
                        });
                        SM.Utils.showToast(response.data.message, 'success');
                    } else {
                        SM.Utils.showToast(response.data.message, 'error');
                    }
                });
            });

            // Change de course -> charger contenu hérité
            $(document).on('change', '#sm-module-course', function() {
                SM.Modules.loadInheritedContent($(this).val());
            });

            // Sauvegarde
            $(document).on('submit', '#sm-module-form', function(e) {
                e.preventDefault();
                SM.Modules.save();
            });
        },

        loadInheritedContent: function(courseId) {
            var $preview = $('#sm-inherited-content');
            var $link = $('#sm-edit-course-link');

            if (!courseId) {
                $preview.hide();
                return;
            }

            SM.Utils.ajax('slidemind_get_course', { course_id: courseId }, function(response) {
                if (response.success) {
                    var f = response.data.course;
                    var html = '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelTheme + '</strong> <span id="sm-inherited-theme">' + (f.theme || '—') + '</span></div>' +
                        '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelObjectives + '</strong> <div id="sm-inherited-objectives" class="sm-inherited-text">' + (f.objectives || '—').replace(/\n/g, '<br>') + '</div></div>';

                    $preview.find('.sm-inherited-preview').html(html);
                    $link.attr('href', 'admin.php?page=slidemind-course-edit&course_id=' + courseId);
                    $preview.show();
                }
            });
        },

        save: function() {
            var $form = $('#sm-module-form');
            var $btn = $form.find('[type="submit"]');
            var data = $form.serializeArray();
            var module = {};

            data.forEach(function(item) {
                module[item.name] = item.value;
            });

            $btn.prop('disabled', true).text(slidemindAdmin.strings.saving);

            SM.Utils.ajax('slidemind_save_module', { module: module }, function(response) {
                $btn.prop('disabled', false).html('<span class="dashicons dashicons-saved"></span> ' + (module.id ? slidemindAdmin.strings.updateButton : slidemindAdmin.strings.createModule));

                if (response.success) {
                    SM.Utils.showToast(response.data.message, 'success');

                    // Redirige si nouveau module
                    if (!module.id && response.data.module_id) {
                        window.location.href = 'admin.php?page=slidemind-modules';
                    }
                } else {
                    SM.Utils.handleAjaxError(response);
                }
            });
        }
    };

    /**
     * Gestionnaire des slides
     */
    SM.Slides = {
        init: function() {
            // Suppression
            $(document).on('click', '.sm-delete-slide', function(e) {
                e.preventDefault();

                if (!confirm(slidemindAdmin.strings.confirmDeleteSlide)) {
                    return;
                }

                var $btn = $(this);
                var slideId = $btn.data('slide-id');

                SM.Utils.ajax('slidemind_delete_slide', { slide_id: slideId }, function(response) {
                    if (response.success) {
                        $btn.closest('tr').fadeOut(300, function() {
                            $(this).remove();
                        });
                        SM.Utils.showToast(response.data.message, 'success');
                    } else {
                        SM.Utils.showToast(response.data.message, 'error');
                    }
                });
            });

            // Change de course -> filtrer modules et mettre à jour scopes
            $(document).on('change', '#sm-slide-course', function() {
                var courseId = $(this).val();
                var $moduleSelect = $('#sm-slide-module');
                var $moduleOptions = $moduleSelect.find('option[data-course-id]');

                if (!courseId) {
                    // No course: disable module select, reset
                    $moduleSelect.val('').prop('disabled', true);
                    $moduleSelect.find('option:first').text(slidemindAdmin.strings.selectTrainingFirst || 'Select a training first');
                    $moduleOptions.hide();
                    SM.Slides.loadInheritedContent(null, null);
                } else {
                    // Enable module select, filter by course
                    $moduleSelect.prop('disabled', false);
                    $moduleSelect.find('option:first').text(slidemindAdmin.strings.noneTrainingLevel || 'None (training-level slide)');
                    $moduleOptions.each(function() {
                        $(this).toggle($(this).data('course-id') == courseId);
                    });
                    // Reset module if it doesn't belong to this course
                    if ($moduleSelect.val() && $moduleSelect.find('option:selected').data('course-id') != courseId) {
                        $moduleSelect.val('');
                    }
                    SM.Slides.loadInheritedContent(null, courseId);
                }
                SM.MemoryVariables.updateAllScopes();
            });

            // Change de module -> charger contenu hérité
            $(document).on('change', '#sm-slide-module', function() {
                var moduleId = $(this).val();
                if (moduleId) {
                    SM.Slides.loadInheritedContent(moduleId, null);
                } else {
                    // Module cleared but course still selected
                    var courseId = $('#sm-slide-course').val();
                    SM.Slides.loadInheritedContent(null, courseId);
                }
                SM.MemoryVariables.updateAllScopes();
            });

            // Template cards in system prompt card
            if ($('#sm-slide-template-cards').length) {
                SM.Slides.loadTemplateCards();
            }

            // Template card click handler (delegated)
            $(document).on('change', '#sm-slide-template-cards input[name="slide_template"]', function() {
                var val = $(this).val();
                if (val === '__dropdown__') {
                    $('#sm-slide-template-cards .sm-template-dropdown-select').show().focus();
                    return;
                }
                $('#sm-slide-template-cards .sm-template-dropdown-select').hide().val('');
                SM.Slides.applyTemplate(val);
            });

            // Dropdown select for "other templates"
            $(document).on('change', '#sm-slide-template-cards .sm-template-dropdown-select', function() {
                var slug = $(this).val();
                if (!slug) return;
                $('#sm-slide-template-cards input[name="slide_template"][value="__dropdown__"]').val(slug);
                SM.Slides.applyTemplate(slug);
            });

            // Copy code blocks (generic handler)
            $(document).on('click', '.sm-copy-code-btn', function() {
                var $btn = $(this);
                var idx = $btn.data('target-pre');
                var $pre = $btn.closest('.sm-card-body').find('.sm-code-block').eq(idx || 0);
                var code = $pre.text();
                SM.Utils.copyToClipboard(code, function(success) {
                    if (success) {
                        var originalHtml = $btn.html();
                        $btn.html('<span class="dashicons dashicons-yes"></span> ' + slidemindAdmin.strings.copied);
                        setTimeout(function() {
                            $btn.html(originalHtml);
                        }, 2000);
                    }
                });
            });

            // Insert variable into prompt
            $(document).on('click', '#sm-insert-variable', function() {
                var name = prompt(slidemindAdmin.strings.variableNamePrompt);
                if (!name) return;

                name = name.replace(/[^a-zA-Z0-9_]/g, '_').toLowerCase();
                var tag = '{{' + name + '}}';
                var $textarea = $('#sm-slide-prompt');
                var textarea = $textarea[0];
                var start = textarea.selectionStart;
                var end = textarea.selectionEnd;
                var text = $textarea.val();

                $textarea.val(text.substring(0, start) + tag + text.substring(end));
                textarea.selectionStart = textarea.selectionEnd = start + tag.length;
                $textarea.focus();
            });

            // Mode widget (radio cards)
            $(document).on('change', 'input[name="widget_mode"]', function() {
                SM.Slides.applyMode($(this).val());
            });

            // Mode selector visual toggle
            $(document).on('change', 'input[name="widget_mode"]', function() {
                $('.sm-mode-option').removeClass('sm-mode-option--active');
                $(this).closest('.sm-mode-option').addClass('sm-mode-option--active');
            });

            // Graceful close toggle
            $(document).on('change', '#sm-graceful-close', function() {
                $('#sm-graceful-close-prompt-wrap').toggle($(this).is(':checked'));
            });

            // Cost projection: update on model or turns change
            $(document).on('change', '#sm-slide-model', function() {
                SM.Slides.updateCostProjection();
            });
            $(document).on('input change', '#sm-max-turns', function() {
                SM.Slides.updateCostProjection();
            });

            // Appel initial si on est sur la page slide-edit
            var $modeRadio = $('input[name="widget_mode"]:checked');
            if ($modeRadio.length) {
                SM.Slides.applyMode($modeRadio.val());
                SM.Slides.updateCostProjection();
            }

            // Sauvegarde
            $(document).on('submit', '#sm-slide-form', function(e) {
                e.preventDefault();
                SM.Slides.save();
            });
        },

        applyMode: function(mode) {
            var isChat    = mode === 'chat';
            var isCapture = mode === 'capture';

            // AI cards: hide in capture mode
            $('#sm-ai-model-card').toggle(!isCapture);
            $('#sm-system-prompt-card').toggle(!isCapture);
            $('#sm-response-example-card').toggle(!isCapture);
            $('#sm-apikey-card').toggle(!isCapture);

            // Toggle required on fields hidden in capture mode
            $('#sm-slide-model').prop('required', !isCapture);

            // Mode-specific cards
            $('#sm-chat-options-card').toggle(isChat);
            $('#sm-output-vars-card').toggle(mode === 'qa');

            // Auto-save section: hide in capture (no AI to auto-save)
            $('#sm-autosave-section').toggle(!isCapture);

            // Sandbox link: hide in capture
            $('.sm-sandbox-link').toggle(!isCapture);

            // Template cards: hide in capture (AI-specific), re-filter by mode
            $('#sm-slide-template-cards').toggle(!isCapture);
            if (!isCapture && SM.Slides._templates) {
                var currentSlug = $('#sm-slide-template-cards input[name="slide_template"]:checked').val() || 'custom';
                SM.Slides.renderTemplateCards($('#sm-slide-template-cards'), SM.Slides._templates, currentSlug, mode);
            }

            // Code generator
            var $codeCard = $('#sm-integration-code-card');
            if ($codeCard.length) {
                $codeCard.attr('data-widget-mode', mode);

                // Keep the dropdown always visible; filter its options based on the new mode.
                var $editorGroup = $('#sm-editor-select').closest('.sm-form-group');
                $editorGroup.show();

                SM.CodeGenerator.currentWidgetMode = mode;
                SM.CodeGenerator.filterEditorOptionsByMode(mode);

                // Sync currentEditor with what the select now shows (filterEditorOptionsByMode may have reset it).
                SM.CodeGenerator.currentEditor = $('#sm-editor-select').val();

                var isEmbed = (SM.CodeGenerator.currentEditor === 'widget' || SM.CodeGenerator.currentEditor === 'shortcode');
                $('#sm-editor-var-names').toggle(!isEmbed);

                SM.CodeGenerator.update();
            }
        },

        loadInheritedContent: function(moduleId, courseId) {
            var $card = $('#sm-inherited-card');
            var $preview = $('#sm-inherited-preview');

            if (!moduleId && !courseId) {
                $card.hide();
                window.slidemindInheritedFields = {};
                SM.Autocomplete.invalidateCache();
                SM.Slides.updateApiKeyHelp({ module: null, course: null });
                return;
            }

            var renderInherited = function(inherited) {
                var html = '';
                var fields = {};

                var varTag = function(v) {
                    return ' <code class="sm-inherited-var-tag sm-clickable-var">{{' + v + '}}</code>';
                };
                var truncate = function(s) {
                    return (s || '—').substring(0, 200);
                };

                if (inherited.course) {
                    var f = inherited.course;
                    fields.course_theme = !!f.theme;
                    fields.course_objectives = !!f.objectives;
                    fields.course_content = !!f.content;
                    fields.course_response_example = !!f.response_example;
                    html += '<div class="sm-inherited-section">' +
                        '<h4>' + slidemindAdmin.strings.labelCourse + ' ' + f.title + '</h4>' +
                        '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelTheme.replace(':', '') + '</strong>' + varTag('course_theme') + ' <span class="sm-inherited-value">' + (f.theme || '—') + '</span></div>' +
                        '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelObjectives.replace(':', '') + '</strong>' + varTag('course_objectives') + '<div class="sm-inherited-text">' + truncate(f.objectives) + '</div></div>' +
                        '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelContent.replace(':', '') + '</strong>' + varTag('course_content') + '<div class="sm-inherited-text">' + truncate(f.content) + '</div></div>' +
                        '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelResponseExample + '</strong>' + varTag('course_response_example') + '<div class="sm-inherited-text">' + truncate(f.response_example) + '</div></div>' +
                        '</div>';
                }

                if (inherited.module) {
                    var m = inherited.module;
                    fields.module_theme = !!m.theme;
                    fields.module_objectives = !!m.objectives;
                    fields.module_content = !!m.content;
                    fields.module_response_example = !!m.response_example;
                    html += '<div class="sm-inherited-section" style="margin-top: 16px; padding-top: 16px; border-top: 1px solid #e5e7eb;">' +
                        '<h4>' + slidemindAdmin.strings.labelModule + ' ' + m.title + '</h4>' +
                        '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelTheme.replace(':', '') + '</strong>' + varTag('module_theme') + ' <span class="sm-inherited-value">' + (m.theme || '—') + '</span></div>' +
                        '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelObjectives.replace(':', '') + '</strong>' + varTag('module_objectives') + '<div class="sm-inherited-text">' + truncate(m.objectives) + '</div></div>' +
                        '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelContent.replace(':', '') + '</strong>' + varTag('module_content') + '<div class="sm-inherited-text">' + truncate(m.content) + '</div></div>' +
                        '<div class="sm-inherited-field"><strong>' + slidemindAdmin.strings.labelResponseExample + '</strong>' + varTag('module_response_example') + '<div class="sm-inherited-text">' + truncate(m.response_example) + '</div></div>' +
                        '</div>';
                }

                // Update global inherited fields map for @ autocomplete filtering
                window.slidemindInheritedFields = fields;
                SM.Autocomplete.invalidateCache();

                $preview.html(html);
                $card.show();
                SM.Slides.updateApiKeyHelp(inherited);
            };

            if (moduleId) {
                // Module selected: get full inherited content (module + course)
                SM.Utils.ajax('slidemind_get_inherited_content', { module_id: moduleId }, function(response) {
                    if (response.success) {
                        renderInherited(response.data.inherited);
                    }
                });
            } else if (courseId) {
                // Course only (no module): get course data
                SM.Utils.ajax('slidemind_get_course', { course_id: courseId }, function(response) {
                    if (response.success) {
                        var f = response.data.course;
                        renderInherited({
                            course: {
                                title: f.title,
                                theme: f.theme,
                                objectives: f.objectives,
                                content: f.content,
                                response_example: f.response_example,
                                has_custom_key: f.has_custom_key
                            },
                            module: null
                        });
                    }
                });
            }
        },

        updateApiKeyHelp: function(inherited) {
            var $help = $('#sm-apikey-help');
            if (!$help.length) return;

            // Ne pas écraser si la slide a sa propre clé (texte vert côté serveur)
            if ($help.find('[style*="sm-success"]').length) return;

            var helpText;
            if (inherited.module && inherited.module.has_custom_key) {
                helpText = '<span style="color: var(--sm-info, #2196f3);">' +
                    SM.Utils.sprintf(slidemindAdmin.strings.apiKeyModule, inherited.module.title) +
                    '</span>';
            } else if (inherited.course && inherited.course.has_custom_key) {
                helpText = '<span style="color: var(--sm-info, #2196f3);">' +
                    SM.Utils.sprintf(slidemindAdmin.strings.apiKeyCourse, inherited.course.title) +
                    '</span>';
            } else {
                helpText = slidemindAdmin.strings.apiKeyGlobal;
            }
            $help.html(helpText);
        },

        updateCostProjection: function() {
            var $container = $('#sm-chat-cost-projection');
            if (!$container.length) return;

            var $selected = $('#sm-slide-model option:selected');
            var pricing = $selected.data('pricing') || null;
            var turns = parseInt($('#sm-max-turns').val()) || 0;

            SM.Utils.renderChatCostProjection($container, pricing, turns);
        },

        loadTemplateCards: function() {
            var $container = $('#sm-slide-template-cards');
            if (!$container.length) return;

            var selectedSlug = $container.find('input[name="slide_template"]:checked').val() || 'custom';

            SM.Utils.ajax('slidemind_get_templates', {}, function(response) {
                var templates = {};

                if (response.success && response.data.templates) {
                    response.data.templates.forEach(function(t) {
                        templates[t.slug] = {
                            name: t.title,
                            icon: t.icon || '',
                            description: t.description || '',
                            widget_mode: t.widget_mode || ''
                        };
                    });
                }

                templates['custom'] = {
                    name: slidemindAdmin.strings.templateCustomName || 'Custom',
                    icon: '✏️',
                    description: slidemindAdmin.strings.templateCustomDesc || '',
                    widget_mode: ''
                };

                SM.Slides._templates = templates;
                var currentMode = $('input[name="widget_mode"]:checked').val() || '';
                SM.Slides.renderTemplateCards($container, templates, selectedSlug, currentMode);
            });
        },

        renderTemplateCards: function($container, templates, selectedSlug, filterMode) {
            $container.empty();

            var favorites = (slidemindAdmin && slidemindAdmin.favoriteTemplates) ? slidemindAdmin.favoriteTemplates : [];
            var keys = SM.Utils.filterTemplateKeysByMode(Object.keys(templates), templates, filterMode);

            // If selected slug was filtered out, fallback to custom
            if (selectedSlug !== 'custom' && keys.indexOf(selectedSlug) === -1) {
                selectedSlug = 'custom';
            }

            var favoriteKeys = [];
            var otherKeys = [];

            keys.forEach(function(slug) {
                if (slug === 'custom') return;
                if (favorites.indexOf(slug) !== -1) {
                    favoriteKeys.push(slug);
                } else {
                    otherKeys.push(slug);
                }
            });

            // Keep favorites in settings order
            favoriteKeys.sort(function(a, b) {
                return favorites.indexOf(a) - favorites.indexOf(b);
            });

            var buildCard = function(slug, t, checked) {
                return '<label class="sm-template-card">' +
                    '<input type="radio" name="slide_template" value="' + slug + '"' + (checked ? ' checked' : '') + '>' +
                    '<div class="sm-template-card-content">' +
                    '<span class="sm-template-icon">' + (t.icon || '') + '</span>' +
                    '<strong>' + t.name + '</strong>' +
                    '</div>' +
                    '</label>';
            };

            // If no favorites, show all as cards
            if (favorites.length === 0) {
                keys.forEach(function(slug) {
                    var t = templates[slug];
                    $container.append(buildCard(slug, t, slug === selectedSlug));
                });
                return;
            }

            // Always add "Custom" card first
            var isCustomSelected = selectedSlug === 'custom' || (!templates[selectedSlug] && otherKeys.indexOf(selectedSlug) === -1);
            var customT = templates['custom'];
            $container.append(buildCard('custom', customT, isCustomSelected));

            // Render favorite cards
            favoriteKeys.forEach(function(slug) {
                var t = templates[slug];
                $container.append(buildCard(slug, t, slug === selectedSlug));
            });

            // "Other templates" dropdown if any
            if (otherKeys.length > 0) {
                var isOtherSelected = otherKeys.indexOf(selectedSlug) !== -1;
                var dropdownHtml = '<div class="sm-template-more">' +
                    '<label class="sm-template-card sm-template-card-dropdown">' +
                    '<input type="radio" name="slide_template" value="__dropdown__"' + (isOtherSelected ? ' checked' : '') + '>' +
                    '<div class="sm-template-card-content">' +
                    '<span class="sm-template-icon">📂</span>' +
                    '<strong>' + (slidemindAdmin.strings.otherTemplates || 'Other templates') + '</strong>' +
                    '</div>' +
                    '</label>' +
                    '<select class="sm-form-select sm-template-dropdown-select"' + (isOtherSelected ? '' : ' style="display: none;"') + '>' +
                    '<option value="">' + (slidemindAdmin.strings.chooseTemplate || '— Choose a template —') + '</option>';

                otherKeys.forEach(function(slug) {
                    var t = templates[slug];
                    dropdownHtml += '<option value="' + slug + '"' + (slug === selectedSlug ? ' selected' : '') + '>' +
                        (t.icon ? t.icon + ' ' : '') + t.name + '</option>';
                });

                dropdownHtml += '</select></div>';
                $container.append(dropdownHtml);
            }
        },

        applyTemplate: function(slug) {
            if (slug === 'custom') {
                // Don't clear prompt on edit (better UX)
                return;
            }

            SM.Utils.ajax('slidemind_get_template_by_slug', { slug: slug }, function(response) {
                if (!response.success || !response.data.template) return;
                var t = response.data.template;

                // Prompt
                $('#sm-slide-prompt').val(t.system_prompt || '');

                // Widget mode (trigger change -> applyMode -> re-filter cards)
                if (t.widget_mode) {
                    $('input[name="widget_mode"][value="' + t.widget_mode + '"]')
                        .prop('checked', true).trigger('change');
                }

                // Temperature + max tokens
                if (t.temperature !== undefined) {
                    $('#sm-slide-temperature').val(t.temperature);
                    $('#sm-slide-temp-value').text(t.temperature);
                }
                if (t.max_tokens) {
                    $('#sm-slide-max-tokens').val(t.max_tokens);
                }

                // Chat fields
                if (t.widget_mode === 'chat') {
                    if (t.max_turns) $('#sm-max-turns').val(t.max_turns);
                    if (t.closing_message) $('#sm-closing-message').val(t.closing_message);
                    if (t.opening_message) $('#sm-opening-message').val(t.opening_message);
                    $('#sm-graceful-close').prop('checked', !!t.graceful_close).trigger('change');
                }

                // Output variables: load from template or clear
                if (t.output_variables && t.output_variables.length) {
                    SM.OutputVariables.loadFromArray(t.output_variables);
                } else {
                    $('#sm-output-vars-list').empty();
                }
            });
        },

        save: function() {
            // Valide les variables de retour si le repeater existe
            if ($('#sm-output-vars-list').length) {
                var outputError = SM.OutputVariables.validate();
                if (outputError) {
                    SM.Utils.showToast(outputError, 'error');
                    return;
                }
                $('#sm-output-variables-json').val(SM.OutputVariables.collect());
            }

            // Validate and collect memory variables
            if ($('#sm-memory-vars-list').length) {
                var memError = SM.MemoryVariables.validate();
                if (memError) {
                    SM.Utils.showToast(memError, 'error');
                    return;
                }
                $('#sm-memory-variables-json').val(SM.MemoryVariables.collect());
            }

            var $form = $('#sm-slide-form');
            var $btn = $form.find('[type="submit"]');
            var data = $form.serializeArray();
            var slide = {};

            data.forEach(function(item) {
                slide[item.name] = item.value;
            });

            if (typeof slide.opening_message === 'undefined') {
                slide.opening_message = '';
            }
            // Ensure unchecked checkboxes send '0'
            if (!slide.graceful_close) {
                slide.graceful_close = '0';
            }
            if (!slide.save_user_input) {
                slide.save_user_input = '0';
            }
            if (!slide.save_ai_response) {
                slide.save_ai_response = '0';
            }

            $btn.prop('disabled', true).text(slidemindAdmin.strings.saving);

            SM.Utils.ajax('slidemind_save_slide', { slide: slide }, function(response) {
                $btn.prop('disabled', false).html('<span class="dashicons dashicons-saved"></span> ' + (slide.id ? slidemindAdmin.strings.updateButton : slidemindAdmin.strings.createSlide));

                if (response.success) {
                    SM.Utils.showToast(response.data.message, 'success');

                    // Redirige vers la page d'édition si nouvelle slide
                    if (!slide.id && response.data.slide_id) {
                        window.location.href = 'admin.php?page=slidemind-slide-edit&slide_id=' + response.data.slide_id;
                    }
                } else {
                    SM.Utils.handleAjaxError(response);
                }
            });
        }
    };

    /**
     * Gestionnaire des variables de retour (output variables)
     */
    SM.OutputVariables = {
        init: function() {
            var self = this;

            // Ajouter une ligne
            $(document).on('click', '#sm-add-output-var', function() {
                self.addRow();
            });

            // Supprimer une ligne
            $(document).on('click', '.sm-output-var-remove', function() {
                $(this).closest('.sm-output-var-row').remove();
            });

            // Toggle champ valeurs enum + indicateur type + placeholder description
            $(document).on('change', '.sm-output-var-type', function() {
                var $row = $(this).closest('.sm-output-var-row');
                var $wrap = $(this).closest('.sm-output-var-type-wrap');
                var type = $(this).val();
                var $values = $row.find('.sm-output-var-values');
                var $desc = $row.find('.sm-output-var-desc');

                // Enum values field
                if (type === 'enum') {
                    $values.show();
                } else {
                    $values.hide().val('');
                }

                // Type hint badge
                $wrap.find('.sm-output-var-type-hint').remove();
                var hints = {
                    booleen: slidemindAdmin.strings.hintBoolean,
                    nombre: slidemindAdmin.strings.hintNumber
                };
                if (hints[type]) {
                    $wrap.append('<span class="sm-output-var-type-hint">' + hints[type] + '</span>');
                }

                // Placeholder contextuel
                var placeholders = {
                    texte: slidemindAdmin.strings.placeholderDescOptional,
                    nombre: slidemindAdmin.strings.placeholderDescNumber,
                    booleen: slidemindAdmin.strings.placeholderDescBoolean,
                    enum: slidemindAdmin.strings.placeholderDescOptional
                };
                $desc.attr('placeholder', placeholders[type] || slidemindAdmin.strings.placeholderDescOptional);
            });

            // Force lowercase sur le nom
            $(document).on('input', '.sm-output-var-name', function() {
                var val = $(this).val().toLowerCase().replace(/[^a-z0-9_]/g, '');
                $(this).val(val);
            });
        },

        addRow: function() {
            var html = '<div class="sm-output-var-row">' +
                '<div class="sm-output-var-fields">' +
                '<input type="text" class="sm-form-input sm-output-var-name" placeholder="nom_variable" pattern="^[a-z][a-z0-9_]*$" title="' + slidemindAdmin.strings.outputVarNameTitle + '">' +
                '<div class="sm-output-var-type-wrap">' +
                '<select class="sm-form-select sm-output-var-type">' +
                '<option value="texte">' + slidemindAdmin.strings.typeText + '</option>' +
                '<option value="nombre">' + slidemindAdmin.strings.typeNumber + '</option>' +
                '<option value="booleen">' + slidemindAdmin.strings.typeBoolean + '</option>' +
                '<option value="enum">Enum</option>' +
                '</select>' +
                '</div>' +
                '<input type="text" class="sm-form-input sm-output-var-values" placeholder="val1, val2, val3" style="display:none;">' +
                '<input type="text" class="sm-form-input sm-output-var-desc" placeholder="' + slidemindAdmin.strings.placeholderDescOptional + '">' +
                '<button type="button" class="sm-btn sm-btn-sm sm-btn-danger sm-output-var-remove" title="' + slidemindAdmin.strings.buttonDelete + '">' +
                '<span class="dashicons dashicons-no-alt"></span>' +
                '</button>' +
                '</div>' +
                '</div>';
            $('#sm-output-vars-list').append(html);
        },

        loadFromArray: function(vars) {
            $('#sm-output-vars-list').empty();
            var self = this;
            vars.forEach(function(v) {
                self.addRow();
                var $row = $('#sm-output-vars-list .sm-output-var-row:last');
                $row.find('.sm-output-var-name').val(v.name || '');
                $row.find('.sm-output-var-type').val(v.type || 'texte').trigger('change');
                $row.find('.sm-output-var-desc').val(v.description || '');
                if (v.type === 'enum' && v.values) {
                    $row.find('.sm-output-var-values').val(v.values);
                }
            });
        },

        collect: function() {
            var variables = [];
            $('#sm-output-vars-list .sm-output-var-row').each(function() {
                var $row = $(this);
                var name = $row.find('.sm-output-var-name').val().trim();
                if (!name) return;

                var type = $row.find('.sm-output-var-type').val();
                var entry = {
                    name: name,
                    type: type,
                    description: $row.find('.sm-output-var-desc').val().trim()
                };

                if (type === 'enum') {
                    entry.values = $row.find('.sm-output-var-values').val().trim();
                }

                variables.push(entry);
            });
            return JSON.stringify(variables);
        },

        validate: function() {
            var names = [];
            var error = null;

            $('#sm-output-vars-list .sm-output-var-row').each(function() {
                var $row = $(this);
                var name = $row.find('.sm-output-var-name').val().trim();
                if (!name) return true; // continue

                // Format du nom
                if (!/^[a-z][a-z0-9_]*$/.test(name)) {
                    error = SM.Utils.sprintf(slidemindAdmin.strings.outputVarNameInvalid, name);
                    return false; // break
                }

                // Doublons
                if (names.indexOf(name) !== -1) {
                    error = SM.Utils.sprintf(slidemindAdmin.strings.outputVarDuplicate, name);
                    return false; // break
                }
                names.push(name);

                // Enum sans valeurs
                var type = $row.find('.sm-output-var-type').val();
                if (type === 'enum') {
                    var values = $row.find('.sm-output-var-values').val().trim();
                    if (!values) {
                        error = SM.Utils.sprintf(slidemindAdmin.strings.outputVarEnumRequired, name);
                        return false; // break
                    }
                }

                return true; // continue
            });

            return error;
        }
    };

    /**
     * Gestionnaire des variables mémoire persistante (memory variables repeater)
     */
    SM.MemoryVariables = {
        _centralDefs: null,

        init: function() {
            var self = this;

            // Load standalone variable definitions for autocomplete (slide-edit only)
            if ($('#sm-memory-vars-list').length) {
                SM.Utils.ajax('slidemind_get_memory_definitions', {}, function(response) {
                    if (response.success) {
                        self._centralDefs = response.data.definitions || [];
                    }
                });
            }

            // Add row
            $(document).on('click', '#sm-add-memory-var', function() {
                self.addRow();
                SM.CodeGenerator.update();
            });

            // Remove row
            $(document).on('click', '.sm-memory-var-remove', function() {
                $(this).closest('.sm-memory-var-row').remove();
                SM.CodeGenerator.update();
            });

            // Force lowercase on name + autocomplete suggestions
            $(document).on('input', '.sm-memory-var-name', function() {
                var val = $(this).val().toLowerCase().replace(/[^a-z0-9_]/g, '');
                $(this).val(val);
                self.showNameSuggestions($(this), val);
            });

            // Show suggestions on focus
            $(document).on('focus', '.sm-memory-var-name', function() {
                self.showNameSuggestions($(this), $(this).val());
            });

            // Hide suggestions on blur (with delay for click)
            $(document).on('blur', '.sm-memory-var-name', function() {
                var $row = $(this).closest('.sm-memory-var-row');
                setTimeout(function() { $row.find('.sm-name-suggestions').remove(); }, 200);
            });

            // Click suggestion
            $(document).on('mousedown', '.sm-name-suggestion-item', function(e) {
                e.preventDefault();
                var $item = $(this);
                var $input = $item.closest('.sm-memory-var-fields').find('.sm-memory-var-name');
                var def = $item.data('def');
                $input.val(def.name);
                $item.closest('.sm-name-suggestions').remove();
                SM.CodeGenerator.update();
            });

            // Update code generator on any change
            $(document).on('input change', '.sm-memory-var-name, .sm-memory-var-scope, .sm-memory-var-source', function() {
                SM.CodeGenerator.update();
            });

            // Dismiss suggestions when scope changes
            $(document).on('change', '.sm-memory-var-scope', function() {
                $(this).closest('.sm-memory-var-row').find('.sm-name-suggestions').remove();
            });

            // Auto-save interaction toggle
            $(document).on('change', '#sm-save-interaction', function() {
                $('#sm-autosave-suboptions').toggle($(this).is(':checked'));
            });

            // Scope hint update
            $(document).on('change', '#sm-save-interaction-scope, .sm-memory-var-scope', function() {
                self.updateScopeHint($(this));
            });

            // Init scope hints
            $('#sm-save-interaction-scope').each(function() {
                self.updateScopeHint($(this));
            });

            // Init scope constraints based on current hierarchy
            this.updateAllScopes();
        },

        addRow: function() {
            var s = slidemindAdmin.strings;
            var canScope = slidemindAdmin.proFeatures && slidemindAdmin.proFeatures.scope_course_module;
            var proTag = canScope ? '' : ' (Pro)';
            var html = '<div class="sm-memory-var-row">' +
                '<div class="sm-memory-var-fields">' +
                '<input type="text" class="sm-form-input sm-memory-var-name" placeholder="' + s.memVarNamePlaceholder + '" pattern="^[a-z][a-z0-9_]*$">' +
                '<select class="sm-form-select sm-memory-var-scope">' +
                '<option value="global">' + s.memVarScopeGlobal + '</option>' +
                '<option value="course">' + s.memVarScopeCourse + proTag + '</option>' +
                '<option value="module">' + s.memVarScopeModule + proTag + '</option>' +
                '</select>' +
                '<input type="text" class="sm-form-input sm-memory-var-source" placeholder="' + s.memVarSourcePlaceholder + '">' +
                '<button type="button" class="sm-btn sm-btn-sm sm-btn-danger sm-memory-var-remove" title="' + s.buttonDelete + '">' +
                '<span class="dashicons dashicons-no-alt"></span>' +
                '</button>' +
                '</div>' +
                '</div>';
            $('#sm-memory-vars-list').append(html);
            this.updateAllScopes();
        },

        collect: function() {
            var variables = [];
            $('#sm-memory-vars-list .sm-memory-var-row').each(function() {
                var $row = $(this);
                var name = $row.find('.sm-memory-var-name').val().trim();
                if (!name) return;

                variables.push({
                    name: name,
                    scope: $row.find('.sm-memory-var-scope').val(),
                    source: $row.find('.sm-memory-var-source').val().trim()
                });
            });
            return JSON.stringify(variables);
        },

        collectRaw: function() {
            var variables = [];
            $('#sm-memory-vars-list .sm-memory-var-row').each(function() {
                var $row = $(this);
                var name = $row.find('.sm-memory-var-name').val().trim();
                if (!name) return;

                variables.push({
                    name: name,
                    scope: $row.find('.sm-memory-var-scope').val(),
                    source: $row.find('.sm-memory-var-source').val().trim()
                });
            });
            return variables;
        },

        validate: function() {
            var names = [];
            var error = null;

            $('#sm-memory-vars-list .sm-memory-var-row').each(function() {
                var $row = $(this);
                var name = $row.find('.sm-memory-var-name').val().trim();
                if (!name) return true;

                if (!/^[a-z][a-z0-9_]*$/.test(name)) {
                    error = SM.Utils.sprintf(slidemindAdmin.strings.memVarNameInvalid, name);
                    return false;
                }

                if (names.indexOf(name) !== -1) {
                    error = SM.Utils.sprintf(slidemindAdmin.strings.memVarDuplicate, name);
                    return false;
                }
                names.push(name);

                var source = $row.find('.sm-memory-var-source').val().trim();
                if (!source) {
                    error = SM.Utils.sprintf(slidemindAdmin.strings.memVarSourceRequired, name);
                    return false;
                }

                return true;
            });

            return error;
        },

        updateScopeHint: function($select) {
            var scope = $select.val();
            var $hint = $select.siblings('.sm-scope-hint');
            if (!$hint.length) return;

            var hints = {
                global: slidemindAdmin.strings.memScopeHintGlobal,
                course: slidemindAdmin.strings.memScopeHintCourse,
                module: slidemindAdmin.strings.memScopeHintModule
            };
            $hint.text(hints[scope] || '');
        },

        /**
         * Updates scope option availability on all scope selects based on current hierarchy.
         */
        updateAllScopes: function() {
            var courseId = $('#sm-slide-course').val();
            var moduleId = $('#sm-slide-module').val();
            var hasCourse = !!courseId;
            var hasModule = !!moduleId;
            var self = this;

            // Auto-save scope
            this.applyScope($('#sm-save-interaction-scope'), hasModule, hasCourse);

            // Per-variable scopes
            $('.sm-memory-var-scope').each(function() {
                self.applyScope($(this), hasModule, hasCourse);
            });
        },

        /**
         * Enables/disables scope options on a single select based on hierarchy and plan.
         */
        applyScope: function($select, hasModule, hasCourse) {
            if (!$select.length) return;
            var canScope = slidemindAdmin.proFeatures && slidemindAdmin.proFeatures.scope_course_module;
            $select.find('option[value="module"]').prop('disabled', !hasModule || !canScope);
            $select.find('option[value="course"]').prop('disabled', !hasCourse || !canScope);
            // If currently selected option is now disabled, reset to global
            if ($select.find('option:selected').prop('disabled')) {
                $select.val('global').trigger('change');
            }
        },

        showNameSuggestions: function($input, val) {
            var $row = $input.closest('.sm-memory-var-row');
            $row.find('.sm-name-suggestions').remove();

            // Only show standalone variable suggestions when scope is "global"
            var scope = $input.closest('.sm-memory-var-fields').find('.sm-memory-var-scope').val();
            if (scope !== 'global') return;

            if (!this._centralDefs || this._centralDefs.length === 0) return;

            var matches = [];
            for (var i = 0; i < this._centralDefs.length; i++) {
                var def = this._centralDefs[i];
                if (!val || def.name.indexOf(val) === 0) {
                    matches.push(def);
                }
            }
            if (matches.length === 0) return;

            var $row = $input.closest('.sm-memory-var-row');
            $row.css('position', 'relative');
            // Remove any existing dropdown in this row
            $row.find('.sm-name-suggestions').remove();

            var html = '<div class="sm-name-suggestions" style="position:absolute;left:0;top:100%;z-index:100;background:#fff;border:1px solid #e5e7eb;border-radius:4px;box-shadow:0 4px 6px rgba(0,0,0,0.1);max-height:150px;overflow-y:auto;width:220px;margin-top:2px;">';
            for (var j = 0; j < matches.length; j++) {
                html += '<div class="sm-name-suggestion-item" style="padding:6px 10px;cursor:pointer;font-size:13px;" data-def=\'' + JSON.stringify(matches[j]).replace(/'/g, '&#39;') + '\'>';
                html += '<code>' + SM.Memory.escHtml(matches[j].name) + '</code>';
                if (matches[j].description) html += ' <small style="color:#6b7280;">— ' + SM.Memory.escHtml(matches[j].description) + '</small>';
                html += '</div>';
            }
            html += '</div>';

            $row.append(html);
        }
    };

    /**
     * Authoring tool variable name uniqueness checker
     */
    SM.EditorVars = {
        _cache: {},
        _currentSlideId: 0,
        _manuallyEdited: false,
        _debounceTimer: null,
        _$warning: null,

        init: function() {
            var self = this;
            var $form = $('#sm-slide-form');
            if (!$form.length) return;

            self._$warning = $('#sm-editor-var-duplicate-warning');
            self._currentSlideId = parseInt($form.find('input[name="id"]').val()) || 0;
            var isNew = !self._currentSlideId;

            // Auto-generate on title/number blur (new slides only)
            if (isNew) {
                $(document).on('blur', '#sm-slide-title, #sm-slide-number', function() {
                    if (self._manuallyEdited) return;
                    self._autoFill();
                });
            }

            // Track manual edits + duplicate check (debounced)
            $(document).on('input', '#sm-editor-input-var, #sm-editor-output-var', function() {
                self._manuallyEdited = true;
                clearTimeout(self._debounceTimer);
                self._debounceTimer = setTimeout(function() { self.checkAndWarn(); }, 300);
            });

            // Re-fetch siblings on course change
            $(document).on('change', '#sm-slide-course', function() {
                self.clearCache();
                self.checkAndWarn();
            });

            // Reset variable to default
            $(document).on('click', '.sm-editor-var-reset', function() {
                var title = $('#sm-slide-title').val();
                var number = $('#sm-slide-number').val();
                var names = SM.Utils.generateEditorVarNames(title, number);
                var target = $(this).data('target');
                var isInput = target.indexOf('input') !== -1;
                var val = names ? (isInput ? names.input : names.output) : (isInput ? 'userMessage' : 'aiResponse');
                $('#' + target).val(val);
                SM.CodeGenerator.update();
                self.checkAndWarn();
            });

            // Initial duplicate check for existing slides
            if (self._currentSlideId) {
                self.checkAndWarn();
            }
        },

        _autoFill: function() {
            var title = $('#sm-slide-title').val();
            var number = $('#sm-slide-number').val();
            var names = SM.Utils.generateEditorVarNames(title, number);
            if (!names) return;
            $('#sm-editor-input-var').val(names.input);
            $('#sm-editor-output-var').val(names.output);
            SM.CodeGenerator.update();
            this.checkAndWarn();
        },

        clearCache: function() {
            this._cache = {};
        },

        fetchSiblings: function(courseId, callback) {
            var self = this;
            if (self._cache[courseId]) {
                callback(self._cache[courseId]);
                return;
            }
            SM.Utils.ajax('slidemind_get_sibling_editor_vars', {
                course_id: courseId,
                exclude_slide_id: self._currentSlideId
            }, function(response) {
                if (response.success) {
                    self._cache[courseId] = response.data.siblings || [];
                    callback(self._cache[courseId]);
                }
            }, function() {
                callback([]);
            });
        },

        checkAndWarn: function() {
            var courseId = $('#sm-slide-course').val();
            var $w = this._$warning;
            if (!$w || !courseId) {
                if ($w) $w.hide();
                return;
            }
            var inputVar = $('#sm-editor-input-var').val();
            var outputVar = $('#sm-editor-output-var').val();

            this.fetchSiblings(courseId, function(siblings) {
                var warnings = [];
                siblings.forEach(function(s) {
                    if (inputVar && (inputVar === s.editor_input_var || inputVar === s.editor_output_var)) {
                        warnings.push(SM.Utils.sprintf(slidemindAdmin.strings.editorVarDuplicateWarning, inputVar, s.title));
                    }
                    if (outputVar && (outputVar === s.editor_input_var || outputVar === s.editor_output_var)) {
                        warnings.push(SM.Utils.sprintf(slidemindAdmin.strings.editorVarDuplicateWarning, outputVar, s.title));
                    }
                });
                if (warnings.length) {
                    $w.html('<span class="dashicons dashicons-warning" style="font-size: 16px; width: 16px; height: 16px; margin-right: 4px; vertical-align: middle;"></span> ' + warnings.join('<br>')).show();
                } else {
                    $w.hide();
                }
            });
        }
    };

    /**
     * Auto-save memory key uniqueness checker
     */
    SM.SaveKeys = {
        _currentSlideId: 0,
        _manuallyEdited: false,
        _debounceTimer: null,
        _$warning: null,

        init: function() {
            var self = this;
            var $form = $('#sm-slide-form');
            if (!$form.length) return;

            self._$warning = $('#sm-save-key-duplicate-warning');
            self._currentSlideId = parseInt($form.find('input[name="id"]').val()) || 0;
            var isNew = !self._currentSlideId;

            // Auto-generate on title/number blur (new slides only)
            if (isNew) {
                $(document).on('blur', '#sm-slide-title, #sm-slide-number', function() {
                    if (self._manuallyEdited) return;
                    self._autoFill();
                });
            }

            // Track manual edits + snake_case cleanup + duplicate check
            $(document).on('input', 'input[name="save_input_key"], input[name="save_response_key"]', function() {
                self._manuallyEdited = true;
                var $el = $(this);
                var cleaned = $el.val().toLowerCase().replace(/[^a-z0-9_]/g, '');
                if (cleaned !== $el.val()) $el.val(cleaned);
                clearTimeout(self._debounceTimer);
                self._debounceTimer = setTimeout(function() { self.checkAndWarn(); }, 300);
            });

            // Auto-fill when "Save exchange" checkbox toggled on
            $(document).on('change', '#sm-save-interaction', function() {
                if (!$(this).is(':checked')) return;
                if (self._manuallyEdited) return;
                var inputKey = $('input[name="save_input_key"]').val();
                var respKey = $('input[name="save_response_key"]').val();
                if (!inputKey || !respKey) {
                    self._autoFill();
                }
            });

            // Re-fetch siblings on course change
            $(document).on('change', '#sm-slide-course', function() {
                SM.EditorVars.clearCache();
                self.checkAndWarn();
            });

            // Re-check on scope change (no cache clear needed — filtering is client-side)
            $(document).on('change', '#sm-save-interaction-scope', function() {
                self.checkAndWarn();
            });

            // Initial check for existing slides
            if (self._currentSlideId) {
                self.checkAndWarn();
            }
        },

        _autoFill: function() {
            var title = $('#sm-slide-title').val();
            var number = $('#sm-slide-number').val();
            var names = SM.Utils.generateMemoryKeyNames(title, number);
            if (!names) return;
            $('input[name="save_input_key"]').val(names.input);
            $('input[name="save_response_key"]').val(names.response);
            this.checkAndWarn();
        },

        checkAndWarn: function() {
            var self = this;
            var courseId = $('#sm-slide-course').val();
            var $w = self._$warning;
            if (!$w) return;

            // Skip if auto-save is unchecked
            if (!$('#sm-save-interaction').is(':checked')) {
                $w.hide();
                return;
            }

            var inputKey = $('input[name="save_input_key"]').val();
            var respKey = $('input[name="save_response_key"]').val();
            var currentScope = $('#sm-save-interaction-scope').val();
            var warnings = [];

            // Cross-check vs capture variables on this slide (same scope)
            var captureVars = SM.MemoryVariables ? SM.MemoryVariables.collectRaw() : [];
            captureVars.forEach(function(v) {
                if (v.scope !== currentScope) return;
                if (inputKey && inputKey === v.name) {
                    warnings.push(SM.Utils.sprintf(slidemindAdmin.strings.saveKeyMemVarCollision, inputKey));
                }
                if (respKey && respKey === v.name) {
                    warnings.push(SM.Utils.sprintf(slidemindAdmin.strings.saveKeyMemVarCollision, respKey));
                }
            });

            // Cross-check vs sibling slides' save keys
            if (courseId) {
                SM.EditorVars.fetchSiblings(courseId, function(siblings) {
                    siblings.forEach(function(s) {
                        if (!s.save_input_key && !s.save_response_key) return;
                        var sScope = s.save_interaction_scope || 'global';
                        if (sScope !== currentScope) return;
                        if (inputKey && (inputKey === s.save_input_key || inputKey === s.save_response_key)) {
                            warnings.push(SM.Utils.sprintf(slidemindAdmin.strings.saveKeyDuplicateWarning, inputKey, s.title));
                        }
                        if (respKey && (respKey === s.save_input_key || respKey === s.save_response_key)) {
                            warnings.push(SM.Utils.sprintf(slidemindAdmin.strings.saveKeyDuplicateWarning, respKey, s.title));
                        }
                    });
                    self._showWarnings(warnings);
                });
            } else {
                self._showWarnings(warnings);
            }
        },

        _showWarnings: function(warnings) {
            var $w = this._$warning;
            if (!$w) return;
            if (warnings.length) {
                $w.html('<span class="dashicons dashicons-warning" style="font-size: 16px; width: 16px; height: 16px; margin-right: 4px; vertical-align: middle;"></span> ' + warnings.join('<br>')).show();
            } else {
                $w.hide();
            }
        }
    };

    /**
     * Génération dynamique du code d'intégration multi-outils auteurs
     *
     * Supporte : Storyline, Captivate, Lectora, DominKnow, iSpring. Le widget iframe
     * est utilisé automatiquement en mode Clavardage (jamais sélectionnable par l'utilisateur).
     */
    SM.CodeGenerator = {

        /* ── Registre des outils auteurs ───────────────────────── */

        editors: {
            storyline: {
                name: 'Articulate Storyline',
                instructions: slidemindAdmin.strings.codeInstructionsStoryline
            },
            captivate: {
                name: 'Adobe Captivate (beta)',
                instructions: slidemindAdmin.strings.codeInstructionsCaptivate
            },
            lectora: {
                name: 'Lectora (ELB Learning) (beta)',
                instructions: slidemindAdmin.strings.codeInstructionsLectora
            },
            dominknow: {
                name: 'DominKnow | Claro (beta)',
                instructions: slidemindAdmin.strings.codeInstructionsDominknow
            },
            ispring: {
                name: 'iSpring Suite (beta)',
                instructions: slidemindAdmin.strings.codeInstructionsIspring
            },
            widget: {
                name: slidemindAdmin.strings.codeEditorWidget,
                forceWidget: true,
                instructions: slidemindAdmin.strings.codeInstructionsWidget
            }
        },

        currentEditor: 'storyline',
        currentWidgetMode: 'chat',
        betaEditors: ['captivate', 'lectora', 'dominknow', 'ispring'],

        /* ── Filter dropdown options based on widget mode ─────── */

        filterEditorOptionsByMode: function(mode) {
            var $select = $('#sm-editor-select');
            if (!$select.length) return;

            // Visibility matrix:
            //   chat    → authoring=hidden, embed=visible (default widget)
            //   qa      → authoring=visible, embed=visible
            //   capture → authoring=visible, embed=hidden (no UI to embed)
            var showAuthoring = (mode !== 'chat');
            var showEmbed     = (mode !== 'capture');

            $select.find('optgroup[data-sm-group="authoring"]').toggle(showAuthoring);
            $select.find('optgroup[data-sm-group="embed"]').toggle(showEmbed);

            // If the current selection is now hidden, fall back to a valid option.
            var currentVal = $select.val();
            var embedKeys = ['widget', 'shortcode'];
            var authoringKeys = ['storyline', 'captivate', 'lectora', 'dominknow', 'ispring'];

            var isEmbed = embedKeys.indexOf(currentVal) !== -1;
            var isAuthoring = authoringKeys.indexOf(currentVal) !== -1;

            if ((isEmbed && !showEmbed) || (isAuthoring && !showAuthoring)) {
                var fallback;
                if (mode === 'chat') {
                    fallback = 'widget';
                } else {
                    var defaultEditor = (typeof slidemindAdmin !== 'undefined'
                        && slidemindAdmin.defaults
                        && slidemindAdmin.defaults.editor) || 'storyline';
                    if (authoringKeys.indexOf(defaultEditor) === -1) defaultEditor = 'storyline';
                    fallback = defaultEditor;
                }
                $select.val(fallback);
                this.currentEditor = fallback;
            }
        },

        /* ── Initialisation (page slide-edit) ──────────────────── */

        init: function() {
            var $card = $('#sm-integration-code-card');
            if (!$card.length) return;

            this.configId = $card.data('config-id');
            this.restUrl  = $card.data('rest-url');
            this.siteUrl  = $card.data('site-url');
            this.$pre     = $('#sm-integration-code-pre');
            this.$instructions = $card.find('.sm-editor-instructions');
            this.$betaWarning  = $('#sm-beta-warning');

            // Read widget mode from data attribute (set by slide-edit.php)
            this.currentWidgetMode = $card.data('widget-mode') || 'qa';

            // Resolve the default selected option based on mode.
            var defaultEditor = (typeof slidemindAdmin !== 'undefined' && slidemindAdmin.defaults && slidemindAdmin.defaults.editor) || 'storyline';
            var authoringKeys = ['storyline', 'captivate', 'lectora', 'dominknow', 'ispring'];

            if (this.currentWidgetMode === 'chat') {
                this.currentEditor = 'widget';
            } else {
                if (authoringKeys.indexOf(defaultEditor) === -1) defaultEditor = 'storyline';
                this.currentEditor = defaultEditor;
            }
            $('#sm-editor-select').val(this.currentEditor);

            // Apply mode-based option visibility.
            this.filterEditorOptionsByMode(this.currentWidgetMode);

            this.update();

            var self = this;

            // Changement d'outil auteur
            $(document).on('change', '#sm-editor-select', function() {
                self.currentEditor = $(this).val();
                var isEmbed = (self.currentEditor === 'widget' || self.currentEditor === 'shortcode');
                $('#sm-editor-var-names').toggle(!isEmbed);
                self.update();
            });

            // Changement de noms de variables d'outil auteur
            $(document).on('input', '#sm-editor-input-var, #sm-editor-output-var', function() {
                self.update();
            });
        },

        /* ── Bootstrap loader (external sm-slide.js) ────────── */

        _buildBootstrap: function(config) {
            var lines = [];
            lines.push("var s = document.createElement('script');");
            lines.push("s.src = '" + slidemindAdmin.pluginUrl + "public/js/sm-slide.js?v=" + slidemindAdmin.version + "';");
            lines.push("s.onload = function() {");
            lines.push("    SlideMind.run({");

            // Build config properties
            var props = [];
            props.push('        configId:  "' + config.configId + '"');
            props.push('        baseUrl:   "' + config.baseUrl + '"');
            props.push('        mode:      "' + config.mode + '"');
            props.push('        authoringTool: "' + (config.authoringTool || config.editor) + '"');

            if (config.mode === 'qa') {
                props.push('        inputVar:  "' + (config.inputVar || 'userMessage') + '"');
                props.push('        outputVar: "' + (config.outputVar || 'aiResponse') + '"');
            }

            if (config.memoryVars && config.memoryVars.length) {
                var memLines = [];
                for (var i = 0; i < config.memoryVars.length; i++) {
                    var v = config.memoryVars[i];
                    var parts = [];
                    parts.push('name: "' + v.name + '"');
                    parts.push('source: "' + v.source + '"');
                    if (v.scope) parts.push('scope: "' + v.scope + '"');
                    memLines.push('            { ' + parts.join(', ') + ' }');
                }
                props.push('        memoryVars: [\n' + memLines.join(',\n') + '\n        ]');
            }

            lines.push(props.join(',\n'));
            lines.push("    });");
            lines.push("};");
            lines.push("document.head.appendChild(s);");
            return lines.join('\n');
        },

        /* ── Embed widget (iframe) ─────────────────────────────── */

        buildWidgetEmbed: function(configId, siteUrl, mode) {
            var widgetUrl = siteUrl + '?slidemind_widget=1'
                + '&config=' + configId;

            // Widget appearance params from hidden inputs
            var f = SM.WidgetAppearance.fields;
            var color      = $('input[name="' + f.color + '"]').val() || '';
            var aiColor    = $('input[name="' + f.aiColor + '"]').val() || '';
            var theme      = $('input[name="' + f.theme + '"]').val() || '';
            var showHeader = $('input[name="' + f.showHeader + '"]').val() || '';
            var aiTextColor = $('input[name="' + f.aiTextColor + '"]').val() || '';
            var radius     = $('input[name="' + f.borderRadius + '"]').val() || '';

            if (color)   widgetUrl += '&color=' + encodeURIComponent(color);
            if (aiColor) widgetUrl += '&ai_color=' + encodeURIComponent(aiColor);
            if (theme && theme !== 'light') widgetUrl += '&theme=' + encodeURIComponent(theme);
            if (showHeader === '0') widgetUrl += '&header=0';
            var widgetTitle = $('input[name="' + f.title + '"]').val() || '';
            if (widgetTitle) widgetUrl += '&title=' + encodeURIComponent(widgetTitle);
            if (aiTextColor) widgetUrl += '&ai_text_color=' + encodeURIComponent(aiTextColor);
            if (radius) widgetUrl += '&radius=' + encodeURIComponent(radius);

            var lines = [];
            lines.push('<!-- SlideMind Widget -->');
            lines.push('<iframe');
            lines.push('    src="' + widgetUrl + '"');
            lines.push('    width="400"');
            lines.push('    height="600"');
            lines.push('    frameborder="0"');
            lines.push('    style="border: none; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);"');
            lines.push('    allow="clipboard-write"');
            lines.push('></iframe>');

            return lines.join('\n');
        },

        /* ── Embed shortcode ([slidemind_embed]) ─────────────── */

        buildShortcodeEmbed: function(configId, siteUrl, mode) {
            var widgetUrl = siteUrl + '?slidemind_widget=1'
                + '&config=' + configId;

            var f = SM.WidgetAppearance.fields;
            var color       = $('input[name="' + f.color + '"]').val() || '';
            var aiColor     = $('input[name="' + f.aiColor + '"]').val() || '';
            var theme       = $('input[name="' + f.theme + '"]').val() || '';
            var showHeader  = $('input[name="' + f.showHeader + '"]').val() || '';
            var aiTextColor = $('input[name="' + f.aiTextColor + '"]').val() || '';
            var radius      = $('input[name="' + f.borderRadius + '"]').val() || '';
            var widgetTitle = $('input[name="' + f.title + '"]').val() || '';

            if (color)   widgetUrl += '&color=' + encodeURIComponent(color);
            if (aiColor) widgetUrl += '&ai_color=' + encodeURIComponent(aiColor);
            if (theme && theme !== 'light') widgetUrl += '&theme=' + encodeURIComponent(theme);
            if (showHeader === '0') widgetUrl += '&header=0';
            if (widgetTitle) widgetUrl += '&title=' + encodeURIComponent(widgetTitle);
            if (aiTextColor) widgetUrl += '&ai_text_color=' + encodeURIComponent(aiTextColor);
            if (radius) widgetUrl += '&radius=' + encodeURIComponent(radius);

            return '[slidemind_embed url="' + widgetUrl + '" width="400" height="600"]';
        },

        /* ── Génération pour la liste des slides ───────────────── */

        buildForList: function(params) {
            var authoringTool = params.authoringTool || params.editor || 'storyline';
            var configUid  = params.configUid;
            var restUrl    = params.restUrl;
            var siteUrl    = params.siteUrl;
            var widgetMode = params.widgetMode || 'qa';
            var inputVar   = params.inputVar || 'userMessage';
            var outputVar  = params.outputVar || 'aiResponse';
            var memoryVars = params.memoryVars || [];

            // Chat mode → force widget embed
            if (widgetMode === 'chat') {
                return this.buildWidgetEmbed(configUid, siteUrl, widgetMode);
            }

            // Capture mode
            if (widgetMode === 'capture') {
                return this._buildBootstrap({
                    configId:   configUid,
                    baseUrl:    restUrl,
                    mode:       'capture',
                    authoringTool: authoringTool,
                    memoryVars: memoryVars
                });
            }

            // Q&A mode
            return this._buildBootstrap({
                configId:   configUid,
                baseUrl:    restUrl,
                mode:       'qa',
                authoringTool: authoringTool,
                inputVar:   inputVar,
                outputVar:  outputVar,
                memoryVars: memoryVars
            });
        },

        /* ── Mise à jour (page slide-edit) ─────────────────────── */

        update: function() {
            if (!this.$pre) return;
            this.inputVar = $('#sm-editor-input-var').val() || 'userMessage';
            this.outputVar = $('#sm-editor-output-var').val() || 'aiResponse';
            var isCapture = this.currentWidgetMode === 'capture';
            var isWidget = this.currentEditor === 'widget';
            var isShortcode = this.currentEditor === 'shortcode';
            var memVars = SM.MemoryVariables ? SM.MemoryVariables.collectRaw() : [];
            var code;

            if (isWidget) {
                code = this.buildWidgetEmbed(this.configId, this.siteUrl, this.currentWidgetMode);
            } else if (isShortcode) {
                code = this.buildShortcodeEmbed(this.configId, this.siteUrl, this.currentWidgetMode);
            } else if (isCapture) {
                code = this._buildBootstrap({
                    configId:   this.configId,
                    baseUrl:    this.restUrl,
                    mode:       'capture',
                    authoringTool: this.currentEditor,
                    memoryVars: memVars
                });
            } else {
                code = this._buildBootstrap({
                    configId:   this.configId,
                    baseUrl:    this.restUrl,
                    mode:       'qa',
                    authoringTool: this.currentEditor,
                    inputVar:   this.inputVar,
                    outputVar:  this.outputVar,
                    memoryVars: memVars
                });
            }

            this.$pre.text(code);

            $('#sm-customize-widget-btn').toggle(isWidget);
            if (this.$instructions) {
                var editor = this.editors[this.currentEditor];
                this.$instructions.html(editor ? editor.instructions : '');
            }
            if (this.$betaWarning) {
                var isBeta = this.betaEditors.indexOf(this.currentEditor) !== -1;
                this.$betaWarning.toggle(isBeta);
                if (isBeta) this.$betaWarning.find('span:last').html(slidemindAdmin.strings.codeBetaWarning);
            }
        }
    };

    /**
     * Widget Appearance Modal
     */
    SM.WidgetAppearance = {
        defaults: { color: '#4f46e5', aiColor: '#f1f5f9', theme: 'light', showHeader: '1', aiTextColor: '', borderRadius: 12 },

        // Field name map: JS key → hidden input name
        fields: {
            color: 'widget_color', aiColor: 'widget_ai_color', theme: 'widget_theme',
            showHeader: 'widget_show_header', title: 'widget_title',
            aiTextColor: 'widget_ai_text_color', borderRadius: 'widget_border_radius'
        },

        // Color picker configs: [inputId, hexId, defaultKey]
        _colorFields: [
            ['sma-color', 'sma-color-hex', 'color'],
            ['sma-ai-color', 'sma-ai-color-hex', 'aiColor'],
            ['sma-ai-text-color', 'sma-ai-text-color-hex', 'aiTextColor']
        ],

        _rafId: null,

        _setColor: function(inputId, hexId, value) {
            $('#' + inputId).val(value);
            $('#' + hexId).text(value);
        },

        _schedulePreview: function() {
            if (!this._rafId) {
                var self = this;
                this._rafId = requestAnimationFrame(function() {
                    self._rafId = null;
                    self.updatePreview();
                });
            }
        },

        _getHiddenVal: function(key) {
            return $('input[name="' + this.fields[key] + '"]').val();
        },

        _setHiddenVal: function(key, value) {
            $('input[name="' + this.fields[key] + '"]').val(value);
        },

        _colorDefault: function(key) {
            return this.defaults[key] || (key === 'aiTextColor' ? '#1e293b' : '');
        },

        init: function() {
            var self = this;

            // Use settings defaults if available
            if (window.smaDefaults) {
                self.defaults = {
                    color: smaDefaults.color || '#4f46e5',
                    aiColor: smaDefaults.aiColor || '#f1f5f9',
                    theme: smaDefaults.theme || 'light',
                    showHeader: smaDefaults.showHeader || '1',
                    aiTextColor: smaDefaults.aiTextColor || '',
                    borderRadius: smaDefaults.borderRadius || 12
                };
            }

            // Open modal from slide-edit or wizard
            $(document).on('click', '#sm-customize-widget-btn, #wizard-customize-widget-btn', function() {
                self.loadCurrentValues();
                SM.Modal.open('sm-widget-appearance-modal');
            });

            // Live preview: color pickers (data-driven)
            $.each(self._colorFields, function(_, f) {
                $(document).on('input', '#' + f[0], function() {
                    $('#' + f[1]).text($(this).val());
                    self._schedulePreview();
                });
                $(document).on('click', '#sma-reset-' + f[0].replace('sma-', ''), function() {
                    self._setColor(f[0], f[1], self._colorDefault(f[2]));
                    self.updatePreview();
                });
            });

            // Live preview: theme, header, radius, title
            $(document).on('change', '#sma-theme', function() { self.updatePreview(); });
            $(document).on('change', '#sma-show-header', function() {
                $('#sma-title-group').toggle($(this).is(':checked'));
                self.updatePreview();
            });
            $(document).on('input', '#sma-border-radius', function() { self._schedulePreview(); });
            $(document).on('input', '#sma-title', function() { self._schedulePreview(); });

            // Reset all
            $(document).on('click', '#sma-reset-all', function() {
                $.each(self._colorFields, function(_, f) {
                    self._setColor(f[0], f[1], self._colorDefault(f[2]));
                });
                $('#sma-theme').val(self.defaults.theme);
                $('#sma-show-header').prop('checked', self.defaults.showHeader !== '0');
                $('#sma-title-group').toggle(self.defaults.showHeader !== '0');
                $('#sma-title').val('');
                $('#sma-border-radius').val(self.defaults.borderRadius);
                self.updatePreview();
            });

            // Apply
            $(document).on('click', '#sma-apply', function() {
                var color      = $('#sma-color').val();
                var aiColor    = $('#sma-ai-color').val();
                var theme      = $('#sma-theme').val();
                var showHeader  = $('#sma-show-header').is(':checked') ? '1' : '0';
                var widgetTitle = $('#sma-title').val().trim();
                var aiTextColor = $('#sma-ai-text-color').val();
                var radius      = parseInt($('#sma-border-radius').val()) || 12;

                // Clear values if they match defaults (= use global)
                self._setHiddenVal('color', color === self.defaults.color ? '' : color);
                self._setHiddenVal('aiColor', aiColor === self.defaults.aiColor ? '' : aiColor);
                self._setHiddenVal('theme', theme === self.defaults.theme ? '' : theme);
                self._setHiddenVal('showHeader', showHeader === self.defaults.showHeader ? '' : showHeader);
                self._setHiddenVal('title', widgetTitle);
                self._setHiddenVal('aiTextColor', aiTextColor === self._colorDefault('aiTextColor') ? '' : aiTextColor);
                self._setHiddenVal('borderRadius', radius === self.defaults.borderRadius ? '' : radius);

                SM.Modal.close();

                // Refresh code generator (works for both slide-edit and wizard)
                if (typeof SM.Wizard !== 'undefined' && SM.Wizard._codeConfigUid) {
                    SM.Wizard.showCodeGenerator();
                } else {
                    SM.CodeGenerator.update();
                }
            });

            // Hex display update on settings page
            $(document).on('input', '#sm-default-widget-color', function() {
                $('#sm-default-widget-color-hex').text($(this).val());
            });
            $(document).on('input', '#sm-default-widget-ai-color', function() {
                $('#sm-default-widget-ai-color-hex').text($(this).val());
            });
            $(document).on('input', '#sm-default-widget-ai-text-color', function() {
                $('#sm-default-widget-ai-text-color-hex').text($(this).val());
            });
        },

        loadCurrentValues: function() {
            var savedColor      = this._getHiddenVal('color');
            var savedAiColor    = this._getHiddenVal('aiColor');
            var savedTheme      = this._getHiddenVal('theme');
            var savedShowHeader = this._getHiddenVal('showHeader');
            var savedTitle      = this._getHiddenVal('title');
            var savedAiText     = this._getHiddenVal('aiTextColor');
            var savedRadius     = this._getHiddenVal('borderRadius');

            this._setColor('sma-color', 'sma-color-hex', savedColor || this.defaults.color);
            this._setColor('sma-ai-color', 'sma-ai-color-hex', savedAiColor || this.defaults.aiColor);
            this._setColor('sma-ai-text-color', 'sma-ai-text-color-hex', savedAiText || this._colorDefault('aiTextColor'));
            $('#sma-theme').val(savedTheme || this.defaults.theme);
            var headerChecked = savedShowHeader ? savedShowHeader !== '0' : this.defaults.showHeader !== '0';
            $('#sma-show-header').prop('checked', headerChecked);
            $('#sma-title-group').toggle(headerChecked);
            $('#sma-title').val(savedTitle || '');
            $('#sma-border-radius').val(savedRadius || this.defaults.borderRadius);

            this.updatePreview();
        },

        updatePreview: function() {
            var color      = $('#sma-color').val() || this.defaults.color;
            var aiColor    = $('#sma-ai-color').val() || this.defaults.aiColor;
            var theme      = $('#sma-theme').val() || this.defaults.theme;
            var showHeader = $('#sma-show-header').is(':checked');
            var aiTextColor = $('#sma-ai-text-color').val() || (theme === 'dark' ? '#e2e8f0' : '#1e293b');
            var radius     = parseInt($('#sma-border-radius').val()) || 12;
            var isDark     = theme === 'dark';
            var bg         = isDark ? '#0f172a' : '#ffffff';
            var border     = isDark ? '#334155' : '#e2e8f0';
            var inputBg    = isDark ? '#1e293b' : '#ffffff';
            var smallRadius = Math.min(radius, 8) + 'px';

            var $preview = $('#sma-preview');
            $preview.attr('data-theme', theme).css({
                'border-radius': radius + 'px',
                'background': bg,
                'border-color': border
            });

            // Header
            var customTitle = $('#sma-title').val().trim();
            $('#sma-preview-header').css('background', color).toggle(showHeader)
                .text(customTitle || 'SlideMind');

            // Bubbles
            $('#sma-preview-ai-bubble').css({
                'background': aiColor,
                'color': aiTextColor,
                'border-radius': radius + 'px ' + radius + 'px ' + radius + 'px 3px'
            });
            $('#sma-preview-user-bubble').css({
                'background': color,
                'border-radius': radius + 'px ' + radius + 'px 3px ' + radius + 'px'
            });

            // Input area
            var $inputArea = $('#sma-preview-input');
            $inputArea.css({ 'background': bg, 'border-color': border });
            $inputArea.find('div:first').css({
                'background': inputBg,
                'border-color': border,
                'color': '#94a3b8',
                'border-radius': smallRadius
            });

            // Send button + body
            $('#sma-preview-send').css({ 'background': color, 'border-radius': smallRadius });
            $('#sma-preview-body').css('background', bg);
        }
    };

    /**
     * Gestionnaire des templates
     */
    SM.Templates = {
        init: function() {
            var self = this;

            // Suppression
            $(document).on('click', '.sm-delete-template', function(e) {
                e.preventDefault();

                if (!confirm(slidemindAdmin.strings.confirmDeleteTemplate)) {
                    return;
                }

                var $btn = $(this);
                var templateId = $btn.data('template-id');

                SM.Utils.ajax('slidemind_delete_template', { template_id: templateId }, function(response) {
                    if (response.success) {
                        // Si on est dans la liste, supprime la ligne
                        var $row = $btn.closest('tr');
                        if ($row.length) {
                            $row.fadeOut(300, function() {
                                $(this).remove();
                            });
                        } else {
                            // Si on est dans l'édition, redirige
                            window.location.href = 'admin.php?page=slidemind-templates';
                        }
                        SM.Utils.showToast(response.data.message, 'success');
                    } else {
                        SM.Utils.showToast(response.data.message, 'error');
                    }
                });
            });

            // Sauvegarde
            $(document).on('submit', '#sm-template-form', function(e) {
                e.preventDefault();
                self.save();
            });

            // Export
            $(document).on('click', '.sm-export-template', function(e) {
                e.preventDefault();
                self.exportTemplate($(this).data('template-id'));
            });

            // Import
            $(document).on('click', '#sm-import-btn', function(e) {
                e.preventDefault();
                self.importTemplate();
            });

            // Reset default
            $(document).on('click', '.sm-reset-default-template', function(e) {
                e.preventDefault();
                var slug = $(this).data('slug');
                if (!confirm(slidemindAdmin.strings.confirmResetTemplate)) {
                    return;
                }

                SM.Utils.ajax('slidemind_reset_default_template', { slug: slug }, function(response) {
                    if (response.success) {
                        SM.Utils.showToast(slidemindAdmin.strings.templateReset, 'success');
                        setTimeout(function() {
                            location.reload();
                        }, 800);
                    } else {
                        SM.Utils.showToast(response.data.message, 'error');
                    }
                });
            });

            // Favorite toggle
            $(document).on('click', '.sm-favorite-toggle', function() {
                var $btn = $(this);
                var slug = $btn.data('slug');

                SM.Utils.ajax('slidemind_toggle_favorite_template', { slug: slug }, function(response) {
                    if (response.success) {
                        var isFav = response.data.is_favorite;
                        $btn.toggleClass('is-favorite', isFav)
                            .attr('title', isFav ? slidemindAdmin.strings.removeFavorite : slidemindAdmin.strings.addFavorite)
                            .find('.dashicons')
                            .removeClass('dashicons-star-filled dashicons-star-empty')
                            .addClass(isFav ? 'dashicons-star-filled' : 'dashicons-star-empty');
                        slidemindAdmin.favoriteTemplates = response.data.favorites;
                        SM.Utils.showToast(isFav ? slidemindAdmin.strings.addedToFavorites : slidemindAdmin.strings.removedFromFavorites, 'success');
                    } else {
                        SM.Utils.showToast(response.data.message, 'error');
                    }
                });
            });

            // Détection des variables dans le prompt
            $(document).on('input', '#sm-template-prompt', function() {
                self.detectVariables();
            });

            // Temperature slider
            $(document).on('input', '#sm-template-temperature', function() {
                $('#sm-template-temp-value').text($(this).val());
            });

            // Widget mode toggle in template-edit
            $(document).on('change', '#sm-template-widget-mode', function() {
                var mode = $(this).val();
                $('#sm-template-chat-options').toggle(mode === 'chat');
                $('#sm-template-output-vars-help').toggle(mode === 'qa');
            });
            // Initial toggle on page load
            $('#sm-template-widget-mode').trigger('change');

            // Détection initiale si on est en mode édition
            if ($('#sm-template-prompt').length && $('#sm-template-prompt').val()) {
                self.detectVariables();
            }

            // Emoji picker
            SM.EmojiPicker.init();
        },

        save: function() {
            var $form = $('#sm-template-form');
            var $btn = $form.find('[type="submit"]');
            var data = $form.serializeArray();
            var template = {};

            data.forEach(function(item) {
                template[item.name] = item.value;
            });

            // Collecte les variables détectées
            template.variables = this.getDetectedVariables().join(',');

            if (typeof template.opening_message === 'undefined') {
                template.opening_message = '';
            }
            // Ensure unchecked checkbox sends '0'
            if (!template.graceful_close) {
                template.graceful_close = '0';
            }

            $btn.prop('disabled', true).text(slidemindAdmin.strings.saving);

            SM.Utils.ajax('slidemind_save_template', { template: template }, function(response) {
                $btn.prop('disabled', false).html('<span class="dashicons dashicons-saved"></span> ' + (template.id ? slidemindAdmin.strings.updateButton : slidemindAdmin.strings.createTemplate));

                if (response.success) {
                    SM.Utils.showToast(response.data.message, 'success');
                    SM.Autocomplete.invalidateCache();

                    // Redirige si nouveau template
                    if (!template.id && response.data.template_id) {
                        window.location.href = 'admin.php?page=slidemind-template-edit&template_id=' + response.data.template_id;
                    }
                } else {
                    SM.Utils.handleAjaxError(response);
                }
            });
        },

        exportTemplate: function(id) {
            SM.Utils.ajax('slidemind_export_template', { template_id: id }, function(response) {
                if (response.success) {
                    // Télécharge le fichier
                    var blob = new Blob([response.data.json], { type: 'application/json' });
                    var url = URL.createObjectURL(blob);
                    var a = document.createElement('a');
                    a.href = url;
                    a.download = response.data.filename;
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);

                    SM.Utils.showToast(slidemindAdmin.strings.templateExported, 'success');
                } else {
                    SM.Utils.showToast(response.data.message, 'error');
                }
            });
        },

        importTemplate: function() {
            var json = $('#sm-import-json').val().trim();

            if (!json) {
                SM.Utils.showToast(slidemindAdmin.strings.pasteJsonContent, 'error');
                return;
            }

            // Validation JSON côté client
            try {
                JSON.parse(json);
            } catch (e) {
                SM.Utils.showToast(SM.Utils.sprintf(slidemindAdmin.strings.invalidJson, e.message), 'error');
                return;
            }

            var $btn = $('#sm-import-btn');
            $btn.prop('disabled', true).text(slidemindAdmin.strings.importing);

            SM.Utils.ajax('slidemind_import_template', { json: json }, function(response) {
                $btn.prop('disabled', false).html('<span class="dashicons dashicons-upload"></span> ' + slidemindAdmin.strings.importButton);

                if (response.success) {
                    SM.Utils.showToast(slidemindAdmin.strings.templateImported, 'success');
                    SM.Modal.close();

                    setTimeout(function() {
                        location.reload();
                    }, 800);
                } else {
                    SM.Utils.handleAjaxError(response);
                }
            });
        },

        detectVariables: function() {
            var prompt = $('#sm-template-prompt').val() || '';
            var regex = /\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g;
            var matches = [];
            var match;
            var systemVars = ['course_theme', 'course_objectives', 'course_content', 'module_theme', 'module_objectives', 'module_content'];

            while ((match = regex.exec(prompt)) !== null) {
                if (matches.indexOf(match[1]) === -1) {
                    matches.push(match[1]);
                }
            }

            var $container = $('#sm-template-variables-list');
            var $noVars = $('#sm-template-no-variables');

            // Sépare système vs custom
            var systemFound = matches.filter(function(v) { return systemVars.indexOf(v) !== -1; });
            var customFound = matches.filter(function(v) { return systemVars.indexOf(v) === -1; });

            $container.find('.sm-variable-group').remove();

            if (matches.length === 0) {
                $noVars.show();
            } else {
                $noVars.hide();

                var html = '';
                if (systemFound.length > 0) {
                    html += '<div class="sm-variable-group" style="margin-bottom: 8px;"><strong style="font-size: 12px; color: #6b7280;">' + slidemindAdmin.strings.pgVarGroupSystem + ':</strong> ';
                    systemFound.forEach(function(v) {
                        html += '<code style="margin: 2px 4px; font-size: 11px; padding: 2px 6px; background: #e5e7eb; border-radius: 4px;">{{' + v + '}}</code>';
                    });
                    html += '</div>';
                }
                if (customFound.length > 0) {
                    html += '<div class="sm-variable-group"><strong style="font-size: 12px; color: #6b7280;">' + slidemindAdmin.strings.pgVarGroupCustom + ':</strong> ';
                    customFound.forEach(function(v) {
                        html += '<code style="margin: 2px 4px; font-size: 11px; padding: 2px 6px; background: #dbeafe; border-radius: 4px; color: #1d4ed8;">{{' + v + '}}</code>';
                    });
                    html += '</div>';
                }

                $container.prepend(html);
            }

            // Met à jour le champ hidden avec les variables custom uniquement
            $('#sm-template-variables-hidden').val(customFound.join(','));
        },

        getDetectedVariables: function() {
            var val = $('#sm-template-variables-hidden').val();
            if (!val) return [];
            return val.split(',').filter(function(v) { return v.trim() !== ''; });
        }
    };

    /**
     * Memory hub: definitions CRUD + learner data
     */
    SM.Memory = {
        currentPage: 1,
        searchTimer: null,
        definitions: [],

        init: function() {
            var self = this;

            // Hide search field on Definitions tab (default active tab)
            var activeTab = $('.sm-tabs .sm-tab.active').data('tab');
            if (activeTab !== 'learners') {
                $('#sm-learners-search').hide();
            }

            // ── Definitions tab ──────────────────────────────────
            if ($('#sm-definitions-tbody').length) {
                self.loadDefinitions();
                self.loadSlideDefinitions();
                self.loadProfileFields();

                // Add definition
                $(document).on('click', '#sm-add-definition', function() {
                    $('#sm-def-id').val('');
                    $('#sm-def-name').val('').prop('disabled', false);
                    $('#sm-def-description').val('');
                    $('#sm-definition-modal .sm-modal-header h3').text(slidemindAdmin.strings.memDefAddVariable || 'Add a variable');
                    SM.Modal.open('sm-definition-modal');
                });

                // Save definition
                $(document).on('click', '#sm-save-definition', function() {
                    self.saveDefinition();
                });

                // Edit definition (pencil icon in row)
                $(document).on('click', '.sm-edit-definition', function(e) {
                    e.preventDefault();
                    var $btn = $(this);
                    var id   = $btn.data('def-id');
                    var def = null;
                    for (var i = 0; i < self.definitions.length; i++) {
                        if (self.definitions[i].id === id) { def = self.definitions[i]; break; }
                    }
                    if (!def) return;

                    $('#sm-def-id').val(def.id);
                    $('#sm-def-name').val(def.name).prop('disabled', true);
                    $('#sm-def-description').val(def.description);
                    $('#sm-definition-modal .sm-modal-header h3').text(slidemindAdmin.strings.buttonEdit || 'Edit');
                    SM.Modal.open('sm-definition-modal');
                });

                // Select all checkbox
                $(document).on('change', '#sm-select-all-defs', function() {
                    var checked = $(this).is(':checked');
                    $('#sm-definitions-tbody input.sm-def-check').prop('checked', checked);
                    self.updateBulkBar();
                });

                // Individual checkbox
                $(document).on('change', '#sm-definitions-tbody input.sm-def-check', function() {
                    self.updateBulkBar();
                    // Sync select-all state
                    var total = $('#sm-definitions-tbody input.sm-def-check').length;
                    var checked = $('#sm-definitions-tbody input.sm-def-check:checked').length;
                    $('#sm-select-all-defs').prop('checked', checked === total && total > 0);
                });

                // Bulk: generate capture code
                $(document).on('click', '#sm-bulk-generate-code', function() {
                    self.openCaptureCodeModal();
                });

                // Bulk: generate retrieve code
                $(document).on('click', '#sm-bulk-generate-retrieve', function() {
                    self.openRetrieveCodeModal();
                });

                // Bulk: delete selected
                $(document).on('click', '#sm-bulk-delete', function() {
                    if (!confirm(slidemindAdmin.strings.memDefConfirmDelete || 'Delete selected variable definitions?')) return;
                    self.deleteSelectedDefinitions();
                });

                // Generate capture code (in modal)
                $(document).on('click', '#sm-do-generate-capture', function() {
                    self.generateCaptureCode();
                });

                // Copy capture code
                $(document).on('click', '#sm-copy-capture-code', function() {
                    var code = $('#sm-capture-code-output').text();
                    SM.Utils.copyToClipboard(code, function() {
                        SM.Utils.showToast(slidemindAdmin.strings.memDefCodeCopied || 'Code copied to clipboard', 'success');
                    });
                });

                // Generate retrieve code (in modal)
                $(document).on('click', '#sm-do-generate-retrieve', function() {
                    self.generateRetrieveCode();
                });

                // Copy retrieve code
                $(document).on('click', '#sm-copy-retrieve-code', function() {
                    var code = $('#sm-retrieve-code-output').text();
                    SM.Utils.copyToClipboard(code, function() {
                        SM.Utils.showToast(slidemindAdmin.strings.memDefRetrieveCodeCopied || 'Code copied to clipboard', 'success');
                    });
                });

                // Tab switch: load learners on first visit
                $(document).on('click', '.sm-tab[data-tab="learners"]', function() {
                    if (!self._learnersLoaded) {
                        self._learnersLoaded = true;
                        self.loadLearners();
                    }
                });

                // Save profile settings
                $(document).on('click', '#sm-save-profile', function() {
                    self.saveProfileSettings();
                });

                // Generate profile retrieve code
                $(document).on('click', '#sm-profile-generate-retrieve', function() {
                    self.openProfileRetrieveModal();
                });

            }

            // Page liste (standalone learners page — fallback)
            if ($('#sm-learners-tbody').length && !$('#sm-definitions-tbody').length) {
                self.loadLearners();

                // Recherche avec debounce
                $(document).on('input', '#sm-learners-search', function() {
                    clearTimeout(self.searchTimer);
                    self.searchTimer = setTimeout(function() {
                        self.currentPage = 1;
                        self.loadLearners();
                    }, 300);
                });

                // Pagination
                $(document).on('click', '.sm-page-btn', function(e) {
                    e.preventDefault();
                    self.currentPage = parseInt($(this).data('page'));
                    self.loadLearners();
                });

                // Purge depuis la liste
                $(document).on('click', '.sm-purge-learner', function(e) {
                    e.preventDefault();
                    var userId = $(this).data('user-id');
                    var sessionId = $(this).data('session-id') || '';
                    var userName = $(this).data('user-name');
                    $('#sm-purge-learner-name').text(userName);
                    $('#sm-confirm-purge').data('user-id', userId).data('session-id', sessionId);
                    SM.Modal.open('sm-purge-modal');
                });

                // Confirmation purge (liste)
                $(document).on('click', '#sm-confirm-purge', function() {
                    var userId = $(this).data('user-id');
                    var sessionId = $(this).data('session-id') || '';
                    self.purge(userId, function() {
                        SM.Modal.close();
                        self.loadLearners();
                    }, sessionId);
                });

                // Export depuis la liste
                $(document).on('click', '.sm-export-learner', function(e) {
                    e.preventDefault();
                    self.exportCsv($(this).data('user-id'), $(this).data('session-id') || '');
                });
            }

            // Page detail
            if ($('#sm-learner-detail-content').length) {
                self.loadDetail();

                // Edition variable
                $(document).on('click', '.sm-edit-variable', function(e) {
                    e.preventDefault();
                    var $row = $(this).closest('tr');
                    $('#sm-edit-variable-id').val($row.data('variable-id'));
                    $('#sm-edit-variable-name').val($row.data('variable-name'));
                    $('#sm-edit-variable-value').val($row.data('variable-value'));
                    SM.Modal.open('sm-edit-variable-modal');
                });

                // Sauvegarde variable
                $(document).on('click', '#sm-save-variable', function() {
                    self.updateVariable();
                });

                // Suppression variable
                $(document).on('click', '.sm-delete-variable', function(e) {
                    e.preventDefault();
                    if (!confirm(slidemindAdmin.strings.confirmDeleteVariable)) return;

                    var $btn = $(this);
                    var variableId = $btn.closest('tr').data('variable-id');

                    SM.Utils.ajax('slidemind_delete_learner_variable', { variable_id: variableId }, function(response) {
                        if (response.success) {
                            $btn.closest('tr').fadeOut(300, function() {
                                $(this).remove();
                            });
                            SM.Utils.showToast(response.data.message, 'success');
                        } else {
                            SM.Utils.showToast(response.data.message, 'error');
                        }
                    });
                });

                // Purge depuis detail (bouton header)
                $(document).on('click', '.sm-purge-learner-detail', function(e) {
                    e.preventDefault();
                    SM.Modal.open('sm-purge-detail-modal');
                });

                // Confirmation purge (detail)
                $(document).on('click', '#sm-confirm-purge-detail', function() {
                    var userId = $(this).data('user-id');
                    var sessionId = $(this).data('session-id') || '';
                    self.purge(userId, function() {
                        SM.Modal.close();
                        window.location.href = 'admin.php?page=slidemind-memory';
                    }, sessionId);
                });

                // Export depuis detail
                $(document).on('click', '.sm-export-learner-detail', function(e) {
                    e.preventDefault();
                    self.exportCsv($(this).data('user-id'), $(this).data('session-id') || '');
                });
            }
        },

        loadLearners: function() {
            var self = this;
            var search = $('#sm-learners-search').val() || '';

            $('#sm-learners-loading').show();
            $('#sm-learners-empty').hide();
            $('#sm-learners-table-wrap').hide();

            SM.Utils.ajax('slidemind_get_learners', {
                page: self.currentPage,
                per_page: 20,
                search: search
            }, function(response) {
                $('#sm-learners-loading').hide();

                if (!response.success) {
                    SM.Utils.showToast(slidemindAdmin.strings.error, 'error');
                    return;
                }

                var data = response.data;

                if (data.learners.length === 0) {
                    $('#sm-learners-empty').show();
                    return;
                }

                var html = '';
                $.each(data.learners, function(i, learner) {
                    var detailUrl = 'admin.php?page=slidemind-learner-detail&user_id=' + learner.user_id;
                    if (learner.is_anonymous && learner.session_id) {
                        detailUrl += '&session_id=' + encodeURIComponent(learner.session_id);
                    }
                    var sessionAttr = learner.session_id ? ' data-session-id="' + self.escAttr(learner.session_id) + '"' : '';
                    html += '<tr>';
                    html += '<td>';
                    html += '<div style="display: flex; align-items: center; gap: 10px;">';
                    if (learner.avatar_url) {
                        html += '<img src="' + self.escAttr(learner.avatar_url) + '" alt="" style="width: 32px; height: 32px; border-radius: 50%;">';
                    } else {
                        html += '<span class="dashicons dashicons-admin-users" style="width: 32px; height: 32px; font-size: 32px; color: #9ca3af;"></span>';
                    }
                    html += '<div>';
                    html += '<strong>' + self.escHtml(learner.display_name) + '</strong>';
                    if (learner.email) {
                        html += '<br><small style="color: #6b7280;">' + self.escHtml(learner.email) + '</small>';
                    }
                    html += '</div></div>';
                    html += '</td>';
                    html += '<td>' + learner.global_count + '</td>';
                    html += '<td>' + learner.course_count + '</td>';
                    html += '<td>' + learner.module_count + '</td>';
                    html += '<td><span class="sm-badge sm-badge-info">' + learner.total_count + '</span></td>';
                    html += '<td><small style="color: #6b7280;">' + self.escHtml(learner.last_activity) + '</small></td>';
                    html += '<td>';
                    html += '<div class="sm-table-actions">';
                    html += '<a href="' + detailUrl + '" class="sm-btn sm-btn-sm sm-btn-secondary" title="Voir"><span class="dashicons dashicons-visibility"></span></a>';
                    if (!learner.is_anonymous) {
                        var wpProfileUrl = 'user-edit.php?user_id=' + learner.user_id;
                        html += '<a href="' + wpProfileUrl + '" class="sm-btn sm-btn-sm sm-btn-secondary" title="' + self.escAttr(slidemindAdmin.strings.viewWpProfile) + '"><span class="dashicons dashicons-admin-users"></span></a>';
                    }
                    html += '<button type="button" class="sm-btn sm-btn-sm sm-btn-secondary sm-export-learner" data-user-id="' + learner.user_id + '"' + sessionAttr + ' title="Exporter"><span class="dashicons dashicons-download"></span></button>';
                    html += '<button type="button" class="sm-btn sm-btn-sm sm-btn-secondary sm-purge-learner" data-user-id="' + learner.user_id + '"' + sessionAttr + ' data-user-name="' + self.escAttr(learner.display_name) + '" title="Purger"><span class="dashicons dashicons-trash"></span></button>';
                    html += '</div>';
                    html += '</td>';
                    html += '</tr>';
                });

                $('#sm-learners-tbody').html(html);
                self.renderPagination(data);
                $('#sm-learners-table-wrap').show();
            });
        },

        renderPagination: function(data) {
            var self = this;
            var html = '';

            if (data.pages <= 1) {
                $('#sm-learners-pagination').html('');
                return;
            }

            html += '<span style="color: #6b7280; margin-right: 12px;">';
            html += SM.Utils.sprintf(slidemindAdmin.strings.paginationInfo, data.page, data.pages, data.total);
            html += '</span>';

            if (data.page > 1) {
                html += '<button type="button" class="sm-btn sm-btn-sm sm-btn-secondary sm-page-btn" data-page="' + (data.page - 1) + '">';
                html += '<span class="dashicons dashicons-arrow-left-alt2"></span> ' + slidemindAdmin.strings.prevPage + '</button> ';
            }

            if (data.page < data.pages) {
                html += '<button type="button" class="sm-btn sm-btn-sm sm-btn-secondary sm-page-btn" data-page="' + (data.page + 1) + '">';
                html += slidemindAdmin.strings.nextPage + ' <span class="dashicons dashicons-arrow-right-alt2"></span></button>';
            }

            $('#sm-learners-pagination').html(html);
        },

        loadDetail: function() {
            var self = this;
            var $content = $('#sm-learner-detail-content');
            var userId = $content.data('user-id');
            var sessionId = $content.data('session-id') || '';

            if (!userId && !sessionId) return;

            $('#sm-learner-detail-loading').show();
            $content.hide();
            $('#sm-learner-detail-empty').hide();

            var params = { user_id: userId };
            if (sessionId) {
                params.session_id = sessionId;
            }

            SM.Utils.ajax('slidemind_get_learner_detail', params, function(response) {
                $('#sm-learner-detail-loading').hide();

                if (!response.success) {
                    SM.Utils.showToast(slidemindAdmin.strings.error, 'error');
                    return;
                }

                var data = response.data;
                $('#sm-learner-total-count').text(data.total_count);

                if (data.groups.length === 0) {
                    $('#sm-learner-detail-empty').show();
                    return;
                }

                var html = '';
                $.each(data.groups, function(i, group) {
                    html += '<div class="sm-card" style="margin-bottom: 16px;">';
                    html += '<h3 style="margin: 0 0 16px 0;">';

                    if (group.scope === 'global') {
                        html += '<span class="dashicons dashicons-admin-site" style="color: #6366f1;"></span> ';
                    } else if (group.scope === 'course') {
                        html += '<span class="dashicons dashicons-welcome-learn-more" style="color: #10b981;"></span> ';
                    } else if (group.scope === 'module') {
                        html += '<span class="dashicons dashicons-media-interactive" style="color: #f59e0b;"></span> ';
                    }

                    html += self.escHtml(group.scope_name);
                    html += ' <span class="sm-badge sm-badge-info" style="font-size: 11px;">' + group.variables.length + '</span>';
                    html += '</h3>';

                    html += '<table class="sm-table">';
                    html += '<thead><tr><th>' + slidemindAdmin.strings.colVariable + '</th><th>' + slidemindAdmin.strings.colValue + '</th><th>' + slidemindAdmin.strings.colLastModified + '</th><th>' + slidemindAdmin.strings.colActions + '</th></tr></thead>';
                    html += '<tbody>';

                    $.each(group.variables, function(j, v) {
                        var displayValue = v.variable_value;
                        if (displayValue && displayValue.length > 120) {
                            displayValue = displayValue.substring(0, 120) + '...';
                        }

                        html += '<tr data-variable-id="' + v.id + '" data-variable-name="' + self.escAttr(v.variable_name) + '" data-variable-value="' + self.escAttr(v.variable_value) + '">';
                        html += '<td><code>' + self.escHtml(v.variable_name) + '</code></td>';
                        html += '<td style="max-width: 400px; word-break: break-word;"><span class="sm-variable-display">' + self.escHtml(displayValue) + '</span></td>';
                        html += '<td><small style="color: #6b7280;">' + self.escHtml(v.updated_at) + '</small></td>';
                        html += '<td>';
                        html += '<div class="sm-table-actions">';
                        html += '<button type="button" class="sm-btn sm-btn-sm sm-btn-secondary sm-edit-variable" title="' + slidemindAdmin.strings.buttonEdit + '"><span class="dashicons dashicons-edit"></span></button>';
                        html += '<button type="button" class="sm-btn sm-btn-sm sm-btn-secondary sm-delete-variable" title="' + slidemindAdmin.strings.buttonDelete + '"><span class="dashicons dashicons-trash"></span></button>';
                        html += '</div>';
                        html += '</td>';
                        html += '</tr>';
                    });

                    html += '</tbody></table>';
                    html += '</div>';
                });

                $('#sm-learner-detail-content').html(html).show();
            });
        },

        updateVariable: function() {
            var variableId = $('#sm-edit-variable-id').val();
            var value = $('#sm-edit-variable-value').val();
            var self = this;

            var $btn = $('#sm-save-variable');
            $btn.prop('disabled', true).text(slidemindAdmin.strings.saving);

            SM.Utils.ajax('slidemind_update_learner_variable', {
                variable_id: variableId,
                value: value
            }, function(response) {
                $btn.prop('disabled', false).html('<span class="dashicons dashicons-saved"></span> ' + slidemindAdmin.strings.saveButton);

                if (response.success) {
                    SM.Utils.showToast(response.data.message, 'success');
                    SM.Modal.close();
                    self.loadDetail();
                } else {
                    SM.Utils.showToast(response.data.message, 'error');
                }
            });
        },

        purge: function(userId, callback, sessionId) {
            var params = { user_id: userId };
            if (sessionId) {
                params.session_id = sessionId;
            }
            SM.Utils.ajax('slidemind_purge_learner', params, function(response) {
                if (response.success) {
                    SM.Utils.showToast(response.data.message, 'success');
                    if (callback) callback();
                } else {
                    SM.Utils.showToast(response.data.message, 'error');
                }
            });
        },

        exportCsv: function(userId, sessionId) {
            var params = { user_id: userId };
            if (sessionId) {
                params.session_id = sessionId;
            }
            SM.Utils.ajax('slidemind_export_learner', params, function(response) {
                if (response.success) {
                    var BOM = '\uFEFF';
                    var blob = new Blob([BOM + response.data.csv], { type: 'text/csv;charset=utf-8;' });
                    var url = URL.createObjectURL(blob);
                    var a = document.createElement('a');
                    a.href = url;
                    a.download = response.data.filename;
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);

                    SM.Utils.showToast(slidemindAdmin.strings.exportCsvDone, 'success');
                } else {
                    SM.Utils.showToast(response.data.message, 'error');
                }
            });
        },

        // ── Definitions CRUD ─────────────────────────────────

        loadDefinitions: function() {
            var self = this;

            $('#sm-definitions-loading').show();
            $('#sm-definitions-empty').hide();
            $('#sm-definitions-table-wrap').hide();

            SM.Utils.ajax('slidemind_get_memory_definitions', {}, function(response) {
                $('#sm-definitions-loading').hide();

                if (!response.success) {
                    SM.Utils.showToast(slidemindAdmin.strings.error, 'error');
                    return;
                }

                self.definitions = response.data.definitions || [];

                if (self.definitions.length === 0) {
                    $('#sm-definitions-empty').show();
                    return;
                }

                self.renderDefinitions();
                $('#sm-definitions-table-wrap').show();
            });
        },

        renderDefinitions: function() {
            var self = this;
            var s = slidemindAdmin.strings;
            var html = '';

            $.each(self.definitions, function(i, def) {
                var count = def.learner_count || 0;
                html += '<tr>';
                html += '<td><input type="checkbox" class="sm-def-check" value="' + self.escAttr(def.id) + '" data-name="' + self.escAttr(def.name) + '"></td>';
                html += '<td><code>' + self.escHtml(def.name) + '</code></td>';
                html += '<td>' + self.escHtml(def.description || '—') + '</td>';
                html += '<td>' + (count > 0 ? '<span class="sm-badge sm-badge-info">' + count + '</span>' : '<span style="color:#9ca3af;">0</span>') + '</td>';
                html += '<td><button type="button" class="sm-btn sm-btn-sm sm-btn-secondary sm-edit-definition" data-def-id="' + self.escAttr(def.id) + '" title="' + (s.buttonEdit || 'Edit') + '"><span class="dashicons dashicons-edit"></span></button></td>';
                html += '</tr>';
            });

            $('#sm-definitions-tbody').html(html);
            $('#sm-select-all-defs').prop('checked', false);
            self.updateBulkBar();
        },

        updateBulkBar: function() {
            var checked = $('#sm-definitions-tbody input.sm-def-check:checked').length;
            var hasSelection = checked > 0;
            $('#sm-bulk-generate-code, #sm-bulk-generate-retrieve, #sm-bulk-delete').prop('disabled', !hasSelection);
            $('#sm-bulk-count').text(hasSelection ? SM.Utils.sprintf(slidemindAdmin.strings.memDefSelectedCount || '%d selected', checked) : '');
        },

        saveDefinition: function() {
            var name = $('#sm-def-name').val().trim();
            var description = $('#sm-def-description').val().trim();
            var id = $('#sm-def-id').val();
            var self = this;

            if (!name) {
                SM.Utils.showToast(slidemindAdmin.strings.titleRequired || 'Name is required', 'error');
                return;
            }

            var $btn = $('#sm-save-definition');
            $btn.prop('disabled', true).text(slidemindAdmin.strings.saving);

            SM.Utils.ajax('slidemind_save_memory_definition', {
                def_id: id,
                name: name,
                description: description
            }, function(response) {
                $btn.prop('disabled', false).text(slidemindAdmin.strings.saveButton);

                if (response.success) {
                    SM.Utils.showToast(response.data.message, 'success');
                    SM.Modal.close();
                    self.definitions = response.data.definitions;
                    if (self.definitions.length === 0) {
                        $('#sm-definitions-table-wrap').hide();
                        $('#sm-definitions-empty').show();
                    } else {
                        self.renderDefinitions();
                        $('#sm-definitions-empty').hide();
                        $('#sm-definitions-table-wrap').show();
                    }
                } else {
                    SM.Utils.showToast(response.data.message, 'error');
                }
            });
        },

        deleteSelectedDefinitions: function() {
            var self = this;
            var $checked = $('#sm-definitions-tbody input.sm-def-check:checked');
            var ids = [];
            $checked.each(function() { ids.push($(this).val()); });

            if (ids.length === 0) return;

            SM.Utils.ajax('slidemind_delete_memory_definitions_bulk', { def_ids: ids }, function(response) {
                if (response.success) {
                    self.definitions = response.data.definitions;
                    SM.Utils.showToast(slidemindAdmin.strings.buttonDelete || 'Deleted', 'success');
                    if (self.definitions.length === 0) {
                        $('#sm-definitions-table-wrap').hide();
                        $('#sm-definitions-empty').show();
                    } else {
                        self.renderDefinitions();
                    }
                } else {
                    SM.Utils.handleAjaxError(response);
                }
            });
        },

        // ── Slide definitions (aggregated, read-only) ───────

        loadSlideDefinitions: function() {
            $('#sm-slide-vars-loading').show();
            $('#sm-slide-vars-empty').hide();
            $('#sm-slide-vars-table-wrap').hide();

            var self = this;

            SM.Utils.ajax('slidemind_get_slide_memory_definitions', {}, function(response) {
                $('#sm-slide-vars-loading').hide();

                if (!response.success) return;

                var vars = response.data.variables || [];

                if (vars.length === 0) {
                    $('#sm-slide-vars-empty').show();
                    return;
                }

                var html = '';
                $.each(vars, function(i, v) {
                    var editUrl = 'admin.php?page=slidemind-slide-edit&slide_id=' + v.slide_id;
                    html += '<tr>';
                    html += '<td><code>' + self.escHtml(v.name) + '</code></td>';
                    html += '<td><span class="sm-badge sm-badge-secondary">' + self.escHtml(v.scope) + '</span></td>';
                    html += '<td><a href="' + self.escAttr(editUrl) + '">' + self.escHtml(v.slide_title) + '</a>';
                    if (v.source) html += ' <small style="color:#6b7280;">(' + self.escHtml(v.source) + ')</small>';
                    html += '</td>';
                    html += '<td>' + (v.module_title ? self.escHtml(v.module_title) : '<span style="color:#9ca3af;">—</span>') + '</td>';
                    html += '<td>' + (v.course_title ? self.escHtml(v.course_title) : '<span style="color:#9ca3af;">—</span>') + '</td>';
                    html += '<td>' + v.learner_count + '</td>';
                    html += '</tr>';
                });

                $('#sm-slide-vars-tbody').html(html);
                $('#sm-slide-vars-table-wrap').show();
            });
        },

        // ── Capture code generator ──────────────────────────

        openCaptureCodeModal: function() {
            var self = this;

            // Collect selected variables from table checkboxes
            var selectedVars = [];
            $('#sm-definitions-tbody input.sm-def-check:checked').each(function() {
                selectedVars.push($(this).data('name'));
            });

            if (selectedVars.length === 0) {
                SM.Utils.showToast(slidemindAdmin.strings.memDefNoDefinitions || 'Select at least one variable', 'warning');
                return;
            }

            // Build mapping fields directly
            var html = '';
            $.each(selectedVars, function(i, name) {
                html += '<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">';
                html += '<code style="min-width: 120px;">' + self.escHtml(name) + '</code>';
                html += '<span>&rarr;</span>';
                html += '<input type="text" class="sm-form-input sm-capture-editor-var" data-mem-name="' + self.escAttr(name) + '" placeholder="EditorVariableName" style="flex:1;" value="' + self.escHtml(self.suggestEditorVarName(name)) + '">';
                html += '</div>';
            });
            $('#sm-capture-mapping').html(html);
            $('#sm-capture-code-output').hide().text('');
            $('#sm-copy-capture-code').hide();

            SM.Modal.open('sm-capture-code-modal');
        },

        suggestEditorVarName: function(name) {
            // Convert snake_case to PascalCase for authoring tool variable suggestion
            return name.split('_').map(function(w) {
                return w.charAt(0).toUpperCase() + w.slice(1);
            }).join('_');
        },

        generateCaptureCode: function() {
            var self = this;
            var editorKey = $('#sm-capture-editor').val();

            // Collect variables from the mapping fields
            var variables = [];
            $('#sm-capture-mapping .sm-capture-editor-var').each(function() {
                var name = $(this).data('mem-name');
                var editorVar = $(this).val() || name;
                variables.push({ name: name, editor_var: editorVar });
            });

            if (variables.length === 0) {
                SM.Utils.showToast(slidemindAdmin.strings.memDefNoDefinitions || 'Select at least one variable', 'error');
                return;
            }

            var $btn = $('#sm-do-generate-capture');
            $btn.prop('disabled', true);

            // Fetch the memory bridge configId (auto-created if needed)
            SM.Utils.ajax('slidemind_generate_capture_code', {}, function(response) {
                $btn.prop('disabled', false);

                if (!response.success) {
                    SM.Utils.showToast(response.data.message, 'error');
                    return;
                }

                var d = response.data;
                var code = self.buildCaptureCode(editorKey, d.config_id, d.rest_url, variables);

                // Beta authoring tool warning
                var isBeta = SM.CodeGenerator.betaEditors.indexOf(editorKey) !== -1;
                var $bw = $('#sm-capture-beta-warning');
                $bw.toggle(isBeta);
                if (isBeta) $bw.find('span:last').html(slidemindAdmin.strings.codeBetaWarning);

                $('#sm-capture-code-output').text(code).show();
                $('#sm-copy-capture-code').show();
            });
        },

        buildCaptureCode: function(editorKey, configId, restUrl, variables) {
            // Convert {name, editor_var} → {name, source, scope} for bootstrap
            var memVars = [];
            for (var i = 0; i < variables.length; i++) {
                memVars.push({ name: variables[i].name, source: variables[i].editor_var, scope: 'global' });
            }
            return SM.CodeGenerator._buildBootstrap({
                configId:   configId,
                baseUrl:    restUrl,
                mode:       'capture',
                authoringTool: editorKey,
                memoryVars: memVars
            });
        },

        // ── Retrieve code generator ──────────────────────────

        openRetrieveCodeModal: function() {
            var self = this;

            // Collect selected variables from table checkboxes
            var selectedVars = [];
            $('#sm-definitions-tbody input.sm-def-check:checked').each(function() {
                selectedVars.push($(this).data('name'));
            });

            if (selectedVars.length === 0) {
                SM.Utils.showToast(slidemindAdmin.strings.memDefNoDefinitions || 'Select at least one variable', 'warning');
                return;
            }

            // Build mapping fields
            var html = '';
            $.each(selectedVars, function(i, name) {
                html += '<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">';
                html += '<code style="min-width: 120px;">' + self.escHtml(name) + '</code>';
                html += '<span>&rarr;</span>';
                html += '<input type="text" class="sm-form-input sm-retrieve-editor-var" data-mem-name="' + self.escAttr(name) + '" placeholder="EditorVariableName" style="flex:1;" value="' + self.escHtml(self.suggestEditorVarName(name)) + '">';
                html += '</div>';
            });
            $('#sm-retrieve-mapping').html(html);
            $('#sm-retrieve-code-output').hide().text('');
            $('#sm-copy-retrieve-code').hide();

            self._retrieveMode = 'memory';

            SM.Modal.open('sm-retrieve-code-modal');
        },

        generateRetrieveCode: function() {
            var self = this;
            var editorKey = $('#sm-retrieve-editor').val();
            var isProfile = self._retrieveMode === 'profile';

            // Collect variables from the mapping fields
            var variables = [];
            $('#sm-retrieve-mapping .sm-retrieve-editor-var').each(function() {
                var name = $(this).data('mem-name');
                var editorVar = $(this).val() || name;
                variables.push({ name: name, editor_var: editorVar });
            });

            if (variables.length === 0) {
                var msg = isProfile
                    ? (slidemindAdmin.strings.memProfileSelectFields || 'Check at least one profile field.')
                    : (slidemindAdmin.strings.memDefNoDefinitions || 'Select at least one variable');
                SM.Utils.showToast(msg, 'error');
                return;
            }

            var $btn = $('#sm-do-generate-retrieve');
            $btn.prop('disabled', true);

            // Reuse same AJAX handler (returns configId + restUrl)
            SM.Utils.ajax('slidemind_generate_capture_code', {}, function(response) {
                $btn.prop('disabled', false);

                if (!response.success) {
                    SM.Utils.showToast(response.data.message, 'error');
                    return;
                }

                var d = response.data;
                var opts = isProfile ? { endpoint: 'profile/get', comment: 'Retrieve learner profile', includeScope: false } : {};
                var code = self.buildRetrieveCode(editorKey, d.config_id, d.rest_url, variables, opts);

                // Beta authoring tool warning
                var isBeta = SM.CodeGenerator.betaEditors.indexOf(editorKey) !== -1;
                var $bw = $('#sm-retrieve-beta-warning');
                $bw.toggle(isBeta);
                if (isBeta) $bw.find('span:last').html(slidemindAdmin.strings.codeBetaWarning);

                $('#sm-retrieve-code-output').text(code).show();
                $('#sm-copy-retrieve-code').show();
            });
        },

        /**
         * Builds retrieve code for memory or profile endpoints.
         *
         * @param {string} editorKey  - Editor identifier (storyline, captivate, etc.)
         * @param {string} configId   - Memory bridge config UID
         * @param {string} restUrl    - REST API base URL
         * @param {Array}  variables  - [{name, editor_var}]
         * @param {object} [opts]     - Optional overrides
         * @param {string} [opts.endpoint]  - REST endpoint path (default: 'memory/get')
         * @param {string} [opts.comment]   - Code comment (default: 'Retrieve memory variables')
         * @param {boolean} [opts.includeScope] - Include scope param (default: true)
         */
        buildRetrieveCode: function(editorKey, configId, restUrl, variables, opts) {
            opts = opts || {};
            var isProfile = opts.endpoint === 'profile/get';

            // Convert {name, editor_var} → {name, source} for bootstrap
            var memVars = [];
            for (var i = 0; i < variables.length; i++) {
                memVars.push({ name: variables[i].name, source: variables[i].editor_var });
            }
            return SM.CodeGenerator._buildBootstrap({
                configId:   configId,
                baseUrl:    restUrl,
                mode:       isProfile ? 'profile' : 'retrieve',
                authoringTool: editorKey,
                memoryVars: memVars
            });
        },

        escHtml: function(str) {
            if (!str && str !== 0) return '';
            var div = document.createElement('div');
            div.appendChild(document.createTextNode(String(str)));
            return div.innerHTML;
        },

        escAttr: function(str) {
            if (!str && str !== 0) return '';
            return String(str)
                .replace(/&/g, '&amp;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;');
        },

        // ── Profile fields (Section C of Definitions tab) ───

        loadProfileFields: function() {
            var self = this;

            $('#sm-profile-loading').show();
            $('#sm-profile-empty').hide();
            $('#sm-profile-fields-wrap').hide();
            $('#sm-profile-actions').hide();

            SM.Utils.ajax('slidemind_get_profile_fields', {}, function(response) {
                $('#sm-profile-loading').hide();

                if (!response.success) {
                    $('#sm-profile-empty').show();
                    SM.Utils.showToast(response.data && response.data.message ? response.data.message : slidemindAdmin.strings.error, 'error');
                    return;
                }

                var groups = response.data.groups || {};
                var sourcesMeta = response.data.sources_meta || {};
                var hasGroups = false;

                var html = '';
                $.each(groups, function(source, fields) {
                    var meta = sourcesMeta[source] || { installed: true };
                    var installed = meta.installed;

                    // Skip empty groups for installed plugins (no fields detected)
                    if (installed && fields.length === 0) return;

                    hasGroups = true;

                    if (!installed) {
                        // Render disabled (greyed-out) plugin group
                        html += '<div class="sm-profile-section" style="margin-bottom: 8px;">';
                        html += '<div class="sm-collapsible-toggle sm-collapsible-disabled">';
                        html += '<span class="dashicons dashicons-admin-plugins"></span> ';
                        html += self.escHtml(source);
                        html += ' <span class="sm-badge sm-badge-muted">' + (slidemindAdmin.strings.memProfileNotInstalled || 'Not installed') + '</span>';
                        html += '</div>';
                        html += '</div>';
                        return;
                    }

                    var isWP = (source === 'WordPress');
                    var total = fields.length;
                    var checked = 0;
                    $.each(fields, function(i, f) { if (f.checked) checked++; });

                    html += '<div class="sm-profile-section" style="margin-bottom: 8px;">';
                    html += '<button type="button" class="sm-collapsible-toggle ' + (isWP ? 'open' : '') + '">';
                    html += '<span class="dashicons dashicons-admin-plugins"></span> ';
                    html += self.escHtml(source);
                    html += ' <span class="sm-badge sm-badge-info sm-profile-badge">' + checked + '/' + total + '</span>';
                    html += ' <span class="dashicons dashicons-arrow-down-alt2"></span>';
                    html += '</button>';
                    html += '<div class="sm-collapsible-content ' + (isWP ? 'open' : '') + '">';
                    html += '<div style="padding: 12px 0;">';
                    html += '<div style="margin-bottom: 8px; text-align: right;">';
                    html += '<a href="#" class="sm-profile-toggle-all" style="font-size: 12px;">' + (slidemindAdmin.strings.memProfileCheckAll || 'Check all / Uncheck all') + '</a>';
                    html += '</div>';

                    $.each(fields, function(i, field) {
                        var hasAlias = field.alias && field.alias.length > 0;
                        var displayVar = hasAlias ? field.alias : field.key;

                        html += '<div class="sm-profile-field-row' + (hasAlias ? ' sm-alias-active' : '') + '" data-field-key="' + self.escAttr(field.key) + '" data-original-label="' + self.escAttr(field.label) + '">';
                        html += '<label class="sm-form-label sm-profile-field-label">';
                        html += '<input type="checkbox" name="allowed_profile_fields[]" value="' + self.escAttr(field.key) + '"' + (field.checked ? ' checked' : '') + '>';
                        html += ' <span class="sm-profile-display-name">' + self.escHtml(hasAlias ? field.alias : field.label) + '</span>';
                        if (hasAlias) {
                            html += ' <span class="sm-profile-original-name">(' + self.escHtml(field.label) + ')</span>';
                        }
                        html += '</label>';
                        html += '<button type="button" class="sm-profile-edit-alias" title="' + (slidemindAdmin.strings.memProfileEditAlias || 'Edit alias') + '">';
                        html += '<span class="dashicons dashicons-edit"></span>';
                        html += '</button>';
                        html += '<div class="sm-profile-alias-wrapper' + (hasAlias ? ' sm-alias-visible' : '') + '">';
                        html += '<input type="text" name="profile_field_aliases[' + self.escAttr(field.key) + ']" value="' + self.escAttr(field.alias) + '" placeholder="' + (slidemindAdmin.strings.memProfileAliasPlaceholder || 'alias (e.g.: status)') + '" class="sm-form-input sm-profile-alias-input">';
                        html += '</div>';
                        html += '<code class="sm-profile-var-tag">{{profile.<span class="sm-profile-var-name">' + self.escHtml(displayVar) + '</span>}}</code>';
                        html += '</div>';
                    });

                    html += '</div></div></div>';
                });

                if (!hasGroups) {
                    $('#sm-profile-empty').show();
                    return;
                }

                $('#sm-profile-fields-wrap').html(html).show();
                $('#sm-profile-actions').css('display', 'flex');
            }, function() {
                $('#sm-profile-loading').hide();
                $('#sm-profile-empty').show();
            });
        },

        saveProfileSettings: function() {
            var self = this;
            var $btn = $('#sm-save-profile');
            $btn.prop('disabled', true).find('.dashicons').removeClass('dashicons-saved').addClass('dashicons-update');

            // Collect checked fields
            var allowed = [];
            $('#sm-profile-fields-wrap input[name="allowed_profile_fields[]"]:checked').each(function() {
                allowed.push($(this).val());
            });

            // Collect aliases (use data-field-key from parent row, not fragile name parsing)
            var aliases = {};
            $('#sm-profile-fields-wrap input.sm-profile-alias-input').each(function() {
                var key = $(this).closest('.sm-profile-field-row').data('field-key');
                var val = $(this).val().trim();
                if (key && val) {
                    aliases[key] = val;
                }
            });

            SM.Utils.ajax('slidemind_save_profile_settings', {
                allowed_profile_fields: allowed,
                profile_field_aliases: aliases
            }, function(response) {
                $btn.prop('disabled', false).find('.dashicons').removeClass('dashicons-update').addClass('dashicons-saved');

                if (response.success) {
                    SM.Utils.showToast(slidemindAdmin.strings.memProfileSaved || 'Profile settings saved', 'success');
                    // Update badge counts locally (no reload needed)
                    $('#sm-profile-fields-wrap .sm-profile-section').each(function() {
                        var total = $(this).find('input[name="allowed_profile_fields[]"]').length;
                        var checked = $(this).find('input[name="allowed_profile_fields[]"]:checked').length;
                        $(this).find('.sm-profile-badge').text(checked + '/' + total);
                    });
                } else {
                    SM.Utils.showToast(response.data.message, 'error');
                }
            });
        },

        openProfileRetrieveModal: function() {
            var self = this;

            // Collect checked profile fields
            var selectedFields = [];
            $('#sm-profile-fields-wrap input[name="allowed_profile_fields[]"]:checked').each(function() {
                var key = $(this).val();
                var $row = $(this).closest('.sm-profile-field-row');
                var aliasInput = $row.find('.sm-profile-alias-input').val() || '';
                var outputKey = aliasInput.trim() || key;
                selectedFields.push({ key: key, outputKey: outputKey });
            });

            if (selectedFields.length === 0) {
                SM.Utils.showToast(slidemindAdmin.strings.memProfileSelectFields || 'Check at least one profile field.', 'warning');
                return;
            }

            // Build mapping fields — reuse the shared retrieve modal
            var html = '';
            $.each(selectedFields, function(i, field) {
                html += '<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">';
                html += '<code style="min-width: 120px;">' + self.escHtml(field.outputKey) + '</code>';
                html += '<span>&rarr;</span>';
                html += '<input type="text" class="sm-form-input sm-retrieve-editor-var" data-mem-name="' + self.escAttr(field.outputKey) + '" placeholder="EditorVariableName" style="flex:1;" value="' + self.escHtml(self.suggestEditorVarName(field.outputKey)) + '">';
                html += '</div>';
            });
            $('#sm-retrieve-mapping').html(html);
            $('#sm-retrieve-code-output').hide().text('');
            $('#sm-copy-retrieve-code').hide();

            // Flag so generateRetrieveCode knows to use profile endpoint
            self._retrieveMode = 'profile';

            SM.Modal.open('sm-retrieve-code-modal');
        }
    };

    // =========================================================================
    // AUTOCOMPLETE @VARIABLE (Tribute.js)
    // =========================================================================

    /**
     * Autocomplete des variables dans les textareas de prompts
     *
     * Declencheur : @
     * Strategie : cache-then-filter (AJAX au 1er @, filtrage local ensuite)
     * Cibles : #sm-slide-prompt, #sm-template-prompt
     */
    SM.Autocomplete = {
        _cache: null,
        _cacheTime: 0,
        _cacheTTL: 60000, // 60 secondes
        _tribute: null,
        _selectors: '#sm-slide-prompt, #sm-slide-response-example, #sm-template-prompt, #sm-sandbox-prompt, #wizard-prompt',

        init: function() {
            if (typeof Tribute === 'undefined') {
                console.warn('[SM] Tribute.js non chargé, autocomplete désactivé');
                return;
            }

            var self = this;

            this._tribute = new Tribute({
                trigger: '@',
                allowSpaces: true,
                menuItemLimit: 20,
                containerClass: 'tribute-container sm-tribute',
                noMatchTemplate: function() {
                    return '<li class="no-match">' + slidemindAdmin.strings.noVariableFound + '</li>';
                },
                lookup: 'value',
                fillAttr: 'key',
                selectTemplate: function(item) {
                    if (!item) return '';
                    return item.original.key;
                },
                menuItemTemplate: function(item) {
                    var d = item.original;
                    return '<span class="sm-var-badge sm-var-badge--' + SM.Memory.escAttr(d.type) + '">' +
                               SM.Memory.escHtml(d.type) +
                           '</span>' +
                           '<span class="sm-var-name">' + item.string + '</span>' +
                           (d.label ? '<span class="sm-var-label">' + SM.Memory.escHtml(d.label) + '</span>' : '');
                },
                values: function(text, cb) {
                    self._getVariables(function(vars) {
                        cb(vars);
                    });
                }
            });

            // Attache aux textareas existants
            this._attachToExisting();

            // Observe le DOM pour les textareas charges dynamiquement
            this._observeDOM();
        },

        _attachToExisting: function() {
            var $targets = $(this._selectors);
            if ($targets.length && this._tribute) {
                this._tribute.attach($targets.get());
            }
        },

        _observeDOM: function() {
            var self = this;

            // Re-attache quand le contenu du modal/page change
            $(document).on('sam:slide-loaded sam:template-loaded', function() {
                setTimeout(function() {
                    self._attachToExisting();
                }, 100);
            });

            // MutationObserver pour les textareas injectees dynamiquement
            if (typeof MutationObserver !== 'undefined') {
                var observer = new MutationObserver(function(mutations) {
                    var shouldAttach = false;
                    mutations.forEach(function(m) {
                        if (m.addedNodes.length) {
                            for (var i = 0; i < m.addedNodes.length; i++) {
                                var node = m.addedNodes[i];
                                if (node.nodeType === 1) {
                                    if (node.matches && node.matches(self._selectors)) {
                                        shouldAttach = true;
                                    } else if (node.querySelector && node.querySelector(self._selectors)) {
                                        shouldAttach = true;
                                    }
                                }
                            }
                        }
                    });
                    if (shouldAttach) {
                        setTimeout(function() {
                            self._attachToExisting();
                        }, 50);
                    }
                });

                observer.observe(document.body, { childList: true, subtree: true });
            }
        },

        /**
         * Filtre les variables selon la hierarchie de la slide courante.
         * Ne filtre que sur la page slide-edit (template/sandbox = tout visible).
         */
        _filterByHierarchy: function(vars) {
            var hasCourse, hasModule, inheritedFields;
            if ($('#sm-slide-form').length) {
                hasCourse = !!$('#sm-slide-course').val();
                hasModule = !!$('#sm-slide-module').val();
                inheritedFields = window.slidemindInheritedFields || {};
            } else if ($('#sm-wizard').length) {
                hasCourse = !!$('#wizard-course').val();
                hasModule = !!$('#wizard-module').val();
                inheritedFields = {};
            } else {
                return vars;
            }

            return vars.filter(function(v) {
                if (!v.requires) return true;
                if (v.requires === 'course' && !hasCourse) return false;
                if (v.requires === 'module' && !hasModule) return false;
                // Filter out system variables whose parent field is empty
                if (v.type === 'system' && v.value && inheritedFields.hasOwnProperty(v.value)) {
                    if (!inheritedFields[v.value]) return false;
                }
                return true;
            });
        },

        _getVariables: function(cb) {
            var self = this;
            var now = Date.now();

            // Retourne le cache s'il est valide
            if (this._cache && (now - this._cacheTime) < this._cacheTTL) {
                cb(self._filterByHierarchy(this._cache));
                return;
            }

            SM.Utils.ajax('slidemind_search_variables', {}, function(response) {
                if (response.success && response.data && response.data.variables) {
                    self._cache = response.data.variables;
                    self._cacheTime = Date.now();
                    cb(self._filterByHierarchy(self._cache));
                } else {
                    cb([]);
                }
            });
        },

        invalidateCache: function() {
            this._cache = null;
            this._cacheTime = 0;
        }
    };

    // =========================================================================
    // GÉNÉRATEUR DE PROMPT PAR IA
    // =========================================================================

    /**
     * Génère un prompt système via l'IA à partir de critères pédagogiques
     */
    SM.PromptGenerator = {
        _targetTextarea: null,
        _modalBuilt: false,

        init: function() {
            // Ne s'active que si un textarea de prompt existe
            if (!$('#sm-slide-prompt').length && !$('#wizard-prompt').length && !$('#sm-sandbox-prompt').length) {
                return;
            }

            this._buildModal();
            this._bindEvents();
        },

        _buildModal: function() {
            if (this._modalBuilt) return;

            var s = slidemindAdmin.strings;
            var html = '<div class="sm-modal-overlay" id="sm-pg-modal">' +
                '<div class="sm-modal" style="max-width: 700px;">' +
                    '<div class="sm-modal-header">' +
                        '<h3>' + s.pgModalTitle + '</h3>' +
                        '<button type="button" class="sm-modal-close">&times;</button>' +
                    '</div>' +
                    '<div class="sm-modal-body">' +
                        '<div class="sm-form-group">' +
                            '<label class="sm-form-label" for="sm-pg-model">' + s.pgModelLabel + '</label>' +
                            '<select id="sm-pg-model" class="sm-form-select"></select>' +
                        '</div>' +
                        '<div class="sm-form-group">' +
                            '<label class="sm-form-label" for="sm-pg-type">' + s.pgTypeLabel + ' <span class="required">*</span></label>' +
                            '<select id="sm-pg-type" class="sm-form-select">' +
                                '<option value="">' + s.pgSelectPlaceholder + '</option>' +
                                '<option value="Tutorat socratique">' + s.pgTypeSocratic + '</option>' +
                                '<option value="Quiz interactif">' + s.pgTypeQuiz + '</option>' +
                                '<option value="Feedback personnalisé">' + s.pgTypeFeedback + '</option>' +
                                '<option value="Jeu de rôle / Simulation">' + s.pgTypeRoleplay + '</option>' +
                                '<option value="Étude de cas">' + s.pgTypeCaseStudy + '</option>' +
                                '<option value="Reformulation / Explication">' + s.pgTypeReformulation + '</option>' +
                                '<option value="Évaluation formative">' + s.pgTypeFormative + '</option>' +
                            '</select>' +
                        '</div>' +
                        '<div class="sm-form-group">' +
                            '<label class="sm-form-label" for="sm-pg-subject">' + s.pgSubjectLabel + ' <span class="required">*</span></label>' +
                            '<input type="text" id="sm-pg-subject" class="sm-form-input" placeholder="' + s.pgSubjectPlaceholder + '">' +
                        '</div>' +
                        '<div class="sm-form-group">' +
                            '<label class="sm-form-label" for="sm-pg-objective">' + s.pgObjectiveLabel + '</label>' +
                            '<input type="text" id="sm-pg-objective" class="sm-form-input" placeholder="' + s.pgObjectivePlaceholder + '">' +
                        '</div>' +
                        '<div class="sm-form-row">' +
                            '<div class="sm-form-group">' +
                                '<label class="sm-form-label" for="sm-pg-tone">' + s.pgToneLabel + '</label>' +
                                '<select id="sm-pg-tone" class="sm-form-select">' +
                                    '<option value="">' + s.pgToneDefault + '</option>' +
                                    '<option value="Bienveillant et encourageant">' + s.pgToneBenevolent + '</option>' +
                                    '<option value="Professionnel et factuel">' + s.pgToneProfessional + '</option>' +
                                    '<option value="Chaleureux et accessible">' + s.pgToneWarm + '</option>' +
                                    '<option value="Stimulant et exigeant">' + s.pgToneStimulating + '</option>' +
                                    '<option value="Ludique et engageant">' + s.pgTonePlayful + '</option>' +
                                '</select>' +
                            '</div>' +
                            '<div class="sm-form-group">' +
                                '<label class="sm-form-label" for="sm-pg-level">' + s.pgLevelLabel + '</label>' +
                                '<select id="sm-pg-level" class="sm-form-select">' +
                                    '<option value="">' + s.pgToneDefault + '</option>' +
                                    '<option value="Débutant">' + s.pgLevelBeginner + '</option>' +
                                    '<option value="Intermédiaire">' + s.pgLevelIntermediate + '</option>' +
                                    '<option value="Avancé">' + s.pgLevelAdvanced + '</option>' +
                                    '<option value="Expert">' + s.pgLevelExpert + '</option>' +
                                '</select>' +
                            '</div>' +
                        '</div>' +
                        '<div class="sm-form-group">' +
                            '<label class="sm-form-label">' + s.pgVariablesLabel + '</label>' +
                            '<div id="sm-pg-variables" class="sm-pg-variables-list">' +
                                '<p class="sm-form-help">' + s.loadingVariables + '</p>' +
                            '</div>' +
                        '</div>' +
                        '<div id="sm-pg-error" class="sm-notice sm-notice-error" style="display: none;"></div>' +
                    '</div>' +
                    '<div class="sm-modal-footer">' +
                        '<button type="button" class="sm-btn sm-btn-secondary sm-modal-close">' + s.cancelButton + '</button>' +
                        '<button type="button" class="sm-btn sm-btn-primary" id="sm-pg-submit">' +
                            '<span class="dashicons dashicons-admin-generic"></span> ' + s.pgGenerateButton +
                        '</button>' +
                    '</div>' +
                '</div>' +
            '</div>';

            $('body').append(html);
            this._modalBuilt = true;
        },

        _bindEvents: function() {
            var self = this;

            // Bouton slide-edit
            $(document).on('click', '#sm-generate-prompt-btn', function() {
                self._targetTextarea = $('#sm-slide-prompt');
                self._openModal();
            });

            // Bouton wizard
            $(document).on('click', '#wizard-generate-prompt-btn', function() {
                self._targetTextarea = $('#wizard-prompt');
                self._openModal();
            });

            // Bouton sandbox
            $(document).on('click', '#sm-sandbox-generate-prompt-btn', function() {
                self._targetTextarea = $('#sm-sandbox-prompt');
                self._openModal();
            });

            // Soumission
            $(document).on('click', '#sm-pg-submit', function() {
                self._generate();
            });
        },

        _openModal: function() {
            // Reset le formulaire
            $('#sm-pg-type').val('');
            $('#sm-pg-subject').val('');
            $('#sm-pg-objective').val('');
            $('#sm-pg-tone').val('');
            $('#sm-pg-level').val('');
            $('#sm-pg-error').hide();

            // Peuple le dropdown modèle depuis le premier .sm-model-select de la page
            var $pgModel = $('#sm-pg-model');
            var $source = $('.sm-model-select').first();
            if ($source.length) {
                $pgModel.html($source.html());
            }
            // Pré-sélectionne le modèle par défaut
            if (slidemindAdmin && slidemindAdmin.defaults && slidemindAdmin.defaults.model) {
                $pgModel.val(slidemindAdmin.defaults.model);
            }

            // Charge les variables
            this._loadVariables();

            SM.Modal.open('sm-pg-modal');
        },

        _loadVariables: function() {
            var self = this;
            var $container = $('#sm-pg-variables');

            // Essaie le cache Autocomplete d'abord
            if (SM.Autocomplete._cache) {
                self._renderVariableCheckboxes(SM.Autocomplete._cache);
                return;
            }

            // Fallback AJAX
            $container.html('<p class="sm-form-help">' + slidemindAdmin.strings.loadingVariables + '</p>');
            SM.Utils.ajax('slidemind_search_variables', {}, function(response) {
                if (response.success && response.data && response.data.variables) {
                    SM.Autocomplete._cache = response.data.variables;
                    SM.Autocomplete._cacheTime = Date.now();
                    self._renderVariableCheckboxes(response.data.variables);
                } else {
                    $container.html('<p class="sm-form-help">' + slidemindAdmin.strings.noVariableAvailable + '</p>');
                }
            });
        },

        _renderVariableCheckboxes: function(variables) {
            var $container = $('#sm-pg-variables');
            var groups = { system: [], memory: [], profile: [], template: [] };
            var groupLabels = {
                system: slidemindAdmin.strings.pgVarGroupSystem,
                memory: slidemindAdmin.strings.pgVarGroupMemory,
                profile: slidemindAdmin.strings.pgVarGroupProfile,
                template: slidemindAdmin.strings.pgVarGroupTemplate
            };

            // Groupe les variables par type
            $.each(variables, function(i, v) {
                var type = v.type || 'template';
                if (!groups[type]) groups[type] = [];
                groups[type].push(v);
            });

            var html = '';
            $.each(groups, function(type, vars) {
                if (vars.length === 0) return;

                html += '<div class="sm-pg-var-group">';
                html += '<strong style="font-size: 12px; color: #6b7280; display: block; margin-bottom: 4px;">' + groupLabels[type] + '</strong>';

                $.each(vars, function(j, v) {
                    var varName = v.value || '';
                    var checked = type === 'system' ? ' checked' : '';
                    html += '<label style="display: inline-flex; align-items: center; gap: 4px; margin: 2px 8px 2px 0; font-size: 13px; cursor: pointer;">';
                    html += '<input type="checkbox" class="sm-pg-var-checkbox" value="' + SM.Memory.escAttr(varName) + '"' + checked + '>';
                    html += '<code style="font-size: 11px;">' + SM.Memory.escHtml(v.key || '{{' + varName + '}}') + '</code>';
                    html += '</label>';
                });

                html += '</div>';
            });

            if (!html) {
                html = '<p class="sm-form-help">' + slidemindAdmin.strings.noVariableAvailable + '</p>';
            }

            $container.html(html);
        },

        _generate: function() {
            var type    = $('#sm-pg-type').val();
            var subject = $('#sm-pg-subject').val().trim();
            var $error  = $('#sm-pg-error');

            // Validation
            if (!type) {
                $error.text(slidemindAdmin.strings.pgErrorSelectType).show();
                return;
            }
            if (!subject) {
                $error.text(slidemindAdmin.strings.pgErrorEnterSubject).show();
                return;
            }
            $error.hide();

            // Collecte les variables cochées
            var variables = [];
            $('.sm-pg-var-checkbox:checked').each(function() {
                variables.push($(this).val());
            });

            // Spinner sur le bouton
            var $btn = $('#sm-pg-submit');
            var originalHtml = $btn.html();
            $btn.prop('disabled', true).html('<span class="sm-spinner-inline"></span> ' + slidemindAdmin.strings.pgGenerating);

            var self = this;

            var modelVal = $('#sm-pg-model').val() || '';

            SM.Utils.ajax('slidemind_generate_prompt', {
                type: type,
                subject: subject,
                objective: $('#sm-pg-objective').val().trim(),
                tone: $('#sm-pg-tone').val(),
                level: $('#sm-pg-level').val(),
                model: modelVal,
                variables: variables
            }, function(response) {
                $btn.prop('disabled', false).html(originalHtml);

                if (response && response.success && response.data && response.data.prompt) {
                    // Insère le prompt dans le textarea cible
                    self._targetTextarea.val(response.data.prompt);
                    self._targetTextarea.trigger('input');

                    // Ferme la modale
                    SM.Modal.close();

                    // Toast de succès
                    SM.Utils.showToast(slidemindAdmin.strings.pgPromptGenerated, 'success');
                } else {
                    var msg = slidemindAdmin.strings.pgGenerationError;
                    if (response && response.data && response.data.message) {
                        msg = response.data.message;
                    } else if (typeof response === 'string') {
                        console.error('SM PromptGenerator: réponse non-JSON', response);
                        msg = slidemindAdmin.strings.pgServerError;
                    } else if (response === 0 || response === -1) {
                        msg = slidemindAdmin.strings.pgSessionExpired;
                    }
                    $error.text(msg).show();
                }
            });
        }
    };

    /**
     * Sélecteur d'emoji pour les templates
     */
    SM.EmojiPicker = {
        emojis: {
            'Éducation': ['🎓','📚','📖','📝','✏️','🏫','👨‍🏫','👩‍🏫','🎒','📐','🔬','🧪','🧮','📊','🗂️'],
            'Communication': ['💬','🗨️','💡','❓','❗','✅','🔔','📢','🤔','🧠','👁️','👂','✋','🙋','🎯'],
            'Activités': ['🎮','🎲','🧩','🏆','⭐','🥇','🎪','🎭','🎨','🎵','🔧','⚙️','🛠️','📋','📌'],
            'Personnes': ['👤','👥','🧑‍🤝‍🧑','🤝','👶','🧒','👦','👧','🧑','👨','👩','🧑‍⚕️','👨‍⚕️','👩‍⚕️','❤️'],
            'Nature & Santé': ['🌱','🌿','🍎','💪','🫀','🩺','🏥','💊','🧬','🌈','☀️','🌙','⏰','📅','🔑']
        },

        init: function() {
            if (!$('.sm-emoji-picker-wrap').length) return;

            var self = this;
            this.render();
            this.updateClearBtn();

            // Toggle dropdown
            $(document).on('click', '.sm-emoji-toggle, .sm-emoji-input', function(e) {
                e.stopPropagation();
                var $dropdown = $(this).closest('.sm-emoji-picker-wrap').find('.sm-emoji-dropdown');
                var isVisible = $dropdown.is(':visible');
                $('.sm-emoji-dropdown').hide();
                if (!isVisible) {
                    $dropdown.show();
                    $dropdown.find('.sm-emoji-search').val('').focus();
                    self.filterEmojis('');
                }
            });

            // Select emoji
            $(document).on('click', '.sm-emoji-item', function(e) {
                e.stopPropagation();
                var emoji = $(this).text();
                $('#sm-template-icon').val(emoji);
                $('.sm-emoji-dropdown').hide();
                self.updateClearBtn();
            });

            // Clear
            $(document).on('click', '.sm-emoji-clear', function(e) {
                e.stopPropagation();
                $('#sm-template-icon').val('');
                self.updateClearBtn();
            });

            // Search
            $(document).on('input', '.sm-emoji-search', function(e) {
                e.stopPropagation();
                self.filterEmojis($(this).val());
            });

            // Prevent search keystrokes from closing
            $(document).on('keydown', '.sm-emoji-search', function(e) {
                e.stopPropagation();
            });

            // Close on outside click
            $(document).on('click', function() {
                $('.sm-emoji-dropdown').hide();
            });

            $(document).on('click', '.sm-emoji-dropdown', function(e) {
                e.stopPropagation();
            });
        },

        render: function() {
            var $grid = $('.sm-emoji-grid');
            if (!$grid.length) return;

            var html = '';
            var self = this;
            $.each(this.emojis, function(category, items) {
                html += '<div class="sm-emoji-category" data-category="' + category + '">' + category + '</div>';
                $.each(items, function(i, emoji) {
                    html += '<button type="button" class="sm-emoji-item" title="' + category + '">' + emoji + '</button>';
                });
            });
            $grid.html(html);
        },

        filterEmojis: function(query) {
            query = query.toLowerCase().trim();
            var $grid = $('.sm-emoji-grid');

            if (!query) {
                $grid.find('.sm-emoji-item, .sm-emoji-category').show();
                return;
            }

            // Map search terms to categories
            var categoryMap = {
                'éducation': 'Éducation', 'education': 'Éducation', 'école': 'Éducation', 'ecole': 'Éducation', 'livre': 'Éducation', 'apprendre': 'Éducation',
                'communication': 'Communication', 'question': 'Communication', 'parler': 'Communication', 'idée': 'Communication', 'idee': 'Communication',
                'activité': 'Activités', 'activite': 'Activités', 'jeu': 'Activités', 'sport': 'Activités', 'outil': 'Activités',
                'personne': 'Personnes', 'gens': 'Personnes', 'humain': 'Personnes', 'enfant': 'Personnes', 'santé': 'Nature & Santé', 'sante': 'Nature & Santé',
                'nature': 'Nature & Santé', 'médical': 'Nature & Santé', 'medical': 'Nature & Santé'
            };

            var matchedCategories = [];
            $.each(categoryMap, function(term, cat) {
                if (term.indexOf(query) !== -1) {
                    matchedCategories.push(cat);
                }
            });

            if (matchedCategories.length > 0) {
                $grid.find('.sm-emoji-item, .sm-emoji-category').hide();
                $.each(matchedCategories, function(i, cat) {
                    $grid.find('.sm-emoji-category[data-category="' + cat + '"]').show()
                        .nextUntil('.sm-emoji-category').show();
                });
            } else {
                $grid.find('.sm-emoji-item, .sm-emoji-category').show();
            }
        },

        updateClearBtn: function() {
            var val = $('#sm-template-icon').val();
            $('.sm-emoji-clear').toggle(!!val);
        }
    };

    /**
     * Gestionnaire d'export/import en masse
     */
    SM.BulkData = {
        _importData: null,

        init: function() {
            var self = this;

            // Export all data (Settings > Data tab)
            $(document).on('click', '#sm-bulk-export-all', function() {
                self.exportAll();
            });

            // Per-type export (list view buttons)
            $(document).on('click', '.sm-export-type-btn', function(e) {
                e.preventDefault();
                var type = $(this).data('export-type');
                if (type) {
                    self.exportType(type);
                }
            });

            // Import file selection
            $(document).on('change', '#sm-bulk-import-file', function() {
                var file = this.files[0];
                if (!file) {
                    self._importData = null;
                    $('#sm-bulk-import-preview').hide();
                    $('#sm-bulk-import-btn').prop('disabled', true);
                    return;
                }
                SM.ImportExport.readFile(file, function(content) {
                    self.showPreview(content);
                });
            });

            // Import button
            $(document).on('click', '#sm-bulk-import-btn', function() {
                self.importAll();
            });
        },

        exportAll: function() {
            var $btn = $('#sm-bulk-export-all');
            var originalHtml = $btn.html();
            $btn.prop('disabled', true).html('<span class="dashicons dashicons-update sm-spin"></span> ' + (slidemindAdmin.strings.exporting));

            SM.Utils.ajax('slidemind_export_all_data', {}, function(response) {
                $btn.prop('disabled', false).html(originalHtml);
                if (response.success) {
                    SM.ImportExport.downloadJson(response.data.json, response.data.filename);
                    SM.Utils.showToast(slidemindAdmin.strings.bulkExportSuccess, 'success');
                } else {
                    SM.Utils.handleAjaxError(response);
                }
            });
        },

        exportType: function(type) {
            var actionMap = {
                'courses': 'slidemind_export_all_courses',
                'modules': 'slidemind_export_all_modules',
                'slides':  'slidemind_export_all_slides'
            };

            var action = actionMap[type];
            if (!action) return;

            SM.Utils.showToast(slidemindAdmin.strings.exporting, 'success');

            SM.Utils.ajax(action, {}, function(response) {
                if (response.success) {
                    SM.ImportExport.downloadJson(response.data.json, response.data.filename);
                } else {
                    SM.Utils.handleAjaxError(response);
                }
            });
        },

        showPreview: function(content) {
            var $preview = $('#sm-bulk-import-preview');
            var $content = $('#sm-bulk-import-preview-content');
            var $btn = $('#sm-bulk-import-btn');

            try {
                var data = JSON.parse(content);
                if (!data.slidemind_export_version || data.export_type !== 'bulk') {
                    $content.html('<strong>' + slidemindAdmin.strings.bulkInvalidFormat + '</strong>');
                    $preview.show();
                    $btn.prop('disabled', true);
                    this._importData = null;
                    return;
                }

                this._importData = content;

                var html = '';
                html += '<strong>' + slidemindAdmin.strings.bulkPreviewTitle + '</strong><br>';
                if (data.exported_at) {
                    html += slidemindAdmin.strings.bulkPreviewDate + ': ' + data.exported_at + '<br>';
                }
                html += slidemindAdmin.strings.bulkPreviewCourses + ': ' + (data.courses ? data.courses.length : 0) + '<br>';

                // Count modules and slides from course exports
                var modulesCount = 0;
                var slidesCount = 0;
                if (data.courses) {
                    for (var i = 0; i < data.courses.length; i++) {
                        var c = data.courses[i];
                        var mods = c.modules || [];
                        modulesCount += mods.length;
                        for (var j = 0; j < mods.length; j++) {
                            slidesCount += (mods[j].slides || []).length;
                        }
                    }
                }
                // Add orphans
                modulesCount += (data.orphan_modules || []).length;
                slidesCount += (data.orphan_slides || []).length;

                html += slidemindAdmin.strings.bulkPreviewModules + ': ' + modulesCount + '<br>';
                html += slidemindAdmin.strings.bulkPreviewSlides + ': ' + slidesCount + '<br>';
                html += slidemindAdmin.strings.bulkPreviewTemplates + ': ' + (data.templates ? data.templates.length : 0) + '<br>';
                html += slidemindAdmin.strings.bulkPreviewMemory + ': ' + (data.memory_definitions ? data.memory_definitions.length : 0);

                $content.html(html);
                $preview.show();
                $btn.prop('disabled', false);
            } catch (e) {
                $content.html('<strong>' + slidemindAdmin.strings.bulkInvalidFormat + '</strong>');
                $preview.show();
                $btn.prop('disabled', true);
                this._importData = null;
            }
        },

        importAll: function() {
            if (!this._importData) return;

            if (!confirm(slidemindAdmin.strings.bulkImportConfirm)) return;

            var $btn = $('#sm-bulk-import-btn');
            var originalHtml = $btn.html();
            $btn.prop('disabled', true).html('<span class="dashicons dashicons-update sm-spin"></span> ' + slidemindAdmin.strings.importing);

            SM.Utils.ajax('slidemind_import_all_data', { json: this._importData }, function(response) {
                $btn.prop('disabled', false).html(originalHtml);
                var $result = $('#sm-bulk-import-result');

                if (response.success) {
                    var c = response.data.counts || {};
                    var html = '<div class="sm-notice sm-notice-success">';
                    html += '<span class="dashicons dashicons-yes-alt"></span>';
                    html += '<div><strong>' + slidemindAdmin.strings.import_success + '</strong><br>';
                    html += slidemindAdmin.strings.bulkPreviewCourses + ': ' + (c.courses || 0) + '<br>';
                    html += slidemindAdmin.strings.bulkPreviewModules + ': ' + (c.modules || 0) + '<br>';
                    html += slidemindAdmin.strings.bulkPreviewSlides + ': ' + (c.slides || 0) + '<br>';
                    html += slidemindAdmin.strings.bulkPreviewTemplates + ': ' + (c.templates || 0) + '<br>';
                    html += slidemindAdmin.strings.bulkPreviewMemory + ': ' + (c.memory_definitions || 0);

                    if (c.errors && c.errors.length) {
                        html += '<br><br><strong>' + slidemindAdmin.strings.bulkImportErrors + ':</strong><br>';
                        for (var i = 0; i < c.errors.length; i++) {
                            html += '- ' + c.errors[i] + '<br>';
                        }
                    }

                    html += '</div></div>';
                    $result.html(html).show();
                } else {
                    SM.Utils.handleAjaxError(response);
                }
            });
        }
    };

    /**
     * Export tool — item picker UX for the unified Export & Import view.
     * Wires the per-section "Choose specific items…" toggles, the master
     * section checkboxes (cascade to items), the per-item count badge,
     * and the title filter shown when a section has more than 20 items.
     */
    SM.ExportTool = {
        init: function() {
            var self = this;

            $(document).on('click', '.sm-toggle-items', function(e) {
                e.preventDefault();
                var targetId = $(this).data('target');
                var $row = $('#' + targetId);
                if (!$row.length) return;
                var willShow = $row.prop('hidden');
                $row.prop('hidden', !willShow);
                $(this).attr('aria-expanded', willShow ? 'true' : 'false');
            });

            $(document).on('change', '.sm-export-section-toggle', function() {
                var section = $(this).val();
                var checked = this.checked;
                $('#sm-export-items-' + section + ' .sm-item-check').prop('checked', checked);
                self.updateCount(section);
            });

            $(document).on('change', '.sm-item-check', function() {
                var section = $(this).closest('.sm-item-list').data('section');
                if (!section) return;
                self.updateCount(section);
            });

            $(document).on('input', '.sm-item-filter', function() {
                var section = $(this).data('section');
                var query = ($(this).val() || '').toLowerCase();
                $('#sm-export-items-' + section + ' .sm-item-list li').each(function() {
                    var label = $(this).find('label').text().toLowerCase();
                    $(this).toggle(label.indexOf(query) !== -1);
                });
            });
        },

        updateCount: function(section) {
            var $row = $('.sm-export-row[data-section="' + section + '"]');
            var $badge = $row.find('.sm-item-count');
            if (!$badge.length) return;
            var total = parseInt($badge.data('total'), 10) || 0;
            var checked = $('#sm-export-items-' + section + ' .sm-item-check:checked').length;
            $badge.text(checked === total ? String(total) : (checked + ' / ' + total));
        }
    };

    /**
     * Unified import tool — preview + execute for any SlideMind export.
     * Operates on the root container #sm-export-import-root (standalone page
     * and Settings → Data tab both render this view).
     */
    SM.ImportTool = {
        _manifest: null,

        init: function() {
            var self = this;

            $(document).on('click', '#sm-import-root-analyze', function() {
                self.analyze();
            });

            $(document).on('click', '.sm-import-item-row .sm-import-action-select', function() {
                // No-op placeholder — selects handle their own state.
            });

            $(document).on('change', '.sm-import-section-toggle', function() {
                var section = $(this).data('section');
                var checked = this.checked;
                $('#sm-import-items-' + section + ' .sm-import-item-check').prop('checked', checked);
            });

            $(document).on('input', '.sm-import-item-filter', function() {
                var section = $(this).data('section');
                var query = $(this).val().toLowerCase();
                $('#sm-import-items-' + section + ' li').each(function() {
                    var title = $(this).find('.sm-import-item-title').text().toLowerCase();
                    $(this).toggle(title.indexOf(query) !== -1);
                });
            });

            $(document).on('click', '#sm-import-root-execute', function() {
                self.execute();
            });
        },

        analyze: function() {
            var self = this;
            var $file = $('#sm-import-root-file');
            var $paste = $('#sm-import-root-json');
            var $btn = $('#sm-import-root-analyze');
            var $preview = $('#sm-import-root-preview');

            var fileObj = $file[0].files && $file[0].files[0];
            var pasted = ($paste.val() || '').trim();

            if (!fileObj && !pasted) {
                SM.Utils.showToast(slidemindAdmin.strings.importProvideFile, 'error');
                return;
            }

            $btn.prop('disabled', true).text(slidemindAdmin.strings.importAnalyzing);

            var run = function(json) {
                SM.Utils.ajax('slidemind_import_preview', { json: json }, function(response) {
                    $btn.prop('disabled', false).text(slidemindAdmin.strings.importAnalyze);
                    if (!response.success) {
                        SM.Utils.handleAjaxError(response);
                        $preview.hide();
                        return;
                    }
                    self._manifest = response.data.manifest;
                    self.render(response.data.manifest, $preview);
                });
            };

            if (fileObj) {
                var reader = new FileReader();
                reader.onload = function(e) { run(e.target.result); };
                reader.readAsText(fileObj, 'UTF-8');
            } else {
                run(pasted);
            }
        },

        render: function(manifest, $preview) {
            var s = slidemindAdmin.strings;
            var html = '';

            html += '<div class="sm-notice sm-notice-info"><div>';
            html += '<strong>' + SM.Utils.esc(s.importSource) + ':</strong> ';
            html += SM.Utils.esc(manifest.source_format || '');
            if (manifest.source_site_url) html += ' · ' + SM.Utils.esc(manifest.source_site_url);
            if (manifest.generated_at) html += ' · ' + SM.Utils.esc(manifest.generated_at);
            html += '</div></div>';

            var hasAnyCollision = ['courses', 'modules', 'slides', 'templates'].some(function(section) {
                var items = manifest[section];
                if (!items || !items.length) return false;
                return items.some(function(item) {
                    return item.existing_match && item.existing_match.post_id;
                });
            });

            if (hasAnyCollision) {
                html += '<div style="margin: 12px 0;">';
                html += '<label>' + SM.Utils.esc(s.importDefaultForMatches) + ': ';
                html += '<select id="sm-import-default-action">';
                html += '<option value="skip" selected>' + SM.Utils.esc(s.importActionSkip) + '</option>';
                html += '<option value="duplicate">' + SM.Utils.esc(s.importActionDuplicate) + '</option>';
                html += '<option value="overwrite">' + SM.Utils.esc(s.importActionOverwrite) + '</option>';
                html += '</select>';
                html += '</label>';
                html += '</div>';
            }

            html += this.renderPostSection('courses', s.importSectionCourses, manifest.courses);
            html += this.renderPostSection('modules', s.importSectionModules, manifest.modules);
            html += this.renderPostSection('slides',  s.importSectionSlides,  manifest.slides);
            html += this.renderPostSection('templates', s.importSectionTemplates, manifest.templates);

            // Memory definitions
            if (manifest.memory_definitions && manifest.memory_definitions.length) {
                html += '<div class="sm-import-section">';
                html += '<h3>' + SM.Utils.esc(s.importSectionMemoryDefs) + ' (' + manifest.memory_definitions.length + ')</h3>';
                html += '<ul class="sm-item-list">';
                manifest.memory_definitions.forEach(function(def) {
                    var id = def.id || '';
                    html += '<li><label><input type="checkbox" class="sm-import-memdef-check" value="' + SM.Utils.esc(id) + '" checked> ' + SM.Utils.esc(def.name || def.variable_name || id) + '</label></li>';
                });
                html += '</ul></div>';
            }

            // Settings opt-in
            if (manifest.settings && Object.keys(manifest.settings).length) {
                html += '<div class="sm-import-section">';
                html += '<h3>' + SM.Utils.esc(s.importSectionSettings) + '</h3>';
                html += '<label><input type="checkbox" id="sm-import-apply-settings"> ' + SM.Utils.esc(s.importApplySettingsLabel) + '</label>';
                html += '</div>';
            }

            // Memory variables (archive-only)
            if (manifest.memory_variables && manifest.memory_variables.length) {
                html += '<div class="sm-import-section">';
                html += '<h3>' + SM.Utils.esc(s.importReportMemoryVariables) + ' (' + manifest.memory_variables.length + ')</h3>';
                html += '<p class="sm-muted">' + SM.Utils.esc(s.importMemoryVariablesNotice) + '</p>';
                html += '</div>';
            }

            html += '<p style="margin-top: 16px;"><button type="button" id="sm-import-root-execute" class="sm-btn sm-btn-primary">';
            html += '<span class="dashicons dashicons-upload"></span> ' + SM.Utils.esc(s.importStart);
            html += '</button></p>';

            $preview.html(html).show();

            // Apply initial default-action to all collision rows
            $('#sm-import-default-action').on('change', function() {
                var v = $(this).val();
                $('.sm-import-action-select[data-collision="1"]').val(v);
            });
        },

        renderPostSection: function(section, label, items) {
            if (!items || !items.length) return '';
            var s = slidemindAdmin.strings;
            var html = '';
            html += '<div class="sm-import-section">';
            html += '<h3>';
            html += '<label><input type="checkbox" class="sm-import-section-toggle" data-section="' + section + '" checked> ';
            html += SM.Utils.esc(label) + ' (' + items.length + ')</label>';
            html += '</h3>';

            if (items.length > 20) {
                html += '<input type="text" class="sm-form-input sm-import-item-filter" placeholder="' + SM.Utils.esc(s.importFilterPlaceholder) + '" data-section="' + section + '" style="margin-bottom: 8px; max-width: 360px;">';
            }

            html += '<ul class="sm-item-list" id="sm-import-items-' + section + '">';
            items.forEach(function(item) {
                var ref = item.file_ref;
                var title = item.title || item.name || '—';
                var hasMatch = item.existing_match && item.existing_match.post_id;
                html += '<li data-file-ref="' + SM.Utils.esc(ref) + '">';
                html += '<label><input type="checkbox" class="sm-import-item-check" data-section="' + section + '" data-file-ref="' + SM.Utils.esc(ref) + '" checked> ';
                html += '<span class="sm-import-item-title">' + SM.Utils.esc(title) + '</span>';
                html += '</label> ';
                if (hasMatch) {
                    html += '<span class="sm-badge sm-badge-warning">' + SM.Utils.esc(s.importCollisionExists) + '</span> ';
                    html += '<select class="sm-import-action-select" data-section="' + section + '" data-file-ref="' + SM.Utils.esc(ref) + '" data-collision="1">';
                    html += '<option value="skip" selected>' + SM.Utils.esc(s.importActionSkip) + '</option>';
                    html += '<option value="duplicate">' + SM.Utils.esc(s.importActionDuplicate) + '</option>';
                    html += '<option value="overwrite">' + SM.Utils.esc(s.importActionOverwrite) + '</option>';
                    html += '</select>';
                } else {
                    html += '<input type="hidden" class="sm-import-action-select" data-section="' + section + '" data-file-ref="' + SM.Utils.esc(ref) + '" data-collision="0" value="new">';
                }
                html += '</li>';
            });
            html += '</ul>';
            html += '</div>';
            return html;
        },

        execute: function() {
            var self = this;
            var s = slidemindAdmin.strings;
            if (!this._manifest) return;

            var decisions = { courses: {}, modules: {}, slides: {}, templates: {} };
            ['courses', 'modules', 'slides', 'templates'].forEach(function(section) {
                $('#sm-import-items-' + section + ' .sm-import-item-check').each(function() {
                    var $check = $(this);
                    if (!$check.prop('checked')) return;
                    var ref = $check.data('file-ref');
                    var $action = $('.sm-import-action-select[data-section="' + section + '"][data-file-ref="' + ref + '"]');
                    var action = $action.val() || 'new';
                    decisions[section][ref] = action;
                });
            });

            var total = 0;
            ['courses', 'modules', 'slides', 'templates'].forEach(function(k) {
                total += Object.keys(decisions[k]).length;
            });
            var applySettings = $('#sm-import-apply-settings').prop('checked');
            var memDefsCount  = $('.sm-import-memdef-check:checked').length;
            if (total === 0 && !applySettings && memDefsCount === 0) {
                SM.Utils.showToast(s.importNoSectionsSelected, 'error');
                return;
            }

            var payload = {
                decisions: JSON.stringify({
                    decisions: decisions,
                    apply_settings: $('#sm-import-apply-settings').prop('checked') ? 1 : 0,
                    selected_memory_definitions: $('.sm-import-memdef-check:checked').map(function() { return $(this).val(); }).get(),
                    scope: 'bulk'
                })
            };

            var $btn = $('#sm-import-root-execute');
            var origHtml = $btn.html();
            $btn.prop('disabled', true).html('<span class="dashicons dashicons-update sm-spin"></span> ' + s.importInProgress);

            SM.Utils.ajax('slidemind_import_execute', payload, function(response) {
                $btn.prop('disabled', false).html(origHtml);
                if (!response.success) {
                    SM.Utils.handleAjaxError(response);
                    return;
                }
                self.renderReport(response.data.report);
            });
        },

        renderReport: function(report) {
            var s = slidemindAdmin.strings;
            var $result = $('#sm-import-root-result');
            var html = '<div class="sm-notice sm-notice-success"><div>';
            html += '<strong>' + SM.Utils.esc(s.import_success) + '</strong><br>';
            html += s.importReportCreated + ': ' + (report.created ? report.created.length : 0) + '<br>';
            html += s.importReportOverwritten + ': ' + (report.overwritten ? report.overwritten.length : 0) + '<br>';
            html += s.importReportSkipped + ': ' + (report.skipped ? report.skipped.length : 0) + '<br>';
            if (report.holders) {
                var holderCount = (report.holders.courses ? report.holders.courses.length : 0) + (report.holders.modules ? report.holders.modules.length : 0);
                html += s.importReportHolders + ': ' + holderCount + '<br>';
            }
            html += s.importReportMemoryVariables + ': ' + (report.memory_variables_skipped || 0) + '<br>';
            html += s.importReportMemoryDefinitions + ': ' + (report.memory_definitions_imported || 0) + '<br>';
            html += s.importReportSettingsApplied + ': ' + (report.settings_applied ? s.importYes : s.importNo);
            if (report.errors && report.errors.length) {
                html += '<br><br><strong>' + s.importReportErrors + ':</strong>';
                html += '<ul>';
                report.errors.forEach(function(err) { html += '<li>' + SM.Utils.esc(err) + '</li>'; });
                html += '</ul>';
            }
            html += '</div></div>';
            $result.html(html).show();
        }
    };

    /**
     * Gestionnaire d'import/export (courses, modules, slides)
     */
    SM.ImportExport = {
        init: function() {
            var self = this;

            // Export course
            $(document).on('click', '.sm-export-course', function(e) {
                e.preventDefault();
                var id = $(this).data('course-id');
                self.exportItem('course', id);
            });

            // Export module
            $(document).on('click', '.sm-export-module', function(e) {
                e.preventDefault();
                var id = $(this).data('module-id');
                self.exportItem('module', id);
            });

            // Export slide
            $(document).on('click', '.sm-export-slide', function(e) {
                e.preventDefault();
                var id = $(this).data('slide-id');
                self.exportItem('slide', id);
            });

            // Lecture du fichier JSON sélectionné → remplit le textarea
            $(document).on('change', '#sm-import-file', function() {
                var file = this.files[0];
                if (!file) return;

                self.readFile(file, function(content) {
                    $('#sm-import-json').val(content);
                });
            });

            // Bouton import
            $(document).on('click', '#sm-do-import', function() {
                var type = $(this).data('type');
                var courseId = $('#sm-import-target-course').val() || 0;
                var moduleId = $('#sm-import-target-module').val() || 0;
                self.importItem(type, courseId, moduleId);
            });
        },

        /**
         * Lance l'export via AJAX et déclenche le téléchargement
         *
         * @param {string} type        'course'|'module'|'slide'
         * @param {number} id          Post ID
         */
        exportItem: function(type, id) {
            var actionMap = {
                'course':    'slidemind_export_course',
                'module':    'slidemind_export_module',
                'slide':     'slidemind_export_slide'
            };

            var dataMap = {
                'course':    { course_id: id },
                'module':    { module_id: id },
                'slide':     { slide_id: id }
            };

            var action = actionMap[type];
            if (!action) return;

            SM.Utils.showToast(slidemindAdmin.strings.exporting, 'success');

            SM.Utils.ajax(action, dataMap[type], function(response) {
                if (response.success) {
                    SM.ImportExport.downloadJson(response.data.json, response.data.filename);
                } else {
                    SM.Utils.showToast(response.data.message || slidemindAdmin.strings.error, 'error');
                }
            });
        },

        /**
         * Lance l'import via AJAX
         *
         * @param {string} type        'course'|'module'|'slide'
         * @param {number} courseId    ID course cible (pour module)
         * @param {number} moduleId    ID module cible (pour slide)
         */
        importItem: function(type, courseId, moduleId) {
            var json = $('#sm-import-json').val().trim();
            if (!json) {
                SM.Utils.showToast(slidemindAdmin.strings.import_json_label, 'error');
                return;
            }

            var scope = (type === 'course') ? 'course' : (type === 'module') ? 'module' : 'slide';

            // Preview first, then execute with a decisions map that selects only items of the scope.
            SM.Utils.ajax('slidemind_import_preview', { json: json }, function(response) {
                if (!response.success) {
                    SM.Utils.handleAjaxError(response);
                    return;
                }
                var manifest = response.data.manifest;

                // Auto-select all items of the scope; deselect the rest.
                // For collisions, use the action chosen in the modal dropdown (default: duplicate).
                var sectionKey = (scope === 'course') ? 'courses' : (scope === 'module') ? 'modules' : 'slides';
                var collisionAction = $('#sm-import-action').val() || 'duplicate';
                var decisions  = { courses: {}, modules: {}, slides: {}, templates: {} };
                (manifest[sectionKey] || []).forEach(function(item) {
                    decisions[sectionKey][item.file_ref] = item.existing_match ? collisionAction : 'new';
                });

                // Warn if the file has items in other sections the user won't get.
                var otherCount =
                    (manifest.courses && sectionKey !== 'courses' ? manifest.courses.length : 0) +
                    (manifest.modules && sectionKey !== 'modules' ? manifest.modules.length : 0) +
                    (manifest.slides && sectionKey !== 'slides' ? manifest.slides.length : 0);
                if (otherCount > 0) {
                    SM.Utils.showToast(slidemindAdmin.strings.importHeldForScope + ' (' + otherCount + ')', 'warning');
                }

                var payload = {
                    decisions: JSON.stringify({
                        decisions: decisions,
                        apply_settings: 0,
                        selected_memory_definitions: [],
                        scope: scope,
                        parent_course_id: courseId || 0,
                        parent_module_id: moduleId || 0
                    })
                };

                var $btn = $('#sm-do-import');
                var orig = $btn.html();
                $btn.prop('disabled', true).html('<span class="dashicons dashicons-update sm-spin"></span> ' + slidemindAdmin.strings.importing);

                SM.Utils.ajax('slidemind_import_execute', payload, function(resp) {
                    $btn.prop('disabled', false).html(orig);
                    if (!resp.success) {
                        SM.Utils.handleAjaxError(resp);
                        return;
                    }
                    var report = resp.data.report;
                    var createdCount = (report.created || []).length + (report.overwritten || []).length;
                    SM.Utils.showToast(
                        slidemindAdmin.strings.import_success + ' — ' +
                        SM.Utils.sprintf(slidemindAdmin.strings.import_created, createdCount),
                        'success'
                    );
                    SM.Modal.close();
                    setTimeout(function() { location.reload(); }, 1200);
                });
            });
        },

        /**
         * Déclenche le téléchargement d'un fichier JSON
         *
         * @param {string} json      Contenu JSON
         * @param {string} filename  Nom du fichier
         */
        downloadJson: function(json, filename) {
            var blob = new Blob([json], { type: 'application/json' });
            var url = URL.createObjectURL(blob);
            var a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        },

        /**
         * Lit un fichier via FileReader et appelle le callback avec son contenu
         *
         * @param {File}     file     Fichier à lire
         * @param {Function} callback Appelé avec le contenu (string)
         */
        readFile: function(file, callback) {
            var reader = new FileReader();
            reader.onload = function(e) {
                if (callback) callback(e.target.result);
            };
            reader.readAsText(file, 'UTF-8');
        }
    };

    /**
     * Gestionnaire des fournisseurs LLM
     */
    SM.Providers = {
        init: function() {
            var self = this;

            // Provider select change (settings, course, slide, wizard, sandbox)
            $(document).on('change', '.sm-provider-select', function() {
                var provider = $(this).val() || '';
                if (!provider) return;

                var $card = $(this).closest('.sm-card, .sm-wizard-panel, .sm-sandbox-sidebar, [data-tab-content]');
                self.reloadModels(provider, $card);
            });

            // Test provider key buttons (settings page)
            $(document).on('click', '.sm-test-provider-key', function(e) {
                e.preventDefault();
                var provider = $(this).data('provider');
                self.testProviderKey(provider);
            });
        },

        /**
         * Recharge les modèles via AJAX pour un fournisseur donné
         */
        reloadModels: function(provider, $context) {
            if (!provider) return;

            // Find model select in context or on page
            var $select = $context ? $context.find('.sm-model-select') : $('.sm-model-select');
            if (!$select.length) {
                // Try broader scope — the defaults tab on settings page
                $select = $('[data-tab-content="defaults"] .sm-model-select');
            }
            if (!$select.length) return;

            // Skip if already loaded for this provider
            if ($select.data('current-provider') === provider) return;

            var currentVal = $select.val();

            // Destroy Select2 temporarily
            if ($.fn.select2 && $select.data('select2')) {
                $select.select2('destroy');
            }

            $select.html('<option value="">' + slidemindAdmin.strings.loading + '</option>');

            SM.Utils.ajax('slidemind_get_models', { provider: provider }, function(response) {
                if (response.success) {
                    var groups = response.data.models || {};
                    var hasModels = false;

                    for (var k in groups) {
                        if (groups.hasOwnProperty(k) && groups[k].length) { hasModels = true; break; }
                    }

                    if (!hasModels) {
                        $select.html('<option value="">' + slidemindAdmin.strings.noModelsEnterKey + '</option>');
                    } else {
                        var html = '<option value="">' + slidemindAdmin.strings.selectModel + '</option>';
                        for (var group in groups) {
                            if (!groups.hasOwnProperty(group)) continue;
                            html += '<optgroup label="' + group + '">';
                            for (var i = 0; i < groups[group].length; i++) {
                                var m = groups[group][i];
                                var sel = m.id === currentVal ? ' selected' : '';
                                var pricingAttr = m.pricing ? " data-pricing='" + JSON.stringify(m.pricing) + "'" : '';
                                html += '<option value="' + m.id + '"' + sel + pricingAttr + '>' + m.name + '</option>';
                            }
                            html += '</optgroup>';
                        }
                        $select.html(html);
                    }

                    $select.data('current-provider', provider);

                    // Re-init Select2
                    if ($.fn.select2) {
                        $select.select2({ width: '100%' });
                    }
                }
            });
        },

        /**
         * Teste une clé API pour un fournisseur spécifique
         */
        testProviderKey: function(provider) {
            var $input = $('#sm-api-key-' + provider);
            var $btn = $input.closest('.sm-provider-key-group').find('.sm-test-provider-key');
            var $result = $('[data-provider-result="' + provider + '"]');
            var apiKey = $input.val();

            if (!apiKey || apiKey.indexOf('****') !== -1) {
                SM.Utils.showToast(slidemindAdmin.strings.enterValidApiKey, 'error');
                return;
            }

            $btn.prop('disabled', true).text(slidemindAdmin.strings.testing);

            SM.Utils.ajax('slidemind_test_api_key', { api_key: apiKey, provider: provider }, function(response) {
                $btn.prop('disabled', false).text(slidemindAdmin.strings.testButton);

                if (response.success) {
                    $result
                        .removeClass('sm-notice-error')
                        .addClass('sm-notice-success')
                        .html('<strong>' + slidemindAdmin.strings.validKey + '</strong>')
                        .show();
                } else {
                    $result
                        .removeClass('sm-notice-success')
                        .addClass('sm-notice-error')
                        .html('<strong>' + slidemindAdmin.strings.invalidKey + ':</strong> ' + response.data.message)
                        .show();
                }
            });
        }
    };

    /**
     * Initialisation
     */
    $(document).ready(function() {
        SM.CopyHandler.init();
        SM.Modal.init();
        SM.Tabs.init();
        SM.Providers.init();
        SM.Settings.init();
        SM.Configurations.init();
        SM.Courses.init();
        SM.Modules.init();
        SM.Slides.init();
        SM.CodeGenerator.init();
        SM.EditorVars.init();
        SM.SaveKeys.init();
        SM.OutputVariables.init();
        SM.MemoryVariables.init();
        SM.Templates.init();
        SM.WidgetAppearance.init();
        SM.ImportExport.init();
        SM.BulkData.init();
        if (SM.ExportTool && typeof SM.ExportTool.init === 'function') {
            SM.ExportTool.init();
        }
        if (SM.ImportTool && typeof SM.ImportTool.init === 'function') {
            SM.ImportTool.init();
        }
        SM.Memory.init();
        SM.Autocomplete.init();
        SM.PromptGenerator.init();

        // Global collapsible toggle handler
        $(document).on('click', '.sm-collapsible-toggle', function() {
            if ($(this).hasClass('sm-collapsible-disabled')) return;
            $(this).toggleClass('open');
            $(this).next('.sm-collapsible-content').toggleClass('open');
        });

        // Clickable variable tags — insert into nearest textarea
        $(document).on('click', '.sm-clickable-var', function() {
            var tag = $(this).text().trim();
            var $textarea = $(this).closest('.sm-card-body, .sm-wizard-panel, .sm-form-group').find('textarea').first();
            if (!$textarea.length) return;

            var textarea = $textarea[0];
            var start = textarea.selectionStart || $textarea.val().length;
            var end = textarea.selectionEnd || start;
            var text = $textarea.val();

            $textarea.val(text.substring(0, start) + tag + text.substring(end));
            textarea.selectionStart = textarea.selectionEnd = start + tag.length;
            $textarea.focus();
            SM.Utils.showToast(tag + ' ' + slidemindAdmin.strings.inserted, 'success');
        });

        // Toggle afficher/masquer clé API
        $(document).on('click', '.sm-toggle-password', function(e) {
            e.preventDefault();
            var $target = $($(this).data('target'));
            var isPassword = $target.attr('type') === 'password';
            $target.attr('type', isPassword ? 'text' : 'password');
            $(this).find('.dashicons')
                .toggleClass('dashicons-visibility', !isPassword)
                .toggleClass('dashicons-hidden', isPassword);
        });

        // Supprimer la clé API dédiée (revenir à la globale)
        $(document).on('click', '.sm-clear-apikey', function(e) {
            e.preventDefault();
            var $target = $($(this).data('target'));
            if (confirm(slidemindAdmin.strings.confirmDeleteApiKey)) {
                $target.val('__CLEAR__');
                $(this).closest('.sm-form-help').text(slidemindAdmin.strings.apiKeyWillBeCleared);
            }
        });

        // Toggle scope au chargement (legacy)
        var $scope = $('#sm-usage-scope');
        if ($scope.length) {
            SM.Configurations.toggleScopeFields($scope.val());
        }

        // Init Select2 globally
        if ($.fn.select2) {
            $('.sm-model-select, .sm-course-select, .sm-module-select').select2({
                width: '100%'
            });
        }

        // Model pricing display on change (slide-edit, settings)
        $(document).on('change', '.sm-model-select', function() {
            var $select = $(this);
            var $option = $select.find(':selected');
            var $container = $select.closest('.sm-form-group').find('.sm-model-pricing');
            if (!$container.length) return;

            var pricing = $option.data('pricing');
            var text = SM.Utils.formatModelPricing(pricing);

            if (text) {
                $container.find('.sm-pricing-text').text(text);
                $container.show();
            } else {
                $container.hide();
            }
        });

        // Show pricing for initially selected model
        $('.sm-model-select').each(function() {
            var $select = $(this);
            var $option = $select.find(':selected');
            var $container = $select.closest('.sm-form-group').find('.sm-model-pricing');
            if (!$container.length || !$option.val()) return;

            var pricing = $option.data('pricing');
            var text = SM.Utils.formatModelPricing(pricing);
            if (text) {
                $container.find('.sm-pricing-text').text(text);
                $container.show();
            }
        });
    });

    // Styles pour les toasts (ajoutés dynamiquement)
    $('<style>')
        .text(`
            .sm-toast {
                position: fixed;
                bottom: 20px;
                right: 20px;
                padding: 14px 24px;
                background: #1f2937;
                color: #fff;
                border-radius: 8px;
                font-size: 14px;
                z-index: 100001;
                opacity: 0;
                transform: translateY(20px);
                transition: all 0.3s ease;
                box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2);
            }
            .sm-toast.show {
                opacity: 1;
                transform: translateY(0);
            }
            .sm-toast-success {
                background: #10b981;
            }
            .sm-toast-error {
                background: #ef4444;
            }
            .sm-toast-warning {
                background: #f59e0b;
            }
            .sm-pg-variables-list {
                max-height: 180px;
                overflow-y: auto;
                border: 1px solid #e5e7eb;
                border-radius: 6px;
                padding: 10px 12px;
                background: #f9fafb;
            }
            .sm-pg-var-group {
                margin-bottom: 8px;
            }
            .sm-pg-var-group:last-child {
                margin-bottom: 0;
            }
            .sm-spinner-inline {
                display: inline-block;
                width: 14px;
                height: 14px;
                border: 2px solid rgba(255,255,255,0.3);
                border-top-color: #fff;
                border-radius: 50%;
                animation: sm-spin 0.6s linear infinite;
                vertical-align: middle;
                margin-right: 4px;
            }
            @keyframes sm-spin {
                to { transform: rotate(360deg); }
            }
        `)
        .appendTo('head');

})(jQuery);
