/**
 * TalkGenAI Admin JavaScript
 * Handles admin interface interactions
 * Updated: 2025-09-28 13:26:25
 */

(function($) {
    'use strict';
    
    // Gate TalkGenAI console logs by WP_DEBUG (from localized talkgenai_ajax.debug)
    try {
        var __tgai_isDebug = !!(window.talkgenai_ajax && Number(window.talkgenai_ajax.debug) === 1);
        var __tgai_console_log = console.log ? console.log.bind(console) : function(){};
        console.log = function() {
            if (!__tgai_isDebug) {
                try {
                    var first = arguments[0];
                    if (typeof first === 'string' && first.indexOf('TalkGenAI:') === 0) {
                        return; // silence plugin logs in production
                    }
                } catch(e) { /* ignore */ }
            }
            return __tgai_console_log.apply(console, arguments);
        };
    } catch(e) { /* ignore */ }
    
    // Global variables
    let currentAppData = null;
    let isGenerating = false;
    let currentMode = 'create'; // 'create' or 'modify'
    let progressInterval = null;
    
    // Progress messages for AI generation (extended up to ~160s)
    const progressMessages = [
        { text: "🤖 Connecting to AI server...", progress: 10 },
        { text: "⏱️ This can take up to 2 minutes — please wait...", progress: 15 },
        { text: "📝 Analyzing your request...", progress: 20 },
        { text: "🧠 Understanding intent & context...", progress: 25 },
        { text: "⚡ Generating application code...", progress: 40 },
        { text: "🧱 Building layout structure...", progress: 50 },
        { text: "🎨 Creating beautiful styling...", progress: 60 },
        { text: "🔧 Adding interactive features...", progress: 70 },
        { text: "🧩 Integrating components...", progress: 78 },
        { text: "🔍 Validating logic & behavior...", progress: 84 },
        { text: "🛡️ Security validation & sanitization...", progress: 90 },
        { text: "📦 Packaging assets...", progress: 94 },
        { text: "🧪 Quick test run...", progress: 96 },
        { text: "🛰️ Preparing results...", progress: 98 },
        { text: "✨ Finalizing your application...", progress: 100 },
        // Extended waiting guidance (keeps user informed up to ~160s)
        { text: "⏳ Almost done — please keep this tab open...", progress: 102 },
        { text: "📡 Do not close or refresh — final chunks are coming...", progress: 104 },
        { text: "🧭 Still working — larger requests take a bit longer...", progress: 106 },
        { text: "🔒 Final checks in progress...", progress: 108 },
        { text: "🚀 Wrapping up — thanks for your patience...", progress: 110 },
        { text: "⏳ Still processing — almost there...", progress: 112 },
        { text: "📡 Stable connection detected — delivering results...", progress: 114 },
        { text: "🧹 Tidying up output...", progress: 116 },
        { text: "✅ Nearly complete — just a moment more...", progress: 118 },
        { text: "⏳ It’s taking a bit longer than usual, but progressing...", progress: 120 }

    ];
    
    // Progress messages for app ideas analysis
    const ideasProgressMessages = [
        { text: "🤖 Connecting to AI server...", progress: 15 },
        { text: "🌐 Analyzing your website...", progress: 30 },
        { text: "🔍 Extracting key information...", progress: 50 },
        { text: "💡 Generating app ideas...", progress: 70 },
        { text: "🎯 Filtering for the best relevant ideas...", progress: 85 },
        { text: "✨ Finalizing your app suggestions...", progress: 95 },
        { text: "⏳ Almost done! This may take a few more seconds...", progress: 100 },
        { text: "✨ Finalizing your app suggestions...", progress: 105 }
    ];
    
    // Progress messages for article generation (extended up to ~160s)
    const articleProgressMessages = [
        { text: "🤖 Connecting to AI server...", progress: 10 },
        { text: "⏱️ This can take up to 2 minutes — please wait...", progress: 15 },
        { text: "📝 Analyzing article requirements...", progress: 20 },
        { text: "🧠 Understanding topic & context...", progress: 25 },
        { text: "📚 Researching content structure...", progress: 40 },
        { text: "✍️ Writing article content...", progress: 50 },
        { text: "📖 Crafting paragraphs & sections...", progress: 60 },
        { text: "🎨 Formatting & styling text...", progress: 70 },
        { text: "🔍 Checking grammar & coherence...", progress: 78 },
        { text: "📊 Adding meta description & SEO...", progress: 84 },
        { text: "🛡️ Final validation & quality check...", progress: 90 },
        { text: "📦 Preparing HTML output...", progress: 94 },
        { text: "🧪 Quick review...", progress: 96 },
        { text: "🛰️ Packaging results...", progress: 98 },
        { text: "✨ Finalizing your article...", progress: 100 },
        // Extended waiting guidance (keeps user informed up to ~160s)
        { text: "⏳ Almost done — please keep this tab open...", progress: 102 },
        { text: "📡 Do not close or refresh — final content is coming...", progress: 104 },
        { text: "🧭 Still working — longer articles take a bit more time...", progress: 106 },
        { text: "🔒 Final checks in progress...", progress: 108 },
        { text: "🚀 Wrapping up — thanks for your patience...", progress: 110 },
        { text: "⏳ Still processing — almost there...", progress: 112 },
        { text: "📡 Stable connection detected — delivering article...", progress: 114 },
        { text: "🧹 Tidying up output...", progress: 116 },
        { text: "✅ Nearly complete — just a moment more...", progress: 118 },
        { text: "⏳ It's taking a bit longer than usual, but progressing...", progress: 120 }
    ];
    
    $(document).ready(function() {
        initializeAdmin();
    });
    
    /**
     * Initialize admin functionality
     */
    function initializeAdmin() {
        console.log('TalkGenAI: initializeAdmin() called - CHUNKED SAVE VERSION LOADED');
        
        // Show debug-only UI when WP_DEBUG is true
        var isDebug = !!(window.talkgenai_ajax && Number(window.talkgenai_ajax.debug) === 1);
        if (isDebug) {
            // Add visual indicator that new version is loaded
            if ($('.wrap h1').length && !$('#chunked-save-indicator').length) {
                $('<div id="chunked-save-indicator" style="background: #28a745; color: white; padding: 5px 10px; border-radius: 3px; margin: 10px 0; font-size: 12px;">✅ Chunked Save System Active</div>')
                    .insertAfter('.wrap h1');
            }
            // Create debug test button first
            createDebugTestButton();
            createSimpleTestButton();
        }
        
        // Header scroll shadow elevation
        bindHeaderScroll();

        // Example card click handler (moved from inline PHP script)
        bindExampleCards();

        // Bind event handlers
        bindGenerateForm();
        bindEditForm();
        bindDeleteActions();
        bindCopyShortcode();
        bindConnectionTest();
        bindPreviewActions(); // Ensure Save App button works on edit page
        bindAdditionalActions(); // Bind new Generate Articles and Get App Ideas buttons
        bindErrorOverlayDismiss(); // Make "Keep Calculator" always dismissable (preview sanitizer strips onclick)
        
        // If edit page preloaded app into window.currentAppData, adopt it here
        try {
            if (!currentAppData && window.currentAppData && typeof window.currentAppData === 'object') {
                console.log('ADMIN.JS: Adopting preloaded currentAppData:', window.currentAppData);
                currentAppData = window.currentAppData;
                
                // Check if preview container already has content (from server-side render)
                const $container = $('#talkgenai-preview-container');
                const hasServerContent = $container.length && $container.html().trim().length > 0;
                console.log('ADMIN.JS: Preview container has server content:', hasServerContent);
                
                // If spec missing critical fields, load full record from DB
                const spec = currentAppData && currentAppData.json_spec;
                const missingCore = !spec || typeof spec !== 'object' || !spec.appClass || !spec.appType;
                console.log('ADMIN.JS: Missing core spec fields:', missingCore);
                
                if (missingCore && currentAppData.id) {
                    // DON'T clear existing content, just ensure visibility
                    if ($container.length) {
                        $('#talkgenai-preview-area').show();
                        $('#talkgenai-preview-placeholder').hide();
                        // Only set HTML if container is empty
                        if (!hasServerContent && currentAppData.html) {
                            $container.html(currentAppData.html);
                        }
                    }
                    // Then load full spec but don't re-render unless necessary
                    loadAppData(currentAppData.id, function(appData){
                        console.log('ADMIN.JS: Loaded full app data:', appData);
                        if (appData) {
                            // Merge the loaded data, ensuring we have proper json_spec
                            currentAppData = {
                                ...currentAppData,
                                ...appData
                            };
                            
                            // Parse json_spec if it's a string
                            if (typeof currentAppData.json_spec === 'string') {
                                try {
                                    currentAppData.json_spec = JSON.parse(currentAppData.json_spec);
                                } catch(e) {
                                    console.error('ADMIN.JS: Error parsing json_spec:', e);
                                }
                            }
                            
                            console.log('ADMIN.JS: Final currentAppData after merge:', currentAppData);
                            console.log('ADMIN.JS: json_spec type:', typeof currentAppData.json_spec);
                            console.log('ADMIN.JS: json_spec content:', currentAppData.json_spec);
                        }
                        updateButtonText();
                        // Only update preview if we have better content
                        if (appData && appData.html && appData.html !== currentAppData.html) {
                            showPreview(appData.html, appData.js || '', appData.css || '');
                        }
                    });
                } else {
                    // Preserve existing content, just ensure visibility and execute JS
                    if ($container.length) {
                        $('#talkgenai-preview-area').show();
                        $('#talkgenai-preview-placeholder').hide();
                        // Only set HTML if container is empty
                        if (!hasServerContent && currentAppData.html) {
                            $container.html(currentAppData.html);
                        }
                        // Execute JS if available
                        if (currentAppData.js) {
                            try {
                                setTimeout(function() {
                                    eval(currentAppData.js);
                                }, 100);
                            } catch(e) {
                                console.warn('ADMIN.JS: Error executing app JS:', e);
                            }
                        }
                    }
                }
            }
        } catch(e) {
            console.error('ADMIN.JS: Error in currentAppData adoption:', e);
        }

        // Show header button if we're editing an existing app
        if (currentAppData || $('#talkgenai-preview-area').is(':visible')) {
            $('#generate-new-btn-header').show();
        }
        bindAutoDetect();
        bindBulkActions();
        // Debug-only bindings
        var isDebug = !!(window.talkgenai_ajax && Number(window.talkgenai_ajax.debug) === 1);
        if (isDebug) {
            bindDebugServerTest();
            createSimpleTestButton();
        }
        
        // Initialize UI components
        initializeTooltips();
        initializeNotifications();
        updateButtonText();
        
        console.log('TalkGenAI Admin initialized');
    }

    /**
     * Dismiss the preserved-calculator error overlay.
     *
     * IMPORTANT: showPreview() runs sanitizeHtml(), which removes ALL "on*" attributes (onclick, etc).
     * So overlay dismissal must be implemented in admin.js via event delegation.
     */
    function bindErrorOverlayDismiss() {
        try {
            if (window.__talkgenai_overlay_dismiss_bound) return;
            window.__talkgenai_overlay_dismiss_bound = true;

            // Button click: dismiss overlay
            $(document).on('click', '#talkgenai-dismiss-btn', function(e) {
                try {
                    e.preventDefault();
                    e.stopPropagation();
                } catch (_) {}
                $('#talkgenai-error-overlay').remove();
                return false;
            });

            // Click backdrop (outside modal): dismiss overlay
            $(document).on('click', '#talkgenai-error-overlay', function(e) {
                if (e && e.target === this) {
                    $('#talkgenai-error-overlay').remove();
                }
            });

            // Prevent clicks inside the modal from bubbling to the overlay
            $(document).on('click', '#talkgenai-error-modal', function(e) {
                try {
                    e.stopPropagation();
                } catch (_) {}
            });
        } catch (e) {
            console.error('TalkGenAI: Failed to bind overlay dismiss handlers:', e);
        }
    }
    
    /**
     * Create debug test button
     */
    function createDebugTestButton() {
        console.log('TalkGenAI: createDebugTestButton() called');
        
        // Remove existing button if it exists
        $('#test-server-limits-btn').remove();
        
        // Add debug server limits test button to admin interface
        console.log('TalkGenAI: Looking for .wrap h1 elements:', $('.wrap h1').length);
        console.log('TalkGenAI: Current page URL:', window.location.href);
        
        if ($('.wrap h1').length) {
            console.log('TalkGenAI: Creating debug test button after h1');
            const $button = $('<button type="button" id="test-server-limits-btn" style="margin: 10px; padding: 5px 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;">Test Server Limits</button>');
            $button.insertAfter('.wrap h1');
            console.log('TalkGenAI: Button inserted, checking if it exists:', $('#test-server-limits-btn').length);
        } else {
            console.warn('TalkGenAI: No .wrap h1 elements found, trying alternatives');
            
            // Try alternative locations
            if ($('.wrap').length) {
                console.log('TalkGenAI: Trying to insert button in .wrap');
                const $button = $('<button type="button" id="test-server-limits-btn" style="margin: 10px; padding: 5px 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;">Test Server Limits</button>');
                $('.wrap').prepend($button);
                console.log('TalkGenAI: Button inserted in wrap, checking if it exists:', $('#test-server-limits-btn').length);
            } else if ($('#wpbody-content').length) {
                console.log('TalkGenAI: Trying to insert button in #wpbody-content');
                const $button = $('<button type="button" id="test-server-limits-btn" style="margin: 10px; padding: 5px 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;">Test Server Limits</button>');
                $('#wpbody-content').prepend($button);
                console.log('TalkGenAI: Button inserted in wpbody-content, checking if it exists:', $('#test-server-limits-btn').length);
            } else {
                console.error('TalkGenAI: Could not find any suitable location to insert button');
            }
        }
        
        // Verify button exists and add immediate click handler
        const $testBtn = $('#test-server-limits-btn');
        if ($testBtn.length) {
            console.log('TalkGenAI: Button found, adding immediate click handler');
            $testBtn.on('click.immediate', function(e) {
                e.preventDefault();
                console.log('TalkGenAI: IMMEDIATE - Debug test button clicked!');
                alert('Button clicked! This means the button is working.');
            });
        } else {
            console.error('TalkGenAI: Button was not created successfully');
        }
    }

    /**
     * Update button text based on current mode
     */
    function updateButtonText() {
        const buttonText = $('#button-text');
        
        if (currentAppData && currentAppData.json_spec) {
            buttonText.text('Modify App 🚀');
            currentMode = 'modify';
        } else {
            buttonText.text('Generate App 🚀');
            currentMode = 'create';
        }
        console.log('TalkGenAI: Button updated to mode:', currentMode);
    }
    
    /**
     * Set save button attention state (pulsing animation)
     */
    function setSaveButtonAttention(enabled) {
        const $saveBtn = $('#save-app-btn');
        if (enabled) {
            $saveBtn.addClass('tgai-attention');
        } else {
            $saveBtn.removeClass('tgai-attention');
        }
    }
    
    
    /**
     * Header scroll shadow elevation
     */
    function bindHeaderScroll() {
        const $header = $('.talkgenai-header-bar');
        if (!$header.length) return;
        $(window).on('scroll.tgai-header', function() {
            if ($(window).scrollTop() > 10) {
                $header.addClass('scrolled');
            } else {
                $header.removeClass('scrolled');
            }
        });
    }

    /**
     * Example card click handler
     */
    function bindExampleCards() {
        $(document).on('click', '.talkgenai-example-card', function() {
            var exampleText = $(this).attr('data-example');
            if (!exampleText) return;

            var $chatInput = $('#tgai-chat-input');
            var $appDescription = $('#app_description');
            var $targetField = null;

            if ($chatInput.length > 0 && $chatInput.is(':visible')) {
                $targetField = $chatInput;
            } else if ($appDescription.length > 0) {
                $targetField = $appDescription;
            }

            if ($targetField && $targetField.length > 0) {
                $targetField.val(exampleText);
                $targetField.focus();

                // Brief highlight
                $targetField.css({
                    'background': '#f5f0ff',
                    'border-color': '#667eea',
                    'transition': 'all 0.3s ease'
                });
                setTimeout(function() {
                    $targetField.css({
                        'background': '',
                        'border-color': ''
                    });
                }, 1500);

                // Scroll to field
                $('html, body').animate({
                    scrollTop: $targetField.offset().top - 100
                }, 400);

                if (typeof showNotification === 'function') {
                    showNotification('Example loaded! Press Enter or click the arrow to generate.', 'success');
                }
            }
        });
    }

    /**
     * Bind generate app form
     */
    function bindGenerateForm() {
        // Chat UI bootstrap (lightweight, admin-only)
        const $descArea = $('#app_description');
        if ($descArea.length && !$('#tgai-chat').length) {
            const chatHtml = [
                '<div id="tgai-chat-container">',
                '  <div id="tgai-chat-transcript"></div>',
                '  <div id="tgai-chat-progress">',
                '    <div id="tgai-progress-spinner"></div>',
                '    <span id="tgai-progress-message">Connecting to AI server...</span>',
                '    <div id="tgai-progress-bar"></div>',
                '  </div>',
                '  <div id="tgai-chat-composer">',
                '    <textarea id="tgai-chat-input" rows="6" placeholder="Describe your app... Press Enter to send (Shift+Enter for newline)"></textarea>',
                '    <button type="button" id="tgai-chat-send">',
                '      <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></svg>',
                '    </button>',
                '  </div>',
                '</div>'
            ].join('');
            const $host = $descArea.closest('.form-field, .talkgenai-generation-form, form');
            $host.after(chatHtml);
            
            // Add rainbow border animation on page load
            setTimeout(function() {
                const $chatInput = $('#tgai-chat-input');
                $chatInput.addClass('tgai-initial-animation');
                setTimeout(function() {
                    $chatInput.removeClass('tgai-initial-animation');
                }, 2000);
            }, 100);
            
            // Hide the original textarea area to avoid duplicate input UX
            $host.find('#app_description').closest('div, p, .form-field, tr, td, .inside').hide();
            // Hide the original Generate/Modify submit button; chat Send will handle it
            $('#talkgenai-generate-form').find('button[type="submit"], input[type="submit"]').closest('p, div, .submit, .form-field').hide();

            // Keep composer lean; cap messages and size
            window.tgaiChat = { messages: [], maxMessages: 10, maxChars: 2000 };
            const tgaiChat = window.tgaiChat; // local reference for strict mode

            const appendMsg = (role, content, isHtml = false) => {
                const $t = $('#tgai-chat-transcript');
                // Remove typing indicator when AI responds
                if (role === 'ai' || role === 'assistant') {
                    $t.find('.tgai-typing-indicator').remove();
                }
                // If isHtml is true, use content as-is; otherwise escape it for safety
                const safe = isHtml ? content : $('<div/>').text(content).html();
                const timestamp = new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
                const messageHtml = [
                    '<div class="tgai-chat-message ' + role + '">',
                    '  <div class="message-content">' + safe + '</div>',
                    '  <div class="message-time">' + timestamp + '</div>',
                    '</div>'
                ].join('');
                $t.append(messageHtml);
                $t.scrollTop($t[0].scrollHeight);
            };

            const showTypingIndicator = () => {
                const $t = $('#tgai-chat-transcript');
                if (!$t.find('.tgai-typing-indicator').length) {
                    $t.append('<div class="tgai-typing-indicator"><span class="dot"></span><span class="dot"></span><span class="dot"></span></div>');
                    $t.scrollTop($t[0].scrollHeight);
                }
            };
            
            // Make appendMsg globally accessible for AJAX callbacks
            window.tgaiAppendMsg = appendMsg;

            const sendChat = () => {
                const $input = $('#tgai-chat-input');
                let text = ($input.val() || '').trim();
                if (!text) return;
                // Limits
                if (text.length > tgaiChat.maxChars) text = text.slice(0, tgaiChat.maxChars);
                tgaiChat.messages.push({ role: 'user', content: text });
                if (tgaiChat.messages.length > tgaiChat.maxMessages) tgaiChat.messages.shift();
                appendMsg('user', text);
                $input.val('');
                showTypingIndicator();

                // Use existing generate/modify flow with SAFE prompt
                // IMPORTANT: Never send the full chat transcript to the backend.
                // The transcript can include AI HTML (upgrade prompts, overlays) which pollutes the AI request.
                const cleanText = String(text)
                    // Strip HTML tags if user pasted HTML
                    .replace(/<\/?[a-z][^>]*>/gi, ' ')
                    // Collapse whitespace
                    .replace(/\s+/g, ' ')
                    .trim();
                const compactPrompt = ('User: ' + cleanText).slice(0, tgaiChat.maxChars || 2000);

                // DEBUG: Check what we have
                console.log('[TalkGenAI-Chat-DEBUG] currentAppData RAW:', currentAppData);
                console.log('[TalkGenAI-Chat-DEBUG] window.currentAppData RAW:', window.currentAppData);
                console.log('[TalkGenAI-Chat-DEBUG] currentAppData analyzed:', {
                    localType: typeof currentAppData,
                    localValue: currentAppData,
                    globalType: typeof window.currentAppData,
                    globalHasSpec: !!(window.currentAppData && window.currentAppData.json_spec),
                    hasData: !!currentAppData,
                    hasSpec: !!(currentAppData && currentAppData.json_spec),
                    specType: currentAppData?.json_spec ? typeof currentAppData.json_spec : 'undefined',
                    appClass: currentAppData?.json_spec?.appClass,
                    appType: currentAppData?.json_spec?.appType
                });

                // CRITICAL FIX: If local currentAppData is empty but window.currentAppData has data, use it!
                if (!currentAppData && window.currentAppData && typeof window.currentAppData === 'object') {
                    console.log('[TalkGenAI-Chat-DEBUG] ⚠️ Local currentAppData is empty, adopting from window.currentAppData');
                    currentAppData = window.currentAppData;
                }

                if (currentAppData && currentAppData.json_spec) {
                    // Check if json_spec is valid before using it
                    let validSpec = currentAppData.json_spec;

                    const missingCore = (
                        !validSpec ||
                        Array.isArray(validSpec) ||
                        typeof validSpec !== 'object' ||
                        !validSpec.appClass ||
                        !validSpec.appType
                    );
                    const missingFields = (
                        !missingCore && (
                            !validSpec.form ||
                            !Array.isArray(validSpec.form.fields) ||
                            validSpec.form.fields.length === 0
                        )
                    );

                    if (missingCore) {
                        console.warn('CHAT: Spec missing core fields; attempting one reload. keys=', Object.keys(validSpec || {}));
                        // Retry once: reload full spec from server, then retry modify
                        if (!tgaiChat._retriedSpecOnce && currentAppData && currentAppData.id && typeof loadAppData === 'function') {
                            tgaiChat._retriedSpecOnce = true;
                            console.log('CHAT: Reloading app spec from server for app id', currentAppData.id);
                            loadAppData(currentAppData.id, function(appData){
                                try {
                                    if (appData) {
                                        // Merge and normalize json_spec if string
                                        currentAppData = { ...currentAppData, ...appData };
                                        if (typeof currentAppData.json_spec === 'string') {
                                            try { currentAppData.json_spec = JSON.parse(currentAppData.json_spec); } catch(_) { /* ignore */ }
                                        }
                                        const spec2 = currentAppData.json_spec;
                                        const ok = spec2 && typeof spec2 === 'object' && spec2.appClass && spec2.appType;
                                        console.log('CHAT: Reloaded spec ok?', ok, ' keys=', spec2 && Object.keys(spec2));
                                        if (ok) { 
                                            console.log('[TalkGenAI-Chat-DEBUG] ✅ Calling modifyApp() with reloaded spec');
                                            modifyApp(compactPrompt, spec2); 
                                            return; 
                                        }
                                    }
                                } catch(e) { console.error('CHAT: Error after reloading spec', e); }
                                console.log('[TalkGenAI-Chat-DEBUG] ❌ Spec validation failed after reload - showing error');
                                appendMsg('ai', '❌ Sorry, this app cannot be modified because its specification is corrupted. Please try again or recreate the app.');
                            });
                            return;
                        }
                        console.log('[TalkGenAI-Chat-DEBUG] ❌ Spec validation failed - no retry attempted - showing error');
                        appendMsg('ai', '❌ Sorry, this app cannot be modified because its specification is corrupted in the database. The app can be viewed but not modified.');
                        return;
                    }

                    // If only fields are missing, allow style-only changes to proceed
                    if (missingFields) {
                        console.warn('CHAT: Spec has empty fields but core is valid. Proceeding for style-only modification.');
                        console.log('[TalkGenAI-Chat-DEBUG] ⚠️ Calling modifyApp() with incomplete spec (missing fields)');
                        modifyApp(compactPrompt, validSpec);
                        return;
                    }

                    console.log('[TalkGenAI-Chat-DEBUG] ✅ Calling modifyApp() with spec');
                    modifyApp(compactPrompt, validSpec);
                } else {
                    console.log('[TalkGenAI-Chat-DEBUG] ❌ Calling generateApp() - NO SPEC');
                    const appTitle = $('#app_title').val() || 'My App';
                    generateApp(compactPrompt, appTitle.trim());
                }
            };

            // Use delegated events for robustness
            $(document).off('click.tgai', '#tgai-chat-send').on('click.tgai', '#tgai-chat-send', function(e){ e.preventDefault(); e.stopPropagation(); setTimeout(sendChat, 0); });
            $(document).off('keydown.tgai', '#tgai-chat-input').on('keydown.tgai', '#tgai-chat-input', function(ev) {
                if (ev.key === 'Enter' && !ev.shiftKey) {
                    ev.preventDefault();
                    ev.stopPropagation();
                    sendChat();
                }
            });
        }

        $('#talkgenai-generate-form').on('submit', function(e) {
            e.preventDefault();
            
            if (isGenerating) {
                return false;
            }
            
            const description = $('#app_description').val().trim();
            const title = $('#app_title').length ? $('#app_title').val().trim() : '';
            
            if (!description) {
                showNotification(talkgenai_ajax.strings.error, 'error');
                return false;
            }
            
            // Determine if this is create or modify based on currentAppData
            if (currentAppData && currentAppData.json_spec) {
                console.log('TalkGenAI: Modify mode - sending current spec');
                modifyApp(description, currentAppData.json_spec);
            } else {
                console.log('TalkGenAI: Create mode - generating new app');
                generateApp(description, title);
            }
        });
        
        // Auto-generate title from description (only if title field exists)
        $('#app_description').on('input', function() {
            const description = $(this).val().trim();
            if ($('#app_title').length && description && !$('#app_title').val()) {
                const autoTitle = generateTitleFromDescription(description);
                $('#app_title').attr('placeholder', autoTitle);
            }
        });
    }
    
    
    /**
     * Bind edit app form
     */
    function bindEditForm() {
        $('#talkgenai-edit-form').on('submit', function(e) {
            e.preventDefault();
            
            const appId = $(this).data('app-id');
            const title = $('#edit_title').val().trim();
            const modification = $('#edit_modification').val().trim();
            
            if (!modification) {
                showNotification('Modification description is required', 'error');
                return false;
            }
            
            // Load current app data and modify
            loadAppData(appId, function(appData) {
                modifyExistingApp(appId, modification, appData.json_spec, title);
            });
        });
    }
    
    /**
     * Bind delete actions
     */
    function bindDeleteActions() {
        console.log('TalkGenAI: Delete button handler registered');
        $(document).on('click', '.delete-app', function(e) {
            e.preventDefault();
            console.log('TalkGenAI: Delete button clicked!');
            
            const appId = $(this).data('app-id');
            console.log('TalkGenAI: App ID:', appId);
            const confirmMessage = talkgenai_ajax.strings.confirm_delete;
            
            if (confirm(confirmMessage)) {
                const $row = $(this).closest('tr').length ? $(this).closest('tr') : $(this).closest('.talkgenai-app-card');
                console.log('TalkGenAI: Calling deleteApp for ID:', appId);
                deleteApp(appId, $row);
            }
        });
    }
    
    /**
     * Bind copy shortcode functionality
     */
    function bindCopyShortcode() {
        // Handle existing copy shortcode buttons
        $(document).on('click', '.copy-shortcode', function() {
            const shortcode = $(this).siblings('.talkgenai-shortcode').data('shortcode');
            
            if (navigator.clipboard) {
                navigator.clipboard.writeText(shortcode).then(function() {
                    showNotification('Shortcode copied to clipboard', 'success');
                });
            } else {
                // Fallback for older browsers
                const textArea = document.createElement('textarea');
                textArea.value = shortcode;
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand('copy');
                document.body.removeChild(textArea);
                showNotification('Shortcode copied to clipboard', 'success');
            }
        });

        // Handle new shortcode column copy buttons
        $(document).on('click', '.talkgenai-copy-shortcode-btn', function() {
            const $btn = $(this);
            const shortcode = $btn.data('shortcode');
            
            if (navigator.clipboard) {
                navigator.clipboard.writeText(shortcode).then(function() {
                    // Visual feedback
                    $btn.addClass('copied');
                    setTimeout(function() {
                        $btn.removeClass('copied');
                    }, 2000);
                    
                    showNotification('Shortcode copied to clipboard', 'success');
                });
            } else {
                // Fallback for older browsers
                const textArea = document.createElement('textarea');
                textArea.value = shortcode;
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand('copy');
                document.body.removeChild(textArea);
                
                // Visual feedback
                $btn.addClass('copied');
                setTimeout(function() {
                    $btn.removeClass('copied');
                }, 2000);
                
                showNotification('Shortcode copied to clipboard', 'success');
            }
        });
    }
    
    /**
     * Bind connection test
     */
    function bindConnectionTest() {
        $('#test-connection-btn').on('click', function() {
            const $btn = $(this);
            const $result = $('#connection-test-result');
            
            $btn.prop('disabled', true).text('Testing...');
            $result.hide();
            
            $.ajax({
                url: talkgenai_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'talkgenai_test_connection',
                    nonce: talkgenai_ajax.nonce
                },
                success: function(response) {
                    if (response.success) {
                        const result = response.data;
                        const className = result.success ? 'success' : 'error';
                        const message = result.success ? 
                            `Connection successful! Response time: ${result.response_time.toFixed(2)}s` :
                            `Connection failed: ${result.message}`;
                        
                        $result.removeClass('success error').addClass(className).text(message).show();
                        
                        // Update header status indicator immediately
                        updateHeaderStatus(result);
                    } else {
                        $result.removeClass('success error').addClass('error').text('Test failed').show();
                    }
                },
                error: function() {
                    $result.removeClass('success error').addClass('error').text('Test failed').show();
                },
                complete: function() {
                    $btn.prop('disabled', false).text('Test Connection');
                }
            });
        });
    }
    
    /**
     * Update header status indicator
     */
    function updateHeaderStatus(status) {
        const $statusContainer = $('.talkgenai-server-status-compact .talkgenai-status');
        if (!$statusContainer.length) return;
        
        if (!status || !status.success) {
            $statusContainer.attr('class', 'talkgenai-status talkgenai-status-error').text('Offline');
            return;
        }
        
        const responseTime = status.response_time || 0;
        let statusClass, statusText;
        
        if (responseTime < 1) {
            statusClass = 'talkgenai-status-excellent';
            statusText = 'Excellent';
        } else if (responseTime < 3) {
            statusClass = 'talkgenai-status-good';
            statusText = 'Good';
        } else if (responseTime < 5) {
            statusClass = 'talkgenai-status-fair';
            statusText = 'Fair';
        } else {
            statusClass = 'talkgenai-status-slow';
            statusText = 'Slow';
        }
        
        $statusContainer.attr('class', 'talkgenai-status ' + statusClass).text(statusText + ' (' + responseTime.toFixed(2) + 's)');
    }
    
    /**
     * Bind auto-detect functionality
     */
    function bindAutoDetect() {
        $('#auto-detect-btn').on('click', function() {
            const $btn = $(this);
            
            $btn.prop('disabled', true).text('Detecting...');
            
            // Try common local server URLs
            const urls = [
                'http://localhost:8000',
                'http://127.0.0.1:8000',
                'http://localhost:8080',
                'http://127.0.0.1:8080'
            ];
            
            testUrls(urls, 0, function(foundUrl) {
                if (foundUrl) {
                    $('#local_server_url').val(foundUrl);
                    showNotification(`Local server detected at: ${foundUrl}`, 'success');
                } else {
                    showNotification('No local server detected', 'warning');
                }
                $btn.prop('disabled', false).text('Auto-Detect Local Server');
            });
        });
    }
    
    /**
     * Bind bulk actions
     */
    function bindBulkActions() {
        $('#cb-select-all-1').on('change', function() {
            $('input[name="app_ids[]"]').prop('checked', this.checked);
        });
        
        $('input[name="app_ids[]"]').on('change', function() {
            const totalCheckboxes = $('input[name="app_ids[]"]').length;
            const checkedCheckboxes = $('input[name="app_ids[]"]:checked').length;
            
            $('#cb-select-all-1').prop('checked', totalCheckboxes === checkedCheckboxes);
        });
    }
    
    /**
     * Bind debug server test functionality
     */
    function bindDebugServerTest() {
        console.log('TalkGenAI: Binding debug server test...');
        
        $('#test-server-limits-btn').on('click', function(e) {
            e.preventDefault();
            console.log('TalkGenAI: Debug test button clicked');
            
            const $btn = $(this);
            
            // Check if AJAX variables are available
            if (typeof talkgenai_ajax === 'undefined') {
                alert('Error: AJAX configuration not found. Please refresh the page.');
                console.error('TalkGenAI: talkgenai_ajax object not found');
                return;
            }
            
            console.log('TalkGenAI: AJAX URL:', talkgenai_ajax.ajax_url);
            console.log('TalkGenAI: Nonce:', talkgenai_ajax.nonce);
            
            $btn.prop('disabled', true).text('Testing...');
            
            $.ajax({
                url: talkgenai_ajax.ajax_url,
                type: 'POST',
                data: {
                    action: 'talkgenai_debug_save_test',
                    nonce: talkgenai_ajax.nonce
                },
                success: function(response) {
                    console.log('TalkGenAI: Debug test response:', response);
                    
                    if (response.success) {
                        const data = response.data;
                        const message = `Server Configuration:
• post_max_size: ${data.post_max_size}
• max_input_vars: ${data.max_input_vars}
• Content Length: ${data.content_length}
• POST Keys Count: ${data.post_keys_count}
• Message: ${data.message}`;
                        
                        alert(message);
                        console.log('Server limits test result:', data);
                    } else {
                        alert('Server test failed: ' + (response.data ? response.data.message : 'Unknown error'));
                        console.error('TalkGenAI: Test failed:', response);
                    }
                },
                error: function(xhr, status, error) {
                    console.error('TalkGenAI: AJAX error:', xhr, status, error);
                    console.error('TalkGenAI: Response text:', xhr.responseText);
                    alert('Server test failed: ' + xhr.status + ' - ' + error + '\nCheck console for details.');
                },
                complete: function() {
                    $btn.prop('disabled', false).text('Test Server Limits');
                }
            });
        });
        
        // Check if button exists
        const $btn = $('#test-server-limits-btn');
        if ($btn.length) {
            console.log('TalkGenAI: Debug test button found and bound');
        } else {
            console.warn('TalkGenAI: Debug test button not found in DOM');
        }
    }
    
    /**
     * Generate new app
     */
    function generateApp(description, title) {
        // Prefer async job system if available to avoid WP timeouts
        if (window.TalkGenAI_JobManager && typeof window.TalkGenAI_JobManager.createJob === 'function') {
            setGeneratingState(true);
            try {
                window.TalkGenAI_JobManager.createJob('app', {
                    description: description
                }, {
                    onProgress: function(p) {
                        // optional: display progress
                    },
                    onSuccess: function(jobResult) {
                        try {
                            // Extract the actual result data (may be nested under 'result' key)
                            var result = jobResult.result || jobResult;
                            
                            var html = result && (result.html || result.article_html) ? (result.html || result.article_html) : '';
                            var js = result && result.js ? result.js : '';
                            var css = result && result.css ? result.css : '';
                            var spec = result && (result.json_spec || result.spec || result.app_spec) ? (result.json_spec || result.spec || result.app_spec) : null;
                            
                            // ✅ FIX Issue #7: Validate app data BEFORE showing success
                            if (!html || html.trim().length === 0) {
                                // ✅ PRIORITY: Check if backend sent ai_message (e.g., unsupported app type)
                                if (result && result.ai_message) {
                                    console.log('TalkGenAI: Backend sent ai_message (structured error/rejection)');
                                    const isHtml = result.is_html || (result.ai_message && result.ai_message.trim().startsWith('<'));
                                    
                                    // Show the backend's message in chat (e.g., "unsupported app type")
                                    try {
                                        if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') {
                                            window.tgaiChat.messages.push({role:'ai', content: result.ai_message});
                                            window.tgaiAppendMsg('ai', result.ai_message, isHtml);
                                        }
                                    } catch(e) {
                                        console.error('TalkGenAI: Error adding ai_message to chat:', e);
                                    }
                                    
                                    setGeneratingState(false);
                                    return; // Stop execution - message delivered
                                }
                                
                                // No ai_message - show generic error as fallback
                                console.error('TalkGenAI: No HTML content in response');
                                showNotification('❌ Generation failed: No app content received. Please try again.', 'error');
                                
                                // Add error to chat
                                try {
                                    if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') {
                                        window.tgaiChat.messages.push({role:'ai', content: '❌ Sorry, I encountered an issue generating your app. The response contained no content. Please try again with a different description or contact support if this persists.'});
                                        window.tgaiAppendMsg('ai', '❌ Sorry, I encountered an issue generating your app. The response contained no content. Please try again with a different description or contact support if this persists.', false);
                                    }
                                } catch(e) {
                                    console.error('TalkGenAI: Error adding validation error to chat:', e);
                                }
                                
                                setGeneratingState(false);
                                return; // Stop execution
                            }
                            
                            // Ensure spec is always a parsed object, never a string
                            if (spec && typeof spec === 'string') {
                                try { 
                                    spec = JSON.parse(spec); 
                                } catch(e) {
                                    console.error('TalkGenAI: Failed to parse json_spec string:', e);
                                    console.error('TalkGenAI: Problematic JSON preview:', spec.substring(0, 200));
                                    spec = null;
                                }
                            }
                            
                            // Validate spec has required fields
                            if (spec && (!spec.appClass || !spec.appType)) {
                                console.warn('TalkGenAI: json_spec missing required fields:', spec);
                            }
                            
                            // ✅ All validation passed - now update and show success
                            currentAppData = { html: html, js: js, css: css, json_spec: spec };
                            showPreview(html, js, css);
                            showNotification(talkgenai_ajax.strings.success, 'success');
                            
                            // Add AI response to chat
                            try { 
                                if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') { 
            // DEBUG: Log what we got from result
            console.log('[Chat-Response-DEBUG] spec.conversationalResponse:', spec?.conversationalResponse);
            console.log('[Chat-Response-DEBUG] result.ai_message:', result.ai_message);
            console.log('[Chat-Response-DEBUG] result keys:', Object.keys(result || {}));
            
            // PRIORITY FIX: Use ai_message FIRST (fresh from backend), then conversationalResponse, then fallback
            const aiResponse = result.ai_message || (spec?.conversationalResponse) || `Perfect! I've created your ${spec?.appClass || 'app'} and it's ready to use. You can see the preview on the right side. Feel free to save it or ask me to make any changes!`;
                                    
                                    // Auto-detect HTML: if ai_message starts with '<', treat as HTML
                                    const isHtml = result.is_html || (result.ai_message && result.ai_message.trim().startsWith('<'));
                                    
                                    console.log('TalkGenAI: Adding AI response to chat (HTML:', isHtml, '):', aiResponse.substring(0, 100));
                                    window.tgaiChat.messages.push({role:'ai', content: aiResponse}); 
                                    window.tgaiAppendMsg('ai', aiResponse, isHtml);
                                }
                            } catch(e) {
                                console.error('TalkGenAI: Error adding AI response to chat:', e);
                            }
                            
                            setSaveButtonAttention(true);
                            
                            // ✅ Auto-click "Generate New" button after first successful generation
                            const $genNewBtn = $('#generate-new-btn-header');
                            if ($genNewBtn.length && !$genNewBtn.is(':visible')) {
                                $genNewBtn.show();
                            }
                        } finally {
                            setGeneratingState(false);
                        }
                    },
                    onError: function(error, errorData) {
                        // ✅ FIX Issue #8: NEVER delete user messages - only add error messages
                        console.log('TalkGenAI: Generation error occurred:', error);
                        console.log('TalkGenAI: Error data:', errorData);
                        
                        // Check if this is a structured error with HTML message
                        if (errorData && errorData.ai_message) {
                            // Auto-detect HTML: if ai_message starts with '<', treat as HTML
                            const isHtml = errorData.is_html || (errorData.ai_message && errorData.ai_message.trim().startsWith('<'));
                            showNotification(error || talkgenai_ajax.strings.error, 'error');
                            
                            // Add HTML message to chat (ADDS message, doesn't delete)
                            try {
                                if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') {
                                    window.tgaiChat.messages.push({role:'ai', content: errorData.ai_message});
                                    window.tgaiAppendMsg('ai', errorData.ai_message, isHtml);
                                }
                            } catch(e) {
                                console.error('TalkGenAI: Error adding error message to chat:', e);
                            }
                        } else {
                            // Show generic error notification
                            showNotification(error || talkgenai_ajax.strings.error, 'error');
                            
                            // Add error message to chat (user message is preserved)
                            try {
                                if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') {
                                    const errorMsg = '❌ ' + (error || 'An error occurred. Please try again.');
                                    window.tgaiChat.messages.push({role:'ai', content: errorMsg});
                                    window.tgaiAppendMsg('ai', errorMsg, false);
                                }
                            } catch(e) {
                                console.error('TalkGenAI: Error adding error notification to chat:', e);
                            }
                        }
                        setGeneratingState(false);
                    }
                });
            } catch (e) {
                // Fallback to legacy path on any unexpected error
                setGeneratingState(false);
            }
            return;
        }

        // Legacy synchronous path (fallback)
        setGeneratingState(true);
        $.ajax({
            url: talkgenai_ajax.ajax_url,
            type: 'POST',
            timeout: 180000, // 3 minutes timeout (same as article generation)
            data: {
                action: 'talkgenai_generate_app',
                nonce: talkgenai_ajax.nonce,
                description: description,
                title: title
            },
            success: function(response) {
                if (response.success) {
                    console.log('TalkGenAI: AJAX Response received:', response.data);
                    console.log('TalkGenAI: HTML length:', response.data.html ? response.data.html.length : 'undefined');
                    console.log('TalkGenAI: JS length:', response.data.js ? response.data.js.length : 'undefined');
                    console.log('TalkGenAI: JS content preview:', response.data.js ? response.data.js.substring(0, 100) : 'no JS');
                    console.log('TalkGenAI: JSON spec type:', typeof response.data.json_spec);
                    console.log('TalkGenAI: JSON spec content:', response.data.json_spec);
                    
                    // Validate json_spec before storing
                    if (response.data.json_spec) {
                        try {
                            // If json_spec is a string, parse it to object
                            let parsedSpec = response.data.json_spec;
                            if (typeof parsedSpec === 'string') {
                                parsedSpec = JSON.parse(parsedSpec);
                                console.log('TalkGenAI: Parsed JSON spec from string');
                            }
                            
                            // Store the parsed object
                            currentAppData = response.data;
                            currentAppData.json_spec = parsedSpec;
                            console.log('TalkGenAI: Stored JSON spec as object:', parsedSpec);
                        } catch (e) {
                            console.error('TalkGenAI: Error parsing JSON spec:', e);
                            console.error('TalkGenAI: Problematic JSON spec:', response.data.json_spec);
                            currentAppData = response.data;
                        }
                    } else {
                        currentAppData = response.data;
                    }
                    
                    // Debug: Log what we received
                    console.log('TalkGenAI: Response data received:');
                    console.log('  - html length:', response.data.html ? response.data.html.length : 0);
                    console.log('  - css length:', response.data.css ? response.data.css.length : 0);
                    console.log('  - js length:', response.data.js ? response.data.js.length : 0);
                    console.log('  - html has <style>:', response.data.html ? response.data.html.includes('<style>') : false);
                    console.log('  - css has content:', response.data.css ? (response.data.css.length > 0) : false);
                    
                    showPreview(response.data.html, response.data.js, response.data.css || '');
                    showNotification(talkgenai_ajax.strings.success, 'success');
                    // Add AI response to chat
                    try { 
                        if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') { 
                            // PRIORITY FIX: Use ai_message FIRST (fresh from backend), then conversationalResponse, then fallback
                            const aiResponse = response.data.ai_message || response.data.json_spec?.conversationalResponse || `Perfect! I've created your ${response.data.json_spec?.appClass || 'app'} and it's ready to use. You can see the preview on the right side. Feel free to save it or ask me to make any changes!`;
                            const isHtml = response.data.is_html || false;
                            console.log('TalkGenAI: Adding AI response to chat (HTML:', isHtml, '):', aiResponse.substring(0, 100));
                            window.tgaiChat.messages.push({role:'ai', content: aiResponse}); 
                            window.tgaiAppendMsg('ai', aiResponse, isHtml);
                            
                            // Trigger save button attention
                            setSaveButtonAttention(true);
                        } 
                    } catch(e) {
                        console.error('TalkGenAI: Error adding AI response to chat:', e);
                    }
                } else {
                    showNotification(response.data.message || talkgenai_ajax.strings.error, 'error');
                }
            },
            error: function() {
                showNotification(talkgenai_ajax.strings.error, 'error');
            },
            complete: function() {
                setGeneratingState(false);
            }
        });
    }
    
    /**
     * Modify current app
     */
    function modifyApp(modification, currentSpec) {
        // CRITICAL FIX: If currentAppData is empty but window.currentAppData has data, sync them
        if (!currentAppData && window.currentAppData && typeof window.currentAppData === 'object') {
            console.log('[TalkGenAI-ModifyApp] ⚠️ Syncing currentAppData from window.currentAppData');
            currentAppData = window.currentAppData;
        }
        
        // Validate currentSpec before sending
        if (!currentSpec || typeof currentSpec !== 'object') {
            console.error('TalkGenAI: Invalid currentSpec - not an object:', currentSpec);
            showNotification('Invalid app specification. Please wait for app data to load completely.', 'error');
            return;
        }
        
        // Ensure required fields exist
        if (!currentSpec.appClass || !currentSpec.appType) {
            console.error('TalkGenAI: Missing required fields in currentSpec:', currentSpec);
            
            // Try to reload app data if we have an ID
            if (currentAppData && currentAppData.id) {
                showNotification('App specification incomplete. Reloading app data...', 'info');
                loadAppData(currentAppData.id, function(appData) {
                    if (appData) {
                        currentAppData = appData;
                        
                        // Parse json_spec if needed
                        if (typeof currentAppData.json_spec === 'string') {
                            try {
                                currentAppData.json_spec = JSON.parse(currentAppData.json_spec);
                            } catch(e) {
                                console.error('Error parsing reloaded json_spec:', e);
                                currentAppData.json_spec = null;
                            }
                        }
                        
                        // If json_spec is still empty/invalid, show error
                        if (!currentAppData.json_spec || 
                            Array.isArray(currentAppData.json_spec) || 
                            typeof currentAppData.json_spec !== 'object' ||
                            !currentAppData.json_spec.appClass) {
                            
                            console.error('TalkGenAI: App specification is corrupted in database');
                            showNotification('This app cannot be modified - specification is corrupted in database.', 'error');
                            return;
                        }
                        
                        // Retry the modification
                        modifyApp(modification, currentAppData.json_spec);
                    } else {
                        showNotification('Unable to load app specification. Please refresh the page.', 'error');
                    }
                });
                return;
            } else {
                showNotification('Invalid app specification. Missing required fields.', 'error');
                return;
            }
        }
        
        // DEBUG: Log what we're passing to JobManager
        console.log('[TalkGenAI-ModifyApp-DEBUG]', {
            modification: modification.substring(0, 50),
            hasCurrentSpec: !!currentSpec,
            currentSpecType: typeof currentSpec,
            appClass: currentSpec?.appClass,
            appType: currentSpec?.appType,
            currentSpecKeys: currentSpec ? Object.keys(currentSpec) : []
        });
        
        // Use job system if available (preferred for async processing)
        if (window.TalkGenAI_JobManager && typeof window.TalkGenAI_JobManager.createJob === 'function') {
            setGeneratingState(true);
            try {
                window.TalkGenAI_JobManager.createJob('app', {
                    description: modification,
                    current_spec: currentSpec
                }, {
                    onProgress: function(p) {
                        // Optional: display progress
                    },
                    onSuccess: function(jobResult) {
                        try {
                            var result = jobResult.result || jobResult;
                            
                            var html = result.html || result.article_html || '';
                            var css = result.css || '';
                            var js = result.js || '';
                            
                            // ✅ FIX Issue #7: Validate modification result
                            if (!html || html.trim().length === 0) {
                                console.error('TalkGenAI: No HTML content in modification response');
                                showNotification('❌ Modification failed: No content received. Please try again.', 'error');
                                
                                // Add error to chat
                                try {
                                    if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') {
                                        window.tgaiChat.messages.push({role:'ai', content: '❌ Sorry, the modification failed. No content was generated. Please try rephrasing your request.'});
                                        window.tgaiAppendMsg('ai', '❌ Sorry, the modification failed. No content was generated. Please try rephrasing your request.', false);
                                    }
                                } catch(e) {
                                    console.error('TalkGenAI: Error adding validation error to chat:', e);
                                }
                                
                                setGeneratingState(false);
                                return; // Stop execution
                            }
                            
                            currentAppData.html = html;
                            currentAppData.css = css;
                            currentAppData.js = js;
                            
                            var spec = result.json_spec || result.spec || result.app_spec;
                            if (spec && typeof spec === 'string') {
                                try { spec = JSON.parse(spec); } catch(e) {}
                            }
                            currentAppData.json_spec = spec;
                            
                            showPreview(currentAppData.html, currentAppData.js, currentAppData.css);
                            hideModifyForm();
                            showNotification('App modified successfully', 'success');
                            
                            // Add AI response to chat
                            try { 
                                if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') { 
            // DEBUG: Log what we got from result
            console.log('[Chat-Modify-Response-DEBUG] spec.conversationalResponse:', spec?.conversationalResponse);
            console.log('[Chat-Modify-Response-DEBUG] result.ai_message:', result.ai_message);
            console.log('[Chat-Modify-Response-DEBUG] result keys:', Object.keys(result || {}));
            console.log('[Chat-Modify-Response-DEBUG] spec keys:', Object.keys(spec || {}));
            
            // PRIORITY FIX: Use ai_message FIRST (fresh from backend), then conversationalResponse, then fallback
            const aiResponse = result.ai_message || (spec?.conversationalResponse) || `Perfect! I've updated your ${spec?.appClass || 'app'} as requested. The changes are now visible in the preview!`;
                                    
                                    // Auto-detect HTML: if ai_message starts with '<', treat as HTML
                                    const isHtml = result.is_html || (result.ai_message && result.ai_message.trim().startsWith('<'));
                                    
                                    window.tgaiChat.messages.push({role:'ai', content: aiResponse}); 
                                    window.tgaiAppendMsg('ai', aiResponse, isHtml);
                                    setSaveButtonAttention(true);
                                }
                            } catch(e) {
                                console.error('TalkGenAI: Error adding AI response to chat:', e);
                            }
                        } finally {
                            setGeneratingState(false);
                        }
                    },
                    onError: function(error, errorData) {
                        // Check if this is a structured error with HTML message
                        if (errorData && errorData.ai_message) {
                            const isHtml = errorData.is_html || false;
                            showNotification(error || 'Modification failed', 'error');
                            
                            // Add HTML message to chat
                            try {
                                if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') {
                                    window.tgaiChat.messages.push({role:'ai', content: errorData.ai_message});
                                    window.tgaiAppendMsg('ai', errorData.ai_message, isHtml);
                                }
                            } catch(e) {
                                console.error('TalkGenAI: Error adding error message to chat:', e);
                            }
                        } else {
                            showNotification(error || 'Modification failed', 'error');
                            
                            // Add error to chat
                            try { 
                                if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') { 
                                    const errorResponse = `Sorry, I couldn't apply that modification. ${error || 'Please try again or rephrase your request.'}`;
                                    window.tgaiChat.messages.push({role:'ai', content: errorResponse}); 
                                    window.tgaiAppendMsg('ai', errorResponse);
                                } 
                            } catch(_){}
                        }
                        
                        setGeneratingState(false);
                    }
                });
            } catch (e) {
                console.error('TalkGenAI: Error creating modification job:', e);
                showNotification('Failed to start modification', 'error');
                setGeneratingState(false);
            }
            return;
        }
        
        // Legacy synchronous fallback (may timeout for long operations)
        console.warn('TalkGenAI: Job system not available, using legacy synchronous modification (may timeout)');
        showNotification('Processing modification... (this may take a while)', 'info');
        
        let jsonString;
        try {
            jsonString = JSON.stringify(currentSpec);
        } catch (e) {
            console.error('TalkGenAI: Error stringifying currentSpec:', e);
            showNotification('Error processing app specification. Please try again.', 'error');
            return;
        }
        
        setGeneratingState(true);
        
        $.ajax({
            url: talkgenai_ajax.ajax_url,
            type: 'POST',
            dataType: 'text',
            timeout: 120000, // 2 minute timeout
            data: {
                action: 'talkgenai_modify_app',
                nonce: talkgenai_ajax.nonce,
                modification: modification,
                current_spec: jsonString
            },
            success: function(rawResponse) {
                window.__tgai_last_modify_raw = rawResponse;
                let response;
                try {
                    if (typeof rawResponse === 'object' && rawResponse !== null) {
                        response = rawResponse;
                    } else {
                        response = JSON.parse(rawResponse);
                    }
                } catch (e) {
                    console.error('TalkGenAI: Failed to parse modify response:', rawResponse);
                    showNotification('Invalid response format', 'error');
                    setGeneratingState(false);
                    return;
                }
                
                if (response && response.success) {
                    currentAppData.html = response.data.html;
                    currentAppData.css = response.data.css || '';
                    currentAppData.js = response.data.js;
                    currentAppData.json_spec = response.data.json_spec;
                    
                    showPreview(response.data.html, response.data.js, response.data.css || '');
                    hideModifyForm();
                    showNotification('App modified successfully', 'success');
                    
                    // Add AI response to chat
                    try { 
                        if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') { 
                            // PRIORITY FIX: Use ai_message FIRST (fresh from backend), then conversationalResponse, then fallback
                            const aiResponse = response.data.ai_message || response.data.json_spec?.conversationalResponse || `Perfect! I've updated your ${response.data.json_spec?.appClass || 'app'} as requested.`;
                            const isHtml = response.data.is_html || false;
                            window.tgaiChat.messages.push({role:'ai', content: aiResponse}); 
                            window.tgaiAppendMsg('ai', aiResponse, isHtml);
                            setSaveButtonAttention(true);
                        }
                    } catch(e) {
                        console.error('TalkGenAI: Error adding AI response to chat:', e);
                    }
                } else {
                    showNotification((response && response.data && response.data.message) || 'Modification failed', 'error');
                    
                    // Add error to chat
                    try { 
                        if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') { 
                            const errorResponse = `Sorry, I couldn't apply that modification. ${response?.data?.message || 'Please try again.'}`;
                            window.tgaiChat.messages.push({role:'ai', content: errorResponse}); 
                            window.tgaiAppendMsg('ai', errorResponse);
                        } 
                    } catch(_){}
                }
                setGeneratingState(false);
            },
            error: function(xhr, status, error) {
                console.error('TalkGenAI: Modification AJAX error:', status, error);
                if (status === 'timeout') {
                    showNotification('Modification is taking too long. The job system is recommended for complex modifications.', 'error');
                } else {
                    showNotification('Modification failed: ' + error, 'error');
                }
                setGeneratingState(false);
            }
        });
    }
    
    /**
     * Modify existing app (from edit page)
     */
    function modifyExistingApp(appId, modification, currentSpec, newTitle) {
        setGeneratingState(true);
        
        $.ajax({
            url: talkgenai_ajax.ajax_url,
            type: 'POST',
            dataType: 'text', // Get raw text to handle escaped JSON
            data: {
                action: 'talkgenai_modify_app',
                nonce: talkgenai_ajax.nonce,
                modification: modification,
                current_spec: JSON.stringify(currentSpec)
            },
            success: function(rawResponse) {
                // Prefer direct parse first to avoid corrupting valid JSON
                window.__tgai_last_modify_raw = rawResponse;
                let response;
                try {
                    if (typeof rawResponse === 'object' && rawResponse !== null) {
                        response = rawResponse;
                    } else {
                        response = JSON.parse(rawResponse);
                    }
                } catch (e0) {
                    // Fallback: parse potentially escaped JSON responses (handles \{ ... \}, BOM, quotes)
                    try {
                        let cleaned = String(rawResponse).replace(/^\uFEFF/, '').trim();
                        // If wrapped in quotes, unwrap once
                        if ((cleaned.startsWith('"') && cleaned.endsWith('"')) || (cleaned.startsWith("'") && cleaned.endsWith("'"))) {
                            cleaned = cleaned.slice(1, -1);
                        }
                        // Common unescapes
                        let unescaped = cleaned
                            .replace(/\\n/g, '\n')
                            .replace(/\\r/g, '\r')
                            .replace(/\\t/g, '\t')
                            .replace(/\\\//g, '/')
                            .replace(/\\\"/g, '"')
                            .replace(/\\\\/g, '\\');
                        // Remove stray backslashes before JSON punctuation/braces
                        unescaped = unescaped.replace(/\\([\{\}\[\]\:,])/g, '$1');
                        // Extract JSON substring if needed
                        const start = unescaped.indexOf('{');
                        const end = unescaped.lastIndexOf('}');
                        const jsonText = (start !== -1 && end > start) ? unescaped.slice(start, end + 1) : unescaped;
                        response = JSON.parse(jsonText);
                    } catch (e) {
                        console.error('TalkGenAI: Failed to parse modify response:', rawResponse);
                        showNotification('Invalid response format', 'error');
                        return;
                    }
                }
                
                if (response && response.success) {
                    // Update the app data and preview (with CSS/JS)
                    try {
                        currentAppData.html = response.data.html;
                        currentAppData.css = response.data.css || '';
                        currentAppData.js = response.data.js;
                        currentAppData.json_spec = response.data.json_spec;
                    } catch (_) {}
                    if (typeof showPreview === 'function') {
                        showPreview(response.data.html, response.data.js, response.data.css || '');
                    } else {
                        $('#talkgenai-preview-container').html(response.data.html);
                    }
                    
                    // Update title if present
                    if (response.data.json_spec && 
                        response.data.json_spec.uiText && 
                        response.data.json_spec.uiText.labels && 
                        response.data.json_spec.uiText.labels.calculatorTitle) {
                        const newTitleVal = response.data.json_spec.uiText.labels.calculatorTitle;
                        if (typeof window.currentAppData !== 'undefined') {
                            window.currentAppData.title = newTitleVal;
                        }
                        if ($('#save_app_title').length) {
                            $('#save_app_title').val(newTitleVal);
                        }
                    }
                    
                    showNotification('App modified successfully', 'success');
                } else {
                    showNotification((response && response.data && response.data.message) || 'Modification failed', 'error');
                }
            },
            error: function() {
                showNotification('Modification failed', 'error');
            },
            complete: function() {
                setGeneratingState(false);
            }
        });
    }
    
    /**
     * Delete app
     */
    function deleteApp(appId, $row) {
        $.ajax({
            url: talkgenai_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'talkgenai_delete_app',
                nonce: talkgenai_ajax.nonce,
                app_id: appId
            },
            success: function(response) {
                if (response.success) {
                    $row.fadeOut(function() {
                        $(this).remove();
                    });
                    // Check if there's a warning flag (files couldn't be deleted)
                    const notifType = response.data.warning ? 'warning' : 'success';
                    showNotification(response.data.message || 'App deleted successfully', notifType);
                } else {
                    showNotification(response.data.message || 'Delete failed', 'error');
                }
            },
            error: function(xhr, status, error) {
                console.error('Delete AJAX error:', status, error);
                showNotification('Delete failed: ' + (error || 'Unknown error'), 'error');
            }
        });
    }
    
    /**
     * Load app data
     */
    function loadAppData(appId, callback) {
        $.ajax({
            url: talkgenai_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'talkgenai_load_app',
                nonce: talkgenai_ajax.nonce,
                app_id: appId
            },
            success: function(response) {
                if (response.success && callback) {
                    callback(response.data);
                }
            },
            error: function() {
                showNotification('Failed to load app data', 'error');
            }
        });
    }
    
    /**
     * Save app changes
     */
    function saveAppChanges(appId, data) {
        // This would typically be handled by a separate AJAX endpoint
        // For now, we'll just show a success message
        console.log('Saving app changes:', appId, data);
    }
    
    /**
     * Basic HTML sanitizer for admin preview (removes script/style and on* attrs)
     */
    function sanitizeHtml(unsafeHtml) {
        try {
            const template = document.createElement('template');
            template.innerHTML = String(unsafeHtml || '');
            const disallowedTags = new Set(['SCRIPT', 'STYLE', 'IFRAME', 'OBJECT', 'EMBED', 'LINK']);
            const walker = document.createTreeWalker(template.content, NodeFilter.SHOW_ELEMENT, null);
            const toRemove = [];
            while (walker.nextNode()) {
                const el = walker.currentNode;
                if (disallowedTags.has(el.tagName)) {
                    toRemove.push(el);
                    continue;
                }
                // Remove event handlers and javascript: URLs
                [...el.attributes].forEach(attr => {
                    const name = attr.name.toLowerCase();
                    const value = (attr.value || '').trim();
                    if (name.startsWith('on')) {
                        el.removeAttribute(attr.name);
                    } else if ((name === 'href' || name === 'src') && /^javascript:/i.test(value)) {
                        el.removeAttribute(attr.name);
                    }
                });
            }
            toRemove.forEach(n => n.remove());
            return template.innerHTML;
        } catch (_) {
            return String(unsafeHtml || '');
        }
    }

    /**
     * Show app preview
     */
    function showPreview(html, js, css) {
        // Inject CSS first if provided
        if (css && css.trim()) {
            let styleId = 'talkgenai-preview-styles';
            let existingStyle = document.getElementById(styleId);
            
            if (existingStyle) {
                existingStyle.textContent = css;
            } else {
                let styleTag = document.createElement('style');
                styleTag.id = styleId;
                styleTag.textContent = css;
                document.head.appendChild(styleTag);
            }
            console.log('TalkGenAI: CSS injected for preview');
        }
        
        const safeHtml = sanitizeHtml(html);
        $('#talkgenai-preview-container').html(safeHtml);
        $('#talkgenai-preview-area').show();
        $('#talkgenai-preview-placeholder').hide();
        // Show the action buttons under the form
        $('.talkgenai-form-actions').show();
        // Show the Generate New button in header
        $('#generate-new-btn-header').show();
        // Highlight Save button to remind user to persist changes
        setSaveButtonAttention(true);
        
        // Execute JavaScript if provided
        if (js && js.trim()) {
            console.log('TalkGenAI: Executing JavaScript for app preview');
            console.log('JavaScript content:', js.substring(0, 200) + '...');
            
            try {
                // Wait a bit for HTML to be fully rendered
                setTimeout(() => {
                    try {
                        // Debug: Check what calculator widgets exist in the HTML
                        const calculatorWidgets = document.querySelectorAll('[id*="calculator-widget"]');
                        console.log('TalkGenAI: Found calculator widgets:', Array.from(calculatorWidgets).map(el => el.id));
                        
                        // Debug: Check what the JavaScript is looking for
                        const jsWidgetIdMatch = js.match(/calculator-widget-\d+/g);
                        console.log('TalkGenAI: JavaScript looking for widget IDs:', jsWidgetIdMatch);
                        
                        // Debug: Check what's inside the widget
                        if (calculatorWidgets.length > 0) {
                            const widget = calculatorWidgets[0];
                            console.log('TalkGenAI: Widget HTML preview:', widget.innerHTML.substring(0, 500));
                            
                            // Check for specific elements the JavaScript needs
                            const calculateBtn = widget.querySelector('#calculateBtn');
                            const form = widget.querySelector('#calculator-form');
                            console.log('TalkGenAI: Calculate button found:', !!calculateBtn);
                            console.log('TalkGenAI: Calculator form found:', !!form);
                            
                            if (calculateBtn) console.log('TalkGenAI: Calculate button ID:', calculateBtn.id);
                            if (form) console.log('TalkGenAI: Form ID:', form.id);
                            
                            // Check what forms actually exist
                            const allForms = widget.querySelectorAll('form');
                            console.log('TalkGenAI: All forms found:', Array.from(allForms).map(f => f.id || f.className || 'no-id-or-class'));
                            
                            // Check for any element that might be the form
                            const possibleForms = widget.querySelectorAll('[id*="form"], [class*="form"]');
                            console.log('TalkGenAI: Possible form elements:', Array.from(possibleForms).map(f => ({
                                tag: f.tagName,
                                id: f.id,
                                class: f.className
                            })));
                        }
                        
                        // Fix JavaScript to work with actual HTML structure
                        let fixedJs = js;
                        
                        // If no form element exists, modify the JavaScript to work without it
                        if (calculatorWidgets.length > 0 && !calculatorWidgets[0].querySelector('#calculator-form')) {
                            console.log('TalkGenAI: No form element found, adapting JavaScript...');
                            
                            // Replace form-dependent code with direct element access
                            fixedJs = fixedJs.replace(
                                /const form = widget\.querySelector\('#calculator-form'\);/g,
                                'const form = widget; // Use widget as form container'
                            );
                            
                            // Replace form checks
                            fixedJs = fixedJs.replace(
                                /if \(!calculateBtn \|\| !form\)/g,
                                'if (!calculateBtn)'
                            );
                            
                            // Replace form-based error messages
                            fixedJs = fixedJs.replace(
                                /console\.error\('Essential calculator elements missing from widget:', widgetId\);/g,
                                'console.log("TalkGenAI: Calculator initialized without form element, using direct element access");'
                            );
                            
                            console.log('TalkGenAI: JavaScript adapted for formless structure');
                        }
                        
                        // Validate JavaScript syntax before execution
                        try {
                            // Test if the JavaScript is valid by attempting to create a function
                            new Function(fixedJs);
                            
                            // If validation passes, execute the JavaScript
                            const script = document.createElement('script');
                            script.textContent = fixedJs;
                            document.head.appendChild(script);
                            
                            console.log('TalkGenAI: JavaScript executed successfully');
                            
                            // Remove the script element after execution
                            setTimeout(() => {
                                if (script.parentNode) {
                                    document.head.removeChild(script);
                                }
                            }, 1000);
                        } catch (syntaxError) {
                            console.error('TalkGenAI: JavaScript syntax error detected:', syntaxError.message);
                            console.error('TalkGenAI: Generated code has syntax errors - this should not happen');
                            
                            // Show error - this indicates a backend issue
                            if (typeof showNotification === 'function') {
                                showNotification('Generated code has syntax errors. The app was saved but may not function correctly. Please regenerate the app.', 'error');
                            }
                        }
                    } catch (execError) {
                        console.error('TalkGenAI: Error executing JavaScript:', execError);
                    }
                }, 200); // Wait 200ms for HTML to render
                
            } catch (error) {
                console.error('TalkGenAI: Error in JavaScript execution setup:', error);
            }
        }
        
        // Bind preview actions
        bindPreviewActions();
        
        // Update button text for modify mode
        updateButtonText();
        
        // Scroll to preview
        $('html, body').animate({
            scrollTop: $('#talkgenai-preview-area').offset().top - 50
        }, 500);
    }
    
    /**
     * Update app preview
     */
    function updatePreview(html, js) {
        $('#talkgenai-preview-container').html(html);
        
        // Execute JavaScript if provided
        if (js && js.trim()) {
            console.log('TalkGenAI: Updating preview with JavaScript');
            
            try {
                // Wait a bit for HTML to be fully rendered
                setTimeout(() => {
                    try {
                        // Debug: Check what calculator widgets exist in the HTML
                        const calculatorWidgets = document.querySelectorAll('[id*="calculator-widget"]');
                        console.log('TalkGenAI: Found calculator widgets (update):', Array.from(calculatorWidgets).map(el => el.id));
                        
                        // Fix JavaScript to work with actual HTML structure
                        let fixedJs = js;
                        
                        // If no form element exists, modify the JavaScript to work without it
                        if (calculatorWidgets.length > 0 && !calculatorWidgets[0].querySelector('#calculator-form')) {
                            console.log('TalkGenAI: No form element found in update, adapting JavaScript...');
                            
                            // Replace form-dependent code with direct element access
                            fixedJs = fixedJs.replace(
                                /const form = widget\.querySelector\('#calculator-form'\);/g,
                                'const form = widget; // Use widget as form container'
                            );
                            
                            // Replace form checks
                            fixedJs = fixedJs.replace(
                                /if \(!calculateBtn \|\| !form\)/g,
                                'if (!calculateBtn)'
                            );
                            
                            // Replace form-based error messages
                            fixedJs = fixedJs.replace(
                                /console\.error\('Essential calculator elements missing from widget:', widgetId\);/g,
                                'console.log("TalkGenAI: Calculator initialized without form element, using direct element access");'
                            );
                            
                            console.log('TalkGenAI: JavaScript adapted for formless structure (update)');
                        }
                        
                        // Validate JavaScript syntax before execution
                        try {
                            // Test if the JavaScript is valid by attempting to create a function
                            new Function(fixedJs);
                            
                            // If validation passes, execute the JavaScript
                            const script = document.createElement('script');
                            script.textContent = fixedJs;
                            document.head.appendChild(script);
                            
                            console.log('TalkGenAI: JavaScript executed successfully in update');
                            
                            // Remove the script element after execution
                            setTimeout(() => {
                                if (script.parentNode) {
                                    document.head.removeChild(script);
                                }
                            }, 1000);
                        } catch (syntaxError) {
                            console.error('TalkGenAI: JavaScript syntax error detected in update:', syntaxError.message);
                            console.error('TalkGenAI: Generated code has syntax errors - this should not happen');
                            
                            // Show error - this indicates a backend issue
                            if (typeof showNotification === 'function') {
                                showNotification('Generated code has syntax errors. The app was saved but may not function correctly. Please regenerate the app.', 'error');
                            }
                        }
                    } catch (execError) {
                        console.error('TalkGenAI: Error executing JavaScript in update:', execError);
                    }
                }, 200); // Wait 200ms for HTML to render
                
            } catch (error) {
                console.error('TalkGenAI: Error in JavaScript execution setup (update):', error);
            }
        }
    }
    
    /**
     * Bind preview actions
     */
    function bindPreviewActions() {
        $('#save-app-btn').off('click').on('click', function() {
            if (!currentAppData) {
                showNotification('No app data to save', 'error');
                return;
            }

            // Open save modal
            openSaveModal();
        });
        
        // Generate New button - reset and start fresh
        $('#generate-new-btn').off('click').on('click', function() {
            // Confirm if user wants to start over
            if (currentAppData && !currentAppData.id) {
                // App not saved yet - ask for confirmation
                if (!confirm('You have an unsaved app. Starting a new generation will discard it. Continue?')) {
                    return;
                }
            }
            
            // Reset to fresh state
            resetForm();
            showNotification('✨ Ready to generate a new app!', 'success');
        });
        
        
        $('#generate-new-btn-header').off('click').on('click', function() {
            resetForm();
            updateButtonText();
        });
    }
    
    /**
     * Bind additional action buttons (Get App Ideas only)
     */
    function bindAdditionalActions() {
        // Get App Ideas button
        $('#get-app-ideas-btn').off('click').on('click', function() {
            openAppIdeasModal();
        });
    }
    
    /**
     * Open modal to capture title and short description, then save
     */
    function openSaveModal() {
        // Remove existing modal if any
        $('#talkgenai-save-modal').remove();

        const isUpdateModal = !!(currentAppData && currentAppData.id);
        const defaultTitle = (isUpdateModal && (currentAppData.title || ''))
            ? currentAppData.title
            : (currentAppData.json_spec && currentAppData.json_spec.page && currentAppData.json_spec.page.title) ||
              generateTitleFromDescription($('#app_description').val().trim() || 'My App');
        const defaultDesc = (isUpdateModal && (currentAppData.description || ''))
            ? currentAppData.description
            : ($('#app_description').val().trim() || 
               (currentAppData.json_spec && currentAppData.json_spec.page && currentAppData.json_spec.page.description) ||
               'Generated app');
        const modal = $(
            '<div id="talkgenai-save-modal" class="talkgenai-generation-form" style="position: fixed; z-index: 100000; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 520px; max-width: 90%; box-shadow: 0 8px 24px rgba(0,0,0,0.2);">' +
                '<h3>Save App</h3>' +
                '<div class="form-field">' +
                    '<label for="save_app_title">App Name</label>' +
                    '<input type="text" id="save_app_title" class="regular-text" value="' + escapeHtml(defaultTitle) + '">' +
                '</div>' +
                '<div class="form-field" style="margin-top:10px;">' +
                    '<label for="save_app_desc">Short Description</label>' +
                    '<textarea id="save_app_desc" rows="3" class="large-text">' + escapeHtml(defaultDesc || '') + '</textarea>' +
                '</div>' +
                '<div style="text-align:right; margin-top:15px;">' +
                    '<button type="button" class="button" id="save_cancel_btn">Cancel</button> ' +
                    '<button type="button" class="button button-primary" id="save_confirm_btn">Save</button>' +
                '</div>' +
            '</div>'
        );

        $('body').append('<div id="talkgenai-save-backdrop" style="position:fixed; inset:0; background:rgba(0,0,0,0.4); z-index:99999;"></div>');
        $('body').append(modal);

        $('#save_cancel_btn').on('click', closeSaveModal);
        $('#talkgenai-save-backdrop').on('click', closeSaveModal);
        $('#save_confirm_btn').on('click', function() {
            const title = $('#save_app_title').val().trim() || defaultTitle;
            const shortDesc = $('#save_app_desc').val().trim();
            performSaveApp(title, shortDesc);
        });
    }

    function closeSaveModal() {
        $('#talkgenai-save-modal').remove();
        $('#talkgenai-save-backdrop').remove();
    }
    
    
    /**
     * Open app ideas modal
     */
    function openAppIdeasModal() {
        // Remove existing modal if any
        $('#talkgenai-ideas-modal').remove();
        
        const modal = $(
            '<div id="talkgenai-ideas-modal" class="talkgenai-generation-form" style="position: fixed; z-index: 100000; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 520px; max-width: 90%; box-shadow: 0 8px 24px rgba(0,0,0,0.2);">' +
                '<h3>💡 Get App Ideas</h3>' +
                '<p>Enter a website URL, or specific website page, describe what you want to achieve, or both. We\'ll analyze the information to suggest relevant app ideas.</p>' +
                '<div class="form-field">' +
                    '<label for="website_url">Website URL or Specific Page</label>' +
                    '<input type="url" id="website_url" class="regular-text" placeholder="https://example.com or https://example.com/specific-page">' +
                '</div>' +
                '<div class="form-field" style="margin-top:10px;">' +
                    '<label for="description">Description (Optional)</label>' +
                    '<textarea id="description" class="regular-text" rows="3" placeholder="Describe what you want to achieve with your app (optional)..."></textarea>' +
                '</div>' +
                '<div style="text-align:right; margin-top:15px;">' +
                    '<button type="button" class="button" id="ideas_cancel_btn">Cancel</button> ' +
                    '<button type="button" class="button button-primary" id="ideas_analyze_btn">Analyze Website</button>' +
                '</div>' +
            '</div>'
        );

        $('body').append('<div id="talkgenai-ideas-backdrop" style="position:fixed; inset:0; background:rgba(0,0,0,0.4); z-index:99999;"></div>');
        $('body').append(modal);

        $('#ideas_cancel_btn').on('click', closeAppIdeasModal);
        $('#talkgenai-ideas-backdrop').on('click', closeAppIdeasModal);
        $('#ideas_analyze_btn').on('click', function() {
            let url = $('#website_url').val().trim();
            const description = $('#description').val().trim();
            
            // Require either URL or description (or both)
            if (!url && !description) {
                showNotification('Please enter a website URL or description (or both)', 'error');
                return;
            }
            
            // If URL is provided, auto-add protocol and validate it
            if (url) {
                // Auto-add https:// if no protocol specified
                url = normalizeUrl(url);
                
                // Update the input field with the normalized URL
                $('#website_url').val(url);
                
                // Validate the normalized URL
                if (!isValidUrl(url)) {
                    showNotification('⚠️ Please enter a valid website URL (e.g., example.com or https://example.com)', 'error');
                    return;
                }
            }
            
            analyzeWebsite(url, description);
        });
    }
    
    function closeAppIdeasModal() {
        $('#talkgenai-ideas-modal').remove();
        $('#talkgenai-ideas-backdrop').remove();
    }

    function performSaveApp(title, shortDesc) {
        if (!currentAppData || !currentAppData.json_spec) {
            showNotification('Nothing to save', 'error');
            closeSaveModal();
            return;
        }

        // Determine if we're updating existing app or creating new one
        const isUpdate = currentAppData && currentAppData.id;
        const enteredTitle = title;
        const enteredDesc = shortDesc || $('#app_description').val().trim();
        
        // Debug: Log what we're about to save
        console.log('TalkGenAI: Preparing to save app data:');
        console.log('  - html length:', currentAppData.html ? currentAppData.html.length : 0);
        console.log('  - css length:', currentAppData.css ? currentAppData.css.length : 0);
        console.log('  - js length:', currentAppData.js ? currentAppData.js.length : 0);
        console.log('  - html has <style>:', currentAppData.html ? currentAppData.html.includes('<style>') : false);
        
        // Validate json_spec before saving
        if (!currentAppData.json_spec || typeof currentAppData.json_spec !== 'object') {
            console.error('TalkGenAI: Invalid json_spec - not an object:', currentAppData.json_spec);
            showNotification('Cannot save: Invalid app specification. Please regenerate the app.', 'error');
            closeSaveModal();
            return;
        }
        
        if (!currentAppData.json_spec.appClass || !currentAppData.json_spec.appType) {
            console.error('TalkGenAI: json_spec missing required fields:', currentAppData.json_spec);
            showNotification('Cannot save: App specification is incomplete. Please regenerate the app.', 'error');
            closeSaveModal();
            return;
        }
        
        // DEBUG: Log currentAppData before stringifying
        console.log('TalkGenAI SAVE DEBUG: currentAppData.json_spec type:', typeof currentAppData.json_spec);
        console.log('TalkGenAI SAVE DEBUG: currentAppData.json_spec preview:', currentAppData.json_spec);
        
        // Ensure json_spec is properly stringified
        let jsonSpecString;
        try {
            if (typeof currentAppData.json_spec === 'string') {
                // Already a string, verify it's valid JSON
                JSON.parse(currentAppData.json_spec);
                jsonSpecString = currentAppData.json_spec;
                console.log('TalkGenAI SAVE DEBUG: json_spec was already a string (valid JSON)');
            } else if (currentAppData.json_spec && typeof currentAppData.json_spec === 'object') {
                // Object, stringify it
                jsonSpecString = JSON.stringify(currentAppData.json_spec);
                console.log('TalkGenAI SAVE DEBUG: json_spec stringified from object, length:', jsonSpecString.length);
            } else {
                console.error('TalkGenAI SAVE DEBUG: json_spec is invalid type!', typeof currentAppData.json_spec);
                jsonSpecString = '{}';
            }
            
            // Verify the stringified version is valid JSON
            JSON.parse(jsonSpecString);
            console.log('TalkGenAI SAVE DEBUG: json_spec string is valid JSON ✓');
            console.log('TalkGenAI SAVE DEBUG: json_spec first 500 chars:', jsonSpecString.substring(0, 500));
            console.log('TalkGenAI SAVE DEBUG: json_spec last 500 chars:', jsonSpecString.substring(jsonSpecString.length - 500));
        } catch (e) {
            console.error('TalkGenAI SAVE DEBUG: Error with json_spec:', e);
            console.error('TalkGenAI SAVE DEBUG: Problematic json_spec value:', currentAppData.json_spec);
            jsonSpecString = '{}';
        }
        
        // Prepare the complete save data (full payload)
        const fullSaveData = {
            action: isUpdate ? 'talkgenai_update_app' : 'talkgenai_save_app',
            nonce: talkgenai_ajax.nonce,
            html: currentAppData.html,
            css: currentAppData.css || '',  // Include CSS content
            js: currentAppData.js || '',
            json_spec: jsonSpecString,
            app_class: currentAppData.json_spec.appClass || (typeof currentAppData.json_spec === 'object' ? currentAppData.json_spec.appClass : null),
            app_type: currentAppData.json_spec.appType || (typeof currentAppData.json_spec === 'object' ? currentAppData.json_spec.appType : null),
            generation_time: currentAppData.generation_time,
            server_response_time: currentAppData.server_response_time
        };
        
        console.log('TalkGenAI: Save payload prepared:');
        console.log('  - html length:', fullSaveData.html ? fullSaveData.html.length : 0);
        console.log('  - css length:', fullSaveData.css ? fullSaveData.css.length : 0);
        console.log('  - js length:', fullSaveData.js ? fullSaveData.js.length : 0);

        // For create: always send title/description. For update: only if user changed them
        if (!isUpdate) {
            fullSaveData.title = enteredTitle;
            fullSaveData.description = enteredDesc;
        } else {
            if (enteredTitle && enteredTitle !== (currentAppData.title || '')) {
                fullSaveData.title = enteredTitle;
            }
            if (enteredDesc && enteredDesc !== (currentAppData.description || '')) {
                fullSaveData.description = enteredDesc;
            }
        }
        
        // Add app_id for updates
        if (isUpdate) {
            fullSaveData.app_id = currentAppData.id;
        }

        // Lightweight content signatures to detect metadata-only changes
        const computeSignature = (str) => {
            const s = typeof str === 'string' ? str : (str || '');
            let hash = 5381;
            for (let i = 0; i < s.length; i++) hash = ((hash << 5) + hash) ^ s.charCodeAt(i);
            return `${s.length}:${(hash >>> 0).toString(16)}`;
        };
        const htmlSig = computeSignature(currentAppData.html || '');
        const jsSig = computeSignature(currentAppData.js || '');
        const jsonSig = computeSignature(typeof currentAppData.json_spec === 'string' ? currentAppData.json_spec : JSON.stringify(currentAppData.json_spec || {}));

        const appKey = currentAppData.id ? `tgai_sig_${currentAppData.id}` : null;
        let lastSaved = (currentAppData._lastSaved) ? currentAppData._lastSaved : null;
        if (!lastSaved && appKey && window.localStorage) {
            try { lastSaved = JSON.parse(localStorage.getItem(appKey) || 'null'); } catch(_) { /* ignore */ }
        }
        const metaOnly = !!(lastSaved && lastSaved.htmlSig === htmlSig && lastSaved.jsSig === jsSig && lastSaved.jsonSig === jsonSig);

        // Build minimal payload for metadata-only updates (omit large fields)
        const metaOnlyData = {
            action: 'talkgenai_update_app',
            nonce: talkgenai_ajax.nonce,
            app_id: currentAppData.id,
            title: (typeof fullSaveData.title !== 'undefined') ? fullSaveData.title : undefined,
            description: (typeof fullSaveData.description !== 'undefined') ? fullSaveData.description : undefined,
            app_class: currentAppData.json_spec.appClass,
            app_type: currentAppData.json_spec.appType
        };
        // Remove undefined keys for cleanliness
        Object.keys(metaOnlyData).forEach(k => (typeof metaOnlyData[k] === 'undefined') && delete metaOnlyData[k]);

        // Calculate total request size for full payload
        const serializedFull = new URLSearchParams(fullSaveData).toString();
        const requestSize = serializedFull.length;
        
        console.log('TalkGenAI: Save request size:', requestSize, 'bytes (adaptive hybrid save)');
        console.log('TalkGenAI: HTML size:', currentAppData.html?.length || 0, 'bytes');
        console.log('TalkGenAI: JS size:', (currentAppData.js || '').length, 'bytes');
        console.log('TalkGenAI: JSON spec size:', JSON.stringify(currentAppData.json_spec).length, 'bytes');
        console.log('TalkGenAI: Meta-only change detected:', metaOnly);

        const SMALL_THRESHOLD = 50000; // bytes (tunable per environment)

        // Strategy:
        // - If meta-only: always use traditional with minimal payload
        // - Else if under threshold: try traditional with full payload, fallback to chunked on failure
        // - Else: go directly to chunked for reliability
        if (metaOnly) {
            console.log('TalkGenAI: Using traditional save (metadata-only)');
            performTraditionalSave(metaOnlyData, /*fallbackToChunked*/ true, fullSaveData);
        } else if (requestSize < SMALL_THRESHOLD) {
            console.log('TalkGenAI: Using traditional save (small request)');
            performTraditionalSave(fullSaveData, /*fallbackToChunked*/ true, fullSaveData);
        } else {
            console.log('TalkGenAI: Using chunked save (large request)');
            performChunkedSave(fullSaveData);
        }
    }

    /**
     * Traditional save for small requests
     */
    function performTraditionalSave(saveData, fallbackToChunked, fallbackFullData) {
        setGeneratingState(true);
        
        $.ajax({
            url: talkgenai_ajax.ajax_url,
            type: 'POST',
            data: saveData,
            // Request raw text to robustly handle environments that escape JSON (e.g., leading backslashes)
            dataType: 'text',
            success: function(responseText) {
                try {
                    let text = (typeof responseText === 'string') ? responseText.trim() : '';
                    // Strip BOM and leading NUL if present
                    if (text.charCodeAt(0) === 0) { text = text.slice(1); }
                    text = text.replace(/^\uFEFF/, '');
                    // Strip common anti-JSON-hijacking prefixes and a single leading backslash if present
                    if (text.startsWith("\\{")) { text = text.slice(1); }
                    text = text.replace(/^\)\]\}',?\s*/,'');
                    const parsed = (typeof responseText === 'object' && responseText !== null) ? responseText : JSON.parse(text);
                    handleSaveSuccess(parsed);
                } catch (e) {
                    console.warn('TalkGenAI: Failed to parse traditional save response, will fallback if allowed.', e);
                    if (fallbackToChunked && fallbackFullData) {
                        console.warn('TalkGenAI: Falling back to chunked save after parse failure');
                        performChunkedSave(fallbackFullData);
                    } else {
                        showNotification('Save failed: invalid server response', 'error');
                    }
                }
            },
            error: function(xhr, status, error) {
                console.error('TalkGenAI: Traditional save failed:', xhr.status, error);
                const statusCode = Number(xhr.status || 0);
                const sizeRelated = (statusCode === 400 || statusCode === 413 || statusCode === 414 || statusCode === 500 || statusCode === 0);
                if (fallbackToChunked && sizeRelated && fallbackFullData) {
                    console.warn('TalkGenAI: Falling back to chunked save after traditional failure');
                    performChunkedSave(fallbackFullData);
                } else {
                    showNotification('Save failed: ' + xhr.status + ' - ' + error, 'error');
                }
            },
            complete: function() {
                setGeneratingState(false);
            }
        });
    }

    /**
     * Chunked save for large requests
     */
    function performChunkedSave(saveData) {
        console.log('TalkGenAI: Starting chunked save process');
        
        // Show chunked save progress
        showChunkedSaveProgress();
        
        // Split data into chunks
        const chunks = createSaveDataChunks(saveData);
        console.log('TalkGenAI: Created', chunks.length, 'chunks for save');
        
        // Generate unique session ID for this save
        const sessionId = 'save_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
        
        // Upload chunks sequentially
        console.log('TalkGenAI: Individual chunk sizes:', chunks.map((chunk, i) => `Chunk ${i}: ${JSON.stringify(chunk).length} bytes`));
        uploadChunksSequentially(chunks, sessionId, 0);
    }

    /**
     * Create chunks from save data
     */
    function createSaveDataChunks(saveData) {
        const chunks = [];
        const maxChunkSize = 3500; // Smaller chunks to avoid WAF/proxy thresholds
        
        // DEBUG: Check json_spec before chunking
        if (saveData.json_spec) {
            console.log('TalkGenAI CHUNK DEBUG: json_spec before chunking (length: ' + saveData.json_spec.length + ')');
            console.log('TalkGenAI CHUNK DEBUG: json_spec first 200 chars:', saveData.json_spec.substring(0, 200));
            console.log('TalkGenAI CHUNK DEBUG: json_spec last 200 chars:', saveData.json_spec.substring(saveData.json_spec.length - 200));
            
            // Check if it's valid JSON
            try {
                const parsed = JSON.parse(saveData.json_spec);
                console.log('TalkGenAI CHUNK DEBUG: json_spec is valid JSON ✓');
            } catch (e) {
                console.error('TalkGenAI CHUNK DEBUG: json_spec is NOT valid JSON!', e.message);
                console.log('TalkGenAI CHUNK DEBUG: json_spec preview (first 1000):', saveData.json_spec.substring(0, 1000));
            }
        }
        
        // Identify the largest fields that need chunking
        const chunkedFields = ['html', 'css', 'js', 'json_spec'];
        const baseData = {};
        const largeData = {};
        
        // Separate large and small data
        for (const [key, value] of Object.entries(saveData)) {
            // IMPORTANT:
            // - Always chunk 'js' even if small, otherwise it may never reach the server
            //   during chunked saves (and finalization will fail with "Missing required app content.")
            // - Other fields are chunked only when above threshold.
            if (chunkedFields.includes(key) && value && (key === 'js' || value.length > 1000)) {
                largeData[key] = value;
            } else {
                baseData[key] = value;
            }
        }
        
        // If we have large fields, chunk them
        if (Object.keys(largeData).length > 0) {
            for (const [fieldName, fieldValue] of Object.entries(largeData)) {
                const fieldChunks = chunkString(fieldValue, maxChunkSize);
                
                fieldChunks.forEach((chunk, index) => {
                    // CRITICAL FIX: Base64-encode chunk to prevent UTF-8 corruption during HTTP POST
                    const encodedChunk = base64EncodeUTF8(chunk);
                    
                    chunks.push({
                        field_name: fieldName,
                        chunk_index: index,
                        chunk_data: encodedChunk,  // Send base64-encoded
                        total_chunks: fieldChunks.length,
                        ...baseData // Include base data in each chunk
                    });
                });
            }
        } else {
            // No large fields, but still chunk to be safe
            chunks.push({
                chunk_index: 0,
                total_chunks: 1,
                ...saveData
            });
        }
        
        return chunks;
    }

    /**
     * Split string into chunks
     */
    function chunkString(str, chunkSize) {
        const chunks = [];
        for (let i = 0; i < str.length; i += chunkSize) {
            chunks.push(str.slice(i, i + chunkSize));
        }
        return chunks;
    }
    
    /**
     * UTF-8 safe base64 encoding
     * Prevents corruption of multi-byte characters (Hebrew, Arabic, emoji, etc.)
     */
    function base64EncodeUTF8(str) {
        try {
            // Convert UTF-8 string to base64
            // Use TextEncoder to properly handle multi-byte UTF-8 characters
            const encoder = new TextEncoder();
            const uint8Array = encoder.encode(str);
            
            // Convert Uint8Array to binary string
            let binaryString = '';
            for (let i = 0; i < uint8Array.length; i++) {
                binaryString += String.fromCharCode(uint8Array[i]);
            }
            
            // Encode to base64
            return btoa(binaryString);
        } catch (e) {
            console.error('TalkGenAI: Base64 encoding failed:', e);
            // Fallback: return original (will likely corrupt, but better than nothing)
            return str;
        }
    }

    /**
     * Upload chunks sequentially with progress
     */
    function uploadChunksSequentially(chunks, sessionId, currentIndex) {
        if (currentIndex >= chunks.length) {
            // All chunks uploaded, finalize the save
            finalizeChunkedSave(sessionId);
            return;
        }
        
        const chunk = chunks[currentIndex];
        const progress = Math.round((currentIndex / chunks.length) * 100);
        
        updateChunkedSaveProgress(progress, `Uploading chunk ${currentIndex + 1} of ${chunks.length}...`);
        
        // FINAL FIX: Send as JSON POST with explicit UTF-8 charset
        // This bypasses WordPress magic quotes AND declares UTF-8 explicitly
        const payload = {
            nonce: talkgenai_ajax.nonce,
            session_id: sessionId,
            chunk_index: currentIndex,
            total_chunks: chunks.length,
            field_name: chunk.field_name || '',
            chunk_data: chunk.chunk_data || '',
            // Include context from first chunk
            app_id: chunk.app_id,
            app_class: chunk.app_class,
            app_type: chunk.app_type,
            title: chunk.title,
            description: chunk.description
        };
        
        // Client-side diagnostics for json_spec
        try {
            if ((chunk.field_name || '') === 'json_spec') {
                const chunkData = chunk.chunk_data || '';
                const looksJSON = chunkData.trim().startsWith('{') || chunkData.trim().startsWith('[');
                console.log('TalkGenAI: json_spec CHUNK (JSON POST UTF-8) => index:', currentIndex, 'len:', chunkData.length, 'looksJSON:', looksJSON, 'preview:', chunkData.substring(0, 50));
            }
        } catch (e) { /* ignore diagnostics errors */ }

        console.log('TalkGenAI: About to send chunk', currentIndex + 1, '(JSON POST)');
        console.log('TalkGenAI: Target URL:', talkgenai_ajax.ajax_url);
        
        // CRITICAL: WordPress AJAX requires 'action' in URL, not JSON body
        $.ajax({
            url: talkgenai_ajax.ajax_url + '?action=talkgenai_save_chunk',
            type: 'POST',
            data: JSON.stringify(payload),
            contentType: 'application/json; charset=UTF-8',
            dataType: 'text', // parse manually to handle escaped JSON
            success: function(raw) {
                let response;
                try {
                    response = (typeof raw === 'object' && raw) ? raw : JSON.parse(raw);
                } catch(e0){
                    try{
                        let s = String(raw).replace(/^\uFEFF/,'').trim();
                        if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) s = s.slice(1,-1);
                        s = s.replace(/\\n/g,'\n').replace(/\\r/g,'\r').replace(/\\t/g,'\t').replace(/\\\//g,'/').replace(/\\\"/g,'"').replace(/\\\\/g,'\\');
                        s = s.replace(/\\([\{\}\[\]\:,])/g,'$1');
                        const a = s.indexOf('{'), b = s.lastIndexOf('}');
                        const jsonText = (a!==-1 && b>a) ? s.slice(a,b+1) : s;
                        response = JSON.parse(jsonText);
                    }catch(e){
                        console.error('TalkGenAI: Chunk upload parse error:', raw);
                        hideChunkedSaveProgress();
                        showNotification('Save failed: Chunk parse error', 'error');
                        return;
                    }
                }
                if (response && response.success) {
                    // Advance to next chunk until all uploaded; do NOT treat as final success
                    console.log('TalkGenAI: Chunk saved OK', currentIndex, response);
                    uploadChunksSequentially(chunks, sessionId, currentIndex + 1);
                } else {
                    console.error('TalkGenAI: Chunk upload failed:', response && response.data);
                    hideChunkedSaveProgress();
                    showNotification('Save failed: ' + (response && response.data && response.data.message || 'Chunk upload failed'), 'error');
                }
            },
            error: function(xhr, status, error) {
                hideChunkedSaveProgress();
                console.error('TalkGenAI: Chunk upload error:', xhr.status, error);
                showNotification('Save failed: Chunk upload error - ' + xhr.status, 'error');
            }
        });
    }

    /**
     * Finalize chunked save
     */
    function finalizeChunkedSave(sessionId) {
        updateChunkedSaveProgress(100, 'Finalizing save...');
        
        const payload = {
            nonce: talkgenai_ajax.nonce,
            session_id: sessionId
        };

        // CRITICAL: WordPress AJAX requires 'action' in URL, not JSON body
        $.ajax({
            url: talkgenai_ajax.ajax_url + '?action=talkgenai_finalize_chunk',
            type: 'POST',
            dataType: 'text', // Get raw text to handle escaped JSON
            data: JSON.stringify(payload),
            contentType: 'application/json; charset=UTF-8',
            success: function(rawResponse) {
                hideChunkedSaveProgress();
                
                // Record raw response for debugging
                window.__tgai_last_finalize_raw = rawResponse;
                console.log('🔍 Finalize raw response:', rawResponse.substring(0, 200));
                
                let response;
                try {
                    // Try direct parse first
                    response = JSON.parse(rawResponse);
                } catch (e1) {
                    try {
                        // Robust parse for potentially escaped JSON responses
                        let cleaned = String(rawResponse).trim();
                        // Strip BOM and leading NUL if present
                        if (cleaned.charCodeAt(0) === 0) { cleaned = cleaned.slice(1); }
                        cleaned = cleaned.replace(/^\uFEFF/, '');
                        // If wrapped in quotes, unwrap once
                        if ((cleaned.startsWith('"') && cleaned.endsWith('"')) || (cleaned.startsWith("'") && cleaned.endsWith("'"))) {
                            cleaned = cleaned.slice(1, -1);
                        }
                        // Common unescapes
                        let unescaped = cleaned
                            .replace(/\\n/g, '\n')
                            .replace(/\\r/g, '\r')
                            .replace(/\\t/g, '\t')
                            .replace(/\\\//g, '/')
                            .replace(/\\\"/g, '"')
                            .replace(/\\\\/g, '\\');
                        // Remove stray backslashes before JSON punctuation/braces
                        unescaped = unescaped.replace(/\\([{}\[\]:,])/g, '$1');
                        // Extract JSON substring if needed
                        const start = unescaped.indexOf('{');
                        const end = unescaped.lastIndexOf('}');
                        const jsonText = (start !== -1 && end > start) ? unescaped.slice(start, end + 1) : unescaped;
                        response = JSON.parse(jsonText);
                    } catch (e2) {
                        console.error('❌ TalkGenAI: Failed to parse finalize response:', rawResponse);
                        showNotification('Save failed: Invalid response format', 'error');
                        return;
                    }
                }
                
                if (response && response.success) {
                    console.log('✅ TalkGenAI: Chunked save completed successfully');
                    handleSaveSuccess(response);
                } else {
                    console.error('❌ TalkGenAI: Save finalization failed:', response ? response.data : 'No response');
                    showNotification('Save failed: ' + (response && response.data && response.data.message ? response.data.message : 'Finalization failed'), 'error');
                }
            },
            error: function(xhr, status, error) {
                hideChunkedSaveProgress();
                console.error('❌ TalkGenAI: Save finalization error:', xhr.status, error);
                showNotification('Save failed: Finalization error - ' + xhr.status, 'error');
            }
        });
    }

    /**
     * Handle successful save (both traditional and chunked)
     */
    function handleSaveSuccess(response) {
        // DEBUG: Log the complete response structure
        console.log('🔍 handleSaveSuccess called with response:', JSON.stringify(response, null, 2));
        
        if (response && response.success) {
            const data = response.data || {};
            console.log('🔍 response.data:', JSON.stringify(data, null, 2));
            console.log('🔍 data.app_id:', data.app_id);
            
            // Require app_id to confirm a real save; otherwise treat as failure
            if (!data.app_id) {
                console.error('❌ TalkGenAI: Save response missing app_id; treating as failure', data);
                showNotification('Save failed: server did not return app ID. Please try again.', 'error');
                return;
            }
            // Persist returned app_id so subsequent saves update the same app
            if (data.app_id) {
                currentAppData.id = data.app_id;
                currentMode = 'modify';
                updateButtonText();
            }
            // Persist latest title/description locally if provided
            if (typeof data.title !== 'undefined') {
                currentAppData.title = data.title;
            }
            if (typeof data.description !== 'undefined') {
                currentAppData.description = data.description;
            }
            // Update last-saved signatures (to enable metadata-only detection)
            try {
                const computeSignature = (str) => {
                    const s = typeof str === 'string' ? str : (str || '');
                    let hash = 5381;
                    for (let i = 0; i < s.length; i++) hash = ((hash << 5) + hash) ^ s.charCodeAt(i);
                    return `${s.length}:${(hash >>> 0).toString(16)}`;
                };
                const htmlSig = computeSignature(currentAppData.html || '');
                const jsSig = computeSignature(currentAppData.js || '');
                const jsonStr = (typeof currentAppData.json_spec === 'string') ? currentAppData.json_spec : JSON.stringify(currentAppData.json_spec || {});
                const jsonSig = computeSignature(jsonStr);
                currentAppData._lastSaved = { htmlSig, jsSig, jsonSig };
                if (currentAppData.id && window.localStorage) {
                    localStorage.setItem(`tgai_sig_${currentAppData.id}`, JSON.stringify(currentAppData._lastSaved));
                }
            } catch(_) { /* ignore */ }
            // Build shortcode fallback if backend did not return one
            const appId = data.app_id || (currentAppData && currentAppData.id);
            let shortcode = data.shortcode;
            if (!shortcode && appId) {
                shortcode = `[talkgenai_app id="${appId}"]`;
            }
            // Remove Save attention highlight after successful save
            setSaveButtonAttention(false);
            closeSaveModal();
            if (shortcode) {
                showShortcodeDialog(shortcode);
            } else {
                showNotification('App saved successfully' + (appId ? ` (ID: ${appId})` : ''), 'success');
            }
        } else if (response && response.data) {
            showNotification(response.data.message || 'Save failed', 'error');
        } else {
            showNotification('Save failed', 'error');
        }
    }

    /**
     * Show chunked save progress modal
     */
    function showChunkedSaveProgress() {
        // Remove existing progress if any
        $('#talkgenai-chunked-progress-modal').remove();
        $('#talkgenai-chunked-progress-backdrop').remove();
        
        const modal = $(
            '<div id="talkgenai-chunked-progress-modal" class="talkgenai-generation-form" style="position: fixed; z-index: 100000; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 420px; max-width: 90%; box-shadow: 0 8px 24px rgba(0,0,0,0.2);">' +
                '<h3>💾 Saving App...</h3>' +
                '<p id="chunked-progress-message">Preparing upload...</p>' +
                '<div style="background: #f1f1f1; border-radius: 4px; height: 20px; margin: 15px 0; overflow: hidden;">' +
                    '<div id="chunked-progress-bar" style="background: linear-gradient(90deg, #007cba, #0073aa); height: 100%; width: 0%; transition: width 0.3s ease; border-radius: 4px;"></div>' +
                '</div>' +
                '<div style="text-align: center; color: #666; font-size: 12px;">Please wait, do not close this window...</div>' +
            '</div>'
        );

        $('body').append('<div id="talkgenai-chunked-progress-backdrop" style="position:fixed; inset:0; background:rgba(0,0,0,0.4); z-index:99999;"></div>');
        $('body').append(modal);
    }

    /**
     * Update chunked save progress
     */
    function updateChunkedSaveProgress(percentage, message) {
        $('#chunked-progress-bar').css('width', percentage + '%');
        $('#chunked-progress-message').text(message);
    }

    /**
     * Hide chunked save progress modal
     */
    function hideChunkedSaveProgress() {
        $('#talkgenai-chunked-progress-modal').remove();
        $('#talkgenai-chunked-progress-backdrop').remove();
    }

    function showShortcodeDialog(shortcode) {
        // Remove if exists
        $('#talkgenai-shortcode-modal').remove();

        const articlesUrl = 'admin.php?page=talkgenai-articles';
        const modal = $(
            '<div id="talkgenai-shortcode-modal" class="talkgenai-generation-form" style="position: fixed; z-index: 100000; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 520px; max-width: 90%; box-shadow: 0 8px 24px rgba(0,0,0,0.2);">' +
                '<h3>App Saved</h3>' +
                '<p>Paste this shortcode in any page or post:</p>' +
                '<code id="tg-shortcode" style="display:block; padding:8px; background:#f1f1f1; border-radius:4px; margin-bottom:10px;">' + escapeHtml(shortcode) + '</code>' +
                '<p style="margin:10px 0 15px; font-size:13px;">Need a ready-made article for this app? <a href="' + articlesUrl + '" style="font-weight:500;">Generate Article &rarr;</a></p>' +
                '<div style="text-align:right;">' +
                    '<button type="button" class="button" id="shortcode_close_btn">Close</button> ' +
                    '<button type="button" class="button button-primary" id="shortcode_copy_btn">Copy</button>' +
                '</div>' +
            '</div>'
        );

        $('body').append('<div id="talkgenai-shortcode-backdrop" style="position:fixed; inset:0; background:rgba(0,0,0,0.4); z-index:99999;"></div>');
        $('body').append(modal);

        $('#shortcode_close_btn').on('click', function() {
            $('#talkgenai-shortcode-modal').remove();
            $('#talkgenai-shortcode-backdrop').remove();
        });

        $('#shortcode_copy_btn').on('click', function() {
            const text = $('#tg-shortcode').text();
            if (navigator.clipboard) {
                navigator.clipboard.writeText(text).then(function() {
                    showNotification('Shortcode copied to clipboard', 'success');
                });
            }
        });
    }
    /**
     * Save current app
     */
    function saveCurrentApp() {
        if (!currentAppData) {
            showNotification('No app data to save', 'error');
            return;
        }
        
        // In a real implementation, this would save to the database
        // For now, we'll simulate success
        showNotification('App saved successfully', 'success');
        
        // Redirect to apps page
        setTimeout(function() {
            window.location.href = 'admin.php?page=talkgenai-apps';
        }, 1000);
    }
    
    
    /**
     * Reset form
     */
    function resetForm() {
        // Clear URL parameters to prevent overwriting existing apps
        // Redirect to clean generate page URL to remove action=edit&app_id=XX parameters
        const currentUrl = new URL(window.location.href);
        const cleanUrl = currentUrl.pathname + '?page=talkgenai';
        
        // Check if we're currently in edit mode (has action=edit parameter)
        if (currentUrl.searchParams.has('action') && currentUrl.searchParams.get('action') === 'edit') {
            console.log('TalkGenAI: Clearing edit mode, redirecting to clean generate page');
            // Clear current app data to prevent any potential issues
            currentAppData = null;
            window.location.href = cleanUrl;
            return; // Exit early as page will reload
        }
        
        $('#talkgenai-generate-form')[0].reset();
        $('#talkgenai-preview-area').hide();
        $('#talkgenai-preview-placeholder').show();
        $('#talkgenai-modify-area').hide();
        // Hide action buttons
        $('.talkgenai-form-actions').hide();
        $('#generate-new-btn-header').hide();
        // Clear chat conversation
        $('#tgai-chat-transcript').empty();
        $('#tgai-chat-input').val('');
        
        // CRITICAL FIX: Clear conversation history array
        if (window.tgaiChat && window.tgaiChat.messages) {
            window.tgaiChat.messages = [];
        }
        
        // Show a fresh start message in chat
        if ($('#tgai-chat-transcript').length) {
            $('#tgai-chat-transcript').html('<div class="tgai-chat-message assistant"><div class="message-content" style="text-align:center;font-style:italic;border:none;background:transparent;color:#9ca3bf;">New conversation started</div></div>');
        }
        
        // Trigger rainbow border animation on reset
        setTimeout(function() {
            const $chatInput = $('#tgai-chat-input');
            $chatInput.addClass('tgai-initial-animation');
            setTimeout(function() {
                $chatInput.removeClass('tgai-initial-animation');
            }, 2000);
        }, 100);
        
        currentAppData = null;
        
        // Scroll to top
        $('html, body').animate({
            scrollTop: $('.talkgenai-generation-form').offset().top - 50
        }, 500);
    }
    
    /**
     * Start progress messages animation
     */
    function startProgressMessages() {
        const $progressDiv = $('#tgai-chat-progress');
        const $progressMessage = $('#tgai-progress-message');
        const $progressBar = $('#tgai-progress-bar');
        
        if ($progressDiv.length) {
            $progressDiv.fadeIn(300);
            let messageIndex = 0;
            
            // Set initial message and progress
            $progressMessage.text(progressMessages[messageIndex].text);
            $progressBar.css('width', progressMessages[messageIndex].progress + '%');
            
            progressInterval = setInterval(() => {
                // Don't go past the last message (Finalizing...)
                if (messageIndex < progressMessages.length - 1) {
                    messageIndex++;
                    
                    // Fade out message, change it, then fade in
                    $progressMessage.fadeOut(200, function() {
                        $(this).text(progressMessages[messageIndex].text).fadeIn(200);
                    });
                    
                    // Animate progress bar
                    $progressBar.css('width', progressMessages[messageIndex].progress + '%');
                } 
                // If we're at the last message, just stay there until completion
                
            }, 2500); // Change message every 2.5 seconds
        }
    }
    
    /**
     * Start progress messages animation for app ideas analysis
     */
    function startIdeasProgressMessages() {
        const $progressDiv = $('#tgai-chat-progress');
        const $progressMessage = $('#tgai-progress-message');
        const $progressBar = $('#tgai-progress-bar');
        
        if ($progressDiv.length) {
            $progressDiv.fadeIn(300);
            let messageIndex = 0;
            
            // Set initial message and progress
            $progressMessage.text(ideasProgressMessages[messageIndex].text);
            $progressBar.css('width', ideasProgressMessages[messageIndex].progress + '%');
            
            progressInterval = setInterval(() => {
                // Don't go past the last message (Finalizing...)
                if (messageIndex < ideasProgressMessages.length - 1) {
                    messageIndex++;
                    
                    // Fade out message, change it, then fade in
                    $progressMessage.fadeOut(200, function() {
                        $(this).text(ideasProgressMessages[messageIndex].text).fadeIn(200);
                    });
                    
                    // Animate progress bar
                    $progressBar.css('width', ideasProgressMessages[messageIndex].progress + '%');
                } 
                // If we're at the last message, just stay there until completion
                
            }, 2500); // Change message every 2.5 seconds
        }
    }
    
    /**
     * Stop progress messages animation
     */
    function stopProgressMessages() {
        const $progressDiv = $('#tgai-chat-progress');
        const $progressMessage = $('#tgai-progress-message');
        const $progressBar = $('#tgai-progress-bar');

        if (progressInterval) {
            clearInterval(progressInterval);
            progressInterval = null;
        }

        // Remove typing indicator
        $('#tgai-chat-transcript .tgai-typing-indicator').remove();

        if ($progressDiv.length) {
            // Show completion message briefly
            $progressMessage.text('Complete! Your app is ready!');
            $progressBar.css('width', '100%');

            setTimeout(() => {
                $progressDiv.fadeOut(400);
            }, 1000);
        }
    }
    
    /**
     * Start progress messages animation for article generation
     */
    function startArticleProgressMessages() {
        const $progressDiv = $('#tgai-article-progress');
        const $progressMessage = $('#tgai-article-progress-message');
        const $progressBar = $('#tgai-article-progress-bar');
        
        if ($progressDiv.length) {
            $progressDiv.fadeIn(300);
            let messageIndex = 0;
            // Dynamically distribute all messages across ~160 seconds
            const totalMs = 160000; // 160s timeline
            const steps = Math.max(1, articleProgressMessages.length - 1);
            const stepMs = Math.floor(totalMs / steps);

            // Set initial message and progress
            $progressMessage.text(articleProgressMessages[messageIndex].text);
            $progressBar.css('width', articleProgressMessages[messageIndex].progress + '%');
            
            progressInterval = setInterval(() => {
                if (messageIndex < articleProgressMessages.length - 1) {
                    messageIndex++;
                    $progressMessage.fadeOut(200, function() {
                        $(this).text(articleProgressMessages[messageIndex].text).fadeIn(200);
                    });
                    $progressBar.css('width', articleProgressMessages[messageIndex].progress + '%');
                } 
            }, stepMs);
        }
    }
    
    /**
     * Stop progress messages animation for article generation
     */
    function stopArticleProgressMessages() {
        const $progressDiv = $('#tgai-article-progress');
        const $progressMessage = $('#tgai-article-progress-message');
        const $progressBar = $('#tgai-article-progress-bar');
        
        if (progressInterval) {
            clearInterval(progressInterval);
            progressInterval = null;
        }
        
        if ($progressDiv.length) {
            // Show completion message briefly
            $progressMessage.text('🎉 Complete! Your article is ready!');
            $progressBar.css('width', '100%');
            
            setTimeout(() => {
                $progressDiv.fadeOut(400);
            }, 1000);
        }
    }
    
    /**
     * Set generating state
     */
    function setGeneratingState(generating) {
        isGenerating = generating;

        const $generateBtn = $('#generate-app-btn');
        const $applyBtn = $('#apply-changes-btn');
        const $chatSendBtn = $('#tgai-chat-send');
        const $spinner = $('.dashicons-update');
        const $generateIcon = $('#generate-icon');

        if (generating) {
            $generateBtn.prop('disabled', true).addClass('loading');
            $applyBtn.prop('disabled', true).addClass('loading');
            $chatSendBtn.prop('disabled', true).addClass('processing');
            $spinner.show();
            $generateIcon.hide();
            startProgressMessages();
        } else {
            $generateBtn.prop('disabled', false).removeClass('loading');
            $applyBtn.prop('disabled', false).removeClass('loading');
            $chatSendBtn.prop('disabled', false).removeClass('processing');
            $spinner.hide();
            $generateIcon.show();
            stopProgressMessages();
        }
    }
    
    /**
     * Generate title from description
     */
    function generateTitleFromDescription(description) {
        // Simple title generation - take first few words
        const words = description.split(' ').slice(0, 6);
        let title = words.join(' ');
        
        if (title.length > 50) {
            title = title.substring(0, 47) + '...';
        }
        
        // Capitalize first letter
        return title.charAt(0).toUpperCase() + title.slice(1);
    }
    
    /**
     * Test URLs for auto-detection
     */
    function testUrls(urls, index, callback) {
        if (index >= urls.length) {
            callback(null);
            return;
        }
        
        const url = urls[index];
        
        // Create a test request
        $.ajax({
            url: url + '/health',
            type: 'GET',
            timeout: 3000,
            success: function() {
                callback(url);
            },
            error: function() {
                testUrls(urls, index + 1, callback);
            }
        });
    }
    
    /**
     * Hide modify form after successful modification
     */
    function hideModifyForm() {
        // Hide modify form and show success state
        const modifyForm = $('#talkgenai-edit-form');
        if (modifyForm.length) {
            // Reset the modification textarea
            $('#edit_modification').val('');
            // Could add visual feedback here, like hiding the form temporarily
            // or showing a success message
        }
        
        // Update button state back to modify mode
        updateButtonText();
    }
    
    /**
     * Show notification
     */
    function showNotification(message, type) {
        type = type || 'info';
        
        // Remove existing notifications
        $('.talkgenai-notice').remove();
        
        // Create new notification
        const $notice = $('<div class="talkgenai-notice ' + type + '">' + message + '</div>');
        
        // Insert after the first h1
        $('h1').first().after($notice);
        
        // Auto-hide after 5 seconds for success messages
        if (type === 'success') {
            setTimeout(function() {
                $notice.fadeOut();
            }, 5000);
        }
        
        // Scroll to notification
        $('html, body').animate({
            scrollTop: $notice.offset().top - 50
        }, 300);
    }
    
    /**
     * Initialize tooltips
     */
    function initializeTooltips() {
        // Add tooltips to elements with title attributes
        $('[title]').each(function() {
            const $this = $(this);
            const title = $this.attr('title');
            
            if (title) {
                $this.removeAttr('title').addClass('talkgenai-tooltip');
                $this.append('<span class="tooltiptext">' + title + '</span>');
            }
        });
    }
    
    /**
     * Initialize notifications
     */
    function initializeNotifications() {
        // Make existing WordPress notices dismissible
        $(document).on('click', '.notice-dismiss', function() {
            $(this).closest('.notice').fadeOut();
        });
    }
    
    /**
     * Utility: Debounce function
     */
    function debounce(func, wait, immediate) {
        let timeout;
        return function() {
            const context = this;
            const args = arguments;
            const later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            const callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    }
    
    /**
     * Utility: Escape HTML
     */
    function escapeHtml(text) {
        if (text === undefined || text === null) {
            return '';
        }
        const map = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#039;'
        };
        const str = String(text);
        return str.replace(/[&<>"']/g, function(m) {
            return map[m];
        });
    }
    
    /**
     * Create and bind simple test button
     */
    function createSimpleTestButton() {
        console.log('TalkGenAI: createSimpleTestButton() called');
        
        // Remove existing button if it exists
        $('#simple-test-btn').remove();
        
        // Add simple test button to admin interface
        if ($('#test-server-limits-btn').length) {
            console.log('TalkGenAI: Creating simple test button after debug button');
            const $button = $('<button type="button" id="simple-test-btn" style="margin: 10px; padding: 5px 10px; background: #28a745; color: white; border: none; border-radius: 3px; cursor: pointer;">Simple Test</button>');
            $button.insertAfter('#test-server-limits-btn');
            console.log('TalkGenAI: Simple test button inserted, checking if it exists:', $('#simple-test-btn').length);
        } else if ($('.wrap h1').length) {
            console.log('TalkGenAI: Creating simple test button after h1');
            const $button = $('<button type="button" id="simple-test-btn" style="margin: 10px; padding: 5px 10px; background: #28a745; color: white; border: none; border-radius: 3px; cursor: pointer;">Simple Test</button>');
            $button.insertAfter('.wrap h1');
            console.log('TalkGenAI: Simple test button inserted, checking if it exists:', $('#simple-test-btn').length);
        }
        
        // Bind click handler
        $(document).off('click', '#simple-test-btn').on('click', '#simple-test-btn', function() {
            console.log('TalkGenAI: Simple test button clicked!');
            
            const testData = {
                action: 'talkgenai_simple_test',
                nonce: talkgenai_ajax.nonce,
                test_data: 'Simple test payload',
                timestamp: Date.now()
            };
            
            console.log('TalkGenAI: Sending simple test request:', testData);
            
            $.ajax({
                url: talkgenai_ajax.ajax_url,
                type: 'POST',
                data: testData,
                success: function(response) {
                    console.log('TalkGenAI: Simple test SUCCESS:', response);
                    alert('Simple test successful! Check console and logs for details.');
                },
                error: function(xhr, status, error) {
                    console.error('TalkGenAI: Simple test ERROR:', {
                        status: xhr.status,
                        statusText: xhr.statusText,
                        responseText: xhr.responseText,
                        error: error
                    });
                    alert('Simple test failed: ' + xhr.status + ' ' + error);
                }
            });
        });
    }
    
    
    /**
     * ✅ FIX Issue #5: Show premium feature upgrade prompt
     */
    function showPremiumFeatureMessage(errorData) {
        // Hide generating state
        setGeneratingState(false);
        
        // Create upgrade prompt HTML
        const upgradeHtml = `
            <div class="talkgenai-premium-prompt">
                <div class="premium-icon">🚀</div>
                <h3>Unlock AI-Powered App Ideas</h3>
                <p>${errorData.message || 'This is a premium feature available on the Starter plan.'}</p>
                
                <div class="premium-features">
                    <div class="feature-item">✅ Analyze any website</div>
                    <div class="feature-item">✅ Get 5 custom app ideas</div>
                    <div class="feature-item">✅ Tailored to your business</div>
                    <div class="feature-item">✅ Save hours of brainstorming</div>
                </div>
                
                <div class="premium-pricing">
                    <strong>Starter Plan: $9.99/month</strong><br>
                    <small>50 credits + unlimited website analysis</small>
                </div>
                
                <div class="premium-actions">
                    <a href="https://app.talkgen.ai/pricing?source=wordpress" target="_blank" class="button button-primary">
                        Upgrade Now
                    </a>
                    <button type="button" class="button button-secondary" onclick="jQuery('.talkgenai-premium-prompt').fadeOut();">
                        Maybe Later
                    </button>
                </div>
            </div>
        `;
        
        // Show in chat or notification area
        if (window.tgaiChat && typeof window.tgaiAppendMsg === 'function') {
            window.tgaiChat.messages.push({role:'ai', content: upgradeHtml});
            window.tgaiAppendMsg('ai', upgradeHtml, true); // true = HTML content
        } else {
            // Fallback to notification
            showNotification('This feature requires the Starter plan. Upgrade to unlock AI-powered app ideas!', 'info');
        }
    }
    
    /**
     * ✅ FIX Issue #4: Normalize URL (auto-add http://)
     */
    function normalizeUrl(url) {
        // Trim whitespace
        url = (url || '').trim();
        
        // Return empty if nothing entered
        if (!url) return url;
        
        // Already has protocol - return as-is
        if (/^https?:\/\//i.test(url)) {
            return url;
        }
        
        // Remove common mistakes like 'www.' prefix
        url = url.replace(/^(www\.)?/, '');
        
        // Add https://
        return 'https://' + url;
    }
    
    /**
     * ✅ FIX Issue #4: Validate URL format
     */
    function isValidUrl(url) {
        try {
            const urlObj = new URL(url);
            // Must be http or https
            return ['http:', 'https:'].includes(urlObj.protocol);
        } catch (e) {
            return false;
        }
    }
    
    /**
     * Analyze website and get app ideas
     */
    function analyzeWebsite(url, description) {
        // ✅ FIX Issue #3 & #4: Validate and normalize URL
        url = (url || '').trim();
        
        if (!url) {
            showNotification('⚠️ Please enter a website URL', 'error');
            return;
        }
        
        // Normalize URL (auto-add https://)
        url = normalizeUrl(url);
        
        // Validate URL
        if (!isValidUrl(url)) {
            showNotification('⚠️ Please enter a valid website URL (e.g., example.com)', 'error');
            return;
        }
        
        setGeneratingState(true);
        closeAppIdeasModal();
        
        // Start ideas-specific progress messages
        startIdeasProgressMessages();
        
        $.ajax({
            url: talkgenai_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'talkgenai_analyze_website',
                nonce: talkgenai_ajax.nonce,
                website_url: url, // Now normalized and validated
                description: description || ''
            },
            success: function(response) {
                if (response.success) {
                    // Check if user just depleted their bonus credits
                    if (response.data && response.data.warning === 'bonus_depleted') {
                        showNotification('🎁 Bonus credits ended! You\'re now using standard features. Upgrade to Starter plan for Premium AI models.', 'warning');
                    }
                    showAppIdeasResult(response.data);
                } else {
                    // Check if API key is missing or unauthorized
                    if (response.data && (response.data.code === 'no_api_key' || response.data.code === 'unauthorized')) {
                        showApiErrorModal(response.data.message || 'API key is not configured', true);
                        return;
                    }
                    
                    // Check if this is a premium feature error (free user trying to use App Ideas)
                    if (response.data && (response.data.code === 'premium_feature' || response.data.error_code === 'premium_feature')) {
                        showPremiumFeatureUpgradeModal(response.data);
                        return;
                    }
                    
                    // Generic error - translate technical messages
                    const errorMsg = response.data && response.data.message ? response.data.message : 'Website analysis failed';
                    const friendlyMsg = translateApiError(errorMsg);
                    
                    // If it's an API key error (by message content), show the modal
                    if (friendlyMsg.includes('API Key') || friendlyMsg.includes('Authorization')) {
                        showApiErrorModal(errorMsg, true);
                    } else {
                        showNotification(friendlyMsg, 'error');
                    }
                }
            },
            error: function(xhr, status, error) {
                // Check if this is a 403 premium feature error FIRST (most common for free users)
                if (xhr.status === 403) {
                    let errorData = null;
                    
                    // Try multiple paths to find the error data
                    if (xhr.responseJSON) {
                        if (xhr.responseJSON.detail && typeof xhr.responseJSON.detail === 'object') {
                            errorData = xhr.responseJSON.detail;
                        } else if (xhr.responseJSON.data) {
                            errorData = xhr.responseJSON.data;
                        } else {
                            errorData = xhr.responseJSON;
                        }
                    }
                    
                    // Check if this is a premium feature error
                    if (errorData && (errorData.code === 'premium_feature' || errorData.error_code === 'premium_feature')) {
                        showPremiumFeatureUpgradeModal(errorData);
                        return;
                    }
                    
                    // If no specific code, but it's a 403, assume it's a premium feature
                    showPremiumFeatureUpgradeModal(errorData || {});
                    return;
                }
                
                // Check if this is a 402 payment required error
                if (xhr.status === 402) {
                    let errorData = xhr.responseJSON && xhr.responseJSON.detail ? xhr.responseJSON.detail : xhr.responseJSON;
                    showPremiumFeatureUpgradeModal(errorData || {});
                    return;
                }
                
                // Check if this is a 401 unauthorized error (missing/invalid API key)
                if (xhr.status === 401) {
                    let errorMsg = 'API key is missing or invalid';
                    if (xhr.responseJSON && xhr.responseJSON.detail) {
                        if (typeof xhr.responseJSON.detail === 'string') {
                            errorMsg = xhr.responseJSON.detail;
                        } else if (xhr.responseJSON.detail.message) {
                            errorMsg = xhr.responseJSON.detail.message;
                        }
                    }
                    
                    showApiErrorModal(errorMsg, true);
                    return;
                }
                
                // Generic error message - also translate if it contains technical terms
                let genericError = '⚠️ Website analysis failed. Please try again.';
                if (xhr.responseJSON && xhr.responseJSON.detail) {
                    const detailMsg = typeof xhr.responseJSON.detail === 'string' 
                        ? xhr.responseJSON.detail 
                        : xhr.responseJSON.detail.message || '';
                    if (detailMsg) {
                        genericError = translateApiError(detailMsg);
                    }
                }
                
                showNotification(genericError, 'error');
            },
            complete: function() {
                setGeneratingState(false);
            }
        });
    }
    
    /**
     * Custom modal (no external dependencies for security)
     */
    function showCustomModal(options) {
        // Remove any existing modal
        $('#talkgenai-custom-modal, #talkgenai-custom-backdrop').remove();
        
        const title = options.title || 'Notice';
        const html = options.html || options.text || '';
        const confirmText = options.confirmButtonText || 'OK';
        const cancelText = options.cancelButtonText || 'Cancel';
        const showCancel = options.showCancelButton || false;
        const onConfirm = options.onConfirm || function() {};
        const onCancel = options.onCancel || function() {};
        
        const modalHtml = `
            <div id="talkgenai-custom-backdrop" style="position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 999999;"></div>
            <div id="talkgenai-custom-modal" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 12px; padding: 30px; max-width: 500px; width: 90%; box-shadow: 0 10px 40px rgba(0,0,0,0.3); z-index: 1000000;">
                <h2 style="margin: 0 0 20px 0; font-size: 22px; color: #333;">${title}</h2>
                <div style="margin: 0 0 25px 0; color: #555; line-height: 1.6;">${html}</div>
                <div style="text-align: right; display: flex; gap: 10px; justify-content: flex-end;">
                    ${showCancel ? `<button id="talkgenai-modal-cancel" class="button" style="padding: 8px 20px;">${cancelText}</button>` : ''}
                    <button id="talkgenai-modal-confirm" class="button button-primary" style="padding: 8px 20px; background: #667eea; border-color: #667eea;">${confirmText}</button>
                </div>
            </div>
        `;
        
        $('body').append(modalHtml);
        
        // Bind events
        $('#talkgenai-modal-confirm').on('click', function() {
            $('#talkgenai-custom-modal, #talkgenai-custom-backdrop').remove();
            onConfirm();
        });
        
        if (showCancel) {
            $('#talkgenai-modal-cancel').on('click', function() {
                $('#talkgenai-custom-modal, #talkgenai-custom-backdrop').remove();
                onCancel();
            });
        }
        
        $('#talkgenai-custom-backdrop').on('click', function() {
            $('#talkgenai-custom-modal, #talkgenai-custom-backdrop').remove();
        });
    }
    
    /**
     * Translate technical API errors into user-friendly messages
     */
    function translateApiError(errorMessage) {
        // Check for specific technical error patterns
        if (!errorMessage || typeof errorMessage !== 'string') {
            return errorMessage;
        }
        
        // Authorization header errors
        if (errorMessage.includes('Authorization header') || 
            errorMessage.includes('Bearer tgai_') ||
            errorMessage.includes('Missing or invalid Authorization')) {
            return '🔑 API Key Required - Please configure your API key in Settings.';
        }
        
        // Invalid API key format
        if (errorMessage.includes('Invalid API key format')) {
            return '🔑 Invalid API Key - Please check your API key in Settings.';
        }
        
        // Insufficient credits
        if (errorMessage.includes('insufficient credits') || errorMessage.includes('not enough credits')) {
            return '💳 You don\'t have enough credits. Please upgrade your plan or purchase more credits.';
        }
        
        // Return original message if no translation found
        return errorMessage;
    }
    
    /**
     * Show user-friendly API error modal
     */
    function showApiErrorModal(errorMessage, showSettingsButton = true) {
        const friendlyMessage = translateApiError(errorMessage);
        
        // Check if this is an API key related error
        const isApiKeyError = friendlyMessage.includes('API Key') || friendlyMessage.includes('Authorization');
        
        if (isApiKeyError) {
            showCustomModal({
                title: '🔑 API Key Required',
                html: `
                    <div style="text-align: left;">
                        <p style="font-size: 16px; margin-bottom: 16px;">
                            Your API key is not configured or is invalid.
                        </p>
                        <div style="background: #f8f9fa; padding: 16px; border-radius: 8px; margin-bottom: 16px;">
                            <p style="margin: 0 0 12px 0; font-weight: 600;">📋 To get your API key:</p>
                            <ol style="margin: 0; padding-left: 20px; color: #555;">
                                <li>Visit <a href="https://app.talkgen.ai/" target="_blank" style="color: #667eea; text-decoration: none;"><strong>app.talkgen.ai</strong></a></li>
                                <li>Sign up with Google (free & instant)</li>
                                <li>Create and copy your API key</li>
                                <li>Return here and paste it in Settings</li>
                            </ol>
                        </div>
                    </div>
                `,
                confirmButtonText: showSettingsButton ? 'Go to Settings' : 'OK',
                showCancelButton: showSettingsButton,
                cancelButtonText: 'Get API Key',
                onConfirm: function() {
                    if (showSettingsButton) {
                        window.location.href = 'admin.php?page=talkgenai-settings';
                    }
                },
                onCancel: function() {
                    window.open('https://app.talkgen.ai/', '_blank', 'noopener,noreferrer');
                }
            });
        } else {
            // Generic error with friendly message
            showCustomModal({
                title: '⚠️ Error',
                html: `<p>${escapeHtml(friendlyMessage)}</p>`,
                confirmButtonText: 'OK'
            });
        }
    }
    
    /**
     * Show premium feature upgrade modal (user-friendly popup)
     */
    function showPremiumFeatureUpgradeModal(data) {
        showCustomModal({
            title: '✨ Premium Feature',
            html: `
                <div style="text-align: left;">
                    <p style="font-size: 16px; margin-bottom: 20px;">
                        <strong>AI-Powered App Ideas</strong> is available for Starter plan users and above.
                    </p>
                    
                    <div style="background: #f8f9fa; padding: 16px; border-radius: 8px; margin-bottom: 20px;">
                        <p style="margin: 0 0 12px 0; font-weight: 600; color: #333;">✅ Starter Plan Includes:</p>
                        <ul style="margin: 0; padding-left: 20px; color: #555;">
                            <li>AI-Powered App Ideas Generator</li>
                            <li>50 generation credits/month</li>
                            <li>20 active apps</li>
                            <li>Premium AI models (GPT-4 & Claude Sonnet 4.5)</li>
                            <li>No rate limits</li>
                        </ul>
                    </div>
                    
                    <p style="font-size: 14px; color: #666; margin: 0;">
                        💡 Your free plan includes 10 credits for creating simple apps!
                    </p>
                </div>
            `,
            showCancelButton: true,
            confirmButtonText: '🚀 Upgrade Now',
            cancelButtonText: 'Maybe Later',
            onConfirm: function() {
                // Open upgrade page in new tab
                const upgradeUrl = (data && data.upgrade_url) || 'https://app.talkgen.ai/dashboard';
                window.open(upgradeUrl, '_blank', 'noopener,noreferrer');
            }
        });
    }
    
    /**
     * Show premium feature upgrade message in chat
     */
    function showPremiumFeatureMessage(data) {
        const $t = $('#tgai-chat-transcript');
        const timestamp = new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
        
        const messageContent = 
            '<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 16px; border-radius: 8px; color: white;">' +
                '<h4 style="margin: 0 0 12px 0; color: white; font-size: 16px;">✨ Premium Feature: AI-Powered App Ideas</h4>' +
                '<p style="margin: 0 0 12px 0; opacity: 0.95;">' + escapeHtml(data.message || data.detail?.message || 'This feature is available for Starter plan users.') + '</p>' +
                '<div style="background: rgba(255,255,255,0.15); padding: 12px; border-radius: 6px; margin-bottom: 12px;">' +
                    '<strong style="display: block; margin-bottom: 4px;">✅ Starter Plan Benefits:</strong>' +
                    '<ul style="margin: 8px 0 0 20px; padding: 0;">' +
                        '<li>AI-Powered App Ideas Generator</li>' +
                        '<li>50 generation credits/month</li>' +
                        '<li>20 active apps</li>' +
                        '<li>Premium AI models (GPT-4 & Claude Sonnet 4.5)</li>' +
                        '<li>No rate limits</li>' +
                    '</ul>' +
                '</div>' +
                '<div style="text-align: center;">' +
                    '<a href="' + escapeHtml(data.upgrade_url || 'https://app.talkgen.ai/dashboard') + '" target="_blank" rel="noopener" style="display: inline-block; background: white; color: #667eea; padding: 10px 24px; border-radius: 6px; text-decoration: none; font-weight: 600; transition: transform 0.2s; box-shadow: 0 4px 12px rgba(0,0,0,0.15);">🚀 Upgrade to Starter Plan</a>' +
                '</div>' +
            '</div>';
        
        const messageHtml = [
            '<div class="tgai-chat-message assistant">',
            '  <div class="message-content">' + messageContent + '</div>',
            '  <div class="message-meta">' + timestamp + '</div>',
            '</div>'
        ].join('');
        
        $t.append(messageHtml);
        
        // Scroll to bottom of chat
        $t.scrollTop($t[0].scrollHeight);
    }
    
    
    /**
     * Show app ideas result
     */
    function showAppIdeasResult(data) {
        // Build context information for display
        let contextInfo = '';
        if (data.website_url && data.description) {
            contextInfo = '<strong>Website:</strong> ' + escapeHtml(data.website_url) + '<br><strong>Description:</strong> ' + escapeHtml(data.description);
        } else if (data.website_url) {
            contextInfo = '<strong>Website:</strong> ' + escapeHtml(data.website_url);
        } else if (data.description) {
            contextInfo = '<strong>Description:</strong> ' + escapeHtml(data.description);
        }
        
        const ideasHtml = data.ideas.map((idea, index) => 
            '<div class="idea-item" style="background: #f8f9fa; padding: 12px; border-radius: 4px; margin-bottom: 8px; border-left: 4px solid #007cba; cursor: pointer; transition: background-color 0.2s;" data-idea-title="' + escapeHtml(idea.title) + '" data-idea-description="' + escapeHtml(idea.description) + '">' +
                '<h4 style="margin: 0 0 8px 0; color: #007cba; font-size: 14px;">' + (index + 1) + '. ' + escapeHtml(idea.title) + '</h4>' +
                '<p style="margin: 0 0 8px 0; color: #666; font-size: 13px;">' + escapeHtml(idea.description) + '</p>' +
                '<div style="font-size: 11px; color: #999;">' +
                    '<strong>Type:</strong> ' + escapeHtml(idea.category) + ' | ' +
                    '<strong>Complexity:</strong> ' + escapeHtml(idea.complexity) +
                    (idea.seo_analysis ? ' | <strong>SEO:</strong> ' + escapeHtml(idea.seo_analysis) : '') +
                '</div>' +
            '</div>'
        ).join('');
        
        // Create the message content for chat transcript
        const messageContent = 
            '<div style="background: #e8f4fd; padding: 12px; border-radius: 4px; margin-bottom: 12px;">' +
                '<strong>💡 App Ideas Analysis</strong><br>' +
                contextInfo + '<br>' +
                '<em>Found ' + data.ideas.length + ' relevant app ideas. Click on any title to load it and create the app.</em>' +
            '</div>' +
            '<div class="app-ideas-container">' +
                ideasHtml +
            '</div>';
        
        // Add the message to chat transcript
        const $t = $('#tgai-chat-transcript');
        const timestamp = new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
        const messageHtml = [
            '<div class="tgai-chat-message assistant">',
            '  <div class="message-content">' + messageContent + '</div>',
            '  <div class="message-meta">' + timestamp + '</div>',
            '</div>'
        ].join('');
        
        $t.append(messageHtml);
        
        // Scroll to bottom of chat
        $t.scrollTop($t[0].scrollHeight);
        
        // Add hover effects to idea items
        $('.idea-item').hover(
            function() {
                $(this).css('background-color', '#e3f2fd');
            },
            function() {
                $(this).css('background-color', '#f8f9fa');
            }
        );
        
        // Handle idea item clicks
        $('.idea-item').on('click', function() {
            const title = $(this).data('idea-title');
            const description = $(this).data('idea-description');
            
            // Populate the chat input with both title and description
            const fullDescription = title + ': ' + description;
            $('#tgai-chat-input').val(fullDescription);
            showNotification('App idea loaded! You can now click "Create App" to generate this app.', 'success');
        });
    }
    
    /**
     * Format Select2 app option for display in dropdown
     */
    function formatAppOption(option) {
        if (!option.id) {
            return option.text;
        }
        
        // Create a styled option with app icon
        var $option = $(
            '<div class="tgai-select2-option">' +
                '<span class="tgai-app-icon">📱</span>' +
                '<span class="tgai-app-title">' + option.text + '</span>' +
            '</div>'
        );
        
        return $option;
    }
    
    /**
     * Format Select2 app selection (what shows when selected)
     */
    function formatAppSelection(option) {
        return option.text || 'Search for an app...';
    }
    
    /**
     * Initialize articles page functionality
     */
    function initializeArticlesPage() {
        // Only run on articles page
        if (window.location.href.indexOf('talkgenai-articles') === -1) {
            return;
        }
        
        console.log('TalkGenAI: Initializing articles page');
        
        // Initialize Select2 for app selector with search
        // Wait a bit for Select2 to load from CDN
        setTimeout(function() {
            if (typeof jQuery.fn.select2 !== 'undefined') {
                try {
                    $('#target_app.tgai-select2-app-selector').select2({
                        placeholder: 'Search for an app...',
                        allowClear: true,
                        width: '100%',
                        minimumResultsForSearch: 5, // Show search box if more than 5 items
                        templateResult: formatAppOption,
                        templateSelection: formatAppSelection
                    });
                    
                    console.log('TalkGenAI: Select2 initialized successfully for app selector');
                } catch(e) {
                    console.error('TalkGenAI: Error initializing Select2:', e);
                }
            } else {
                console.warn('TalkGenAI: Select2 library not loaded, using standard dropdown');
            }
        }, 500);
        
        // Bind articles form
        $('#talkgenai-articles-form').on('submit', function(e) {
            e.preventDefault();
            
            const appId = $('#target_app').val();
            const length = $('#article_length').val();
            const instructions = $('#article_instructions').val();
            
            if (!appId) {
                showNotification('Please select an app to generate an article for', 'error');
                return;
            }
            
            generateArticle(appId, length, instructions);
        });
        
        // Bind tab functionality
        $(document).on('click', '.talkgenai-tab-button', function() {
            const tabName = $(this).data('tab');
            
            // Remove active class from all tabs and panels
            $('.talkgenai-tab-button').removeClass('active');
            $('.talkgenai-tab-panel').removeClass('active');
            
            // Add active class to clicked tab and corresponding panel
            $(this).addClass('active');
            $(`#${tabName}-tab`).addClass('active');
        });
        
        // Bind copy visual button
        $(document).on('click', '#copy-visual-btn', function() {
            const $content = $('#article-content');
            const htmlContent = $content.html();
            const textContent = $content.text();
            
            if (navigator.clipboard) {
                // Try to copy HTML first, fallback to text
                if (navigator.clipboard.write) {
                    const blob = new Blob([htmlContent], { type: 'text/html' });
                    navigator.clipboard.write([new ClipboardItem({ 'text/html': blob })])
                        .then(function() {
                            showNotification('Visual content copied to clipboard (HTML format)', 'success');
                        })
                        .catch(function() {
                            // Fallback to text
                            navigator.clipboard.writeText(textContent).then(function() {
                                showNotification('Visual content copied to clipboard (text format)', 'success');
                            });
                        });
                } else {
                    // Fallback to text
                    navigator.clipboard.writeText(textContent).then(function() {
                        showNotification('Visual content copied to clipboard', 'success');
                    });
                }
            } else {
                showNotification('Copy not supported in this browser', 'error');
            }
        });
        
        // Bind copy code button
        $(document).on('click', '#copy-code-btn', function() {
            const codeContent = $('#article-code').text();
            
            if (navigator.clipboard) {
                navigator.clipboard.writeText(codeContent).then(function() {
                    showNotification('HTML code copied to clipboard', 'success');
                });
            } else {
                showNotification('Copy not supported in this browser', 'error');
            }
        });
        
        // Bind download button (bottom + top)
        $(document).on('click', '#download-article-btn, #download-article-btn-top', function() {
            const htmlContent = $('#article-code').text();
            const appTitle = $('#target_app option:selected').text() || 'article';
            const filename = appTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase() + '.html';
            
            // Download as HTML file
            const blob = new Blob([htmlContent], { type: 'text/html' });
            const url = window.URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
            
            showNotification('Article downloaded as HTML file', 'success');
        });
        
        // Bind copy meta description button
        $(document).on('click', '#copy-meta-description-btn', function() {
            const metaDescription = $('#meta-description-text').val();
            if (metaDescription) {
                if (navigator.clipboard) {
                    navigator.clipboard.writeText(metaDescription).then(function() {
                        showNotification('Meta description copied to clipboard', 'success');
                    }).catch(function() {
                        showNotification('Failed to copy meta description', 'error');
                    });
                } else {
                    showNotification('Copy not supported in this browser', 'error');
                }
            } else {
                showNotification('No meta description to copy', 'error');
            }
        });
    }
    
    /**
     * Generate article for articles page
     */
    function generateArticle(appId, length, instructions = '') {
        const $btn = $('#generate-article-btn');
        const $resultArea = $('#article-result-area');
        const $content = $('#article-content');
        const appUrl = ($('#app_url').length ? ($('#app_url').val() || '') : '');
        
        // Show loading state with progress indicator
        $btn.prop('disabled', true).html('<span class="dashicons dashicons-update spin"></span> Generating...');
        
        // Hide result area during generation
        $resultArea.hide();
        
        // Create progress UI if it doesn't exist
        let $progressDiv = $('#tgai-article-progress');
        if ($progressDiv.length === 0) {
            $progressDiv = $('<div id="tgai-article-progress" style="display: none; padding: 15px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; border-radius: 12px; margin: 15px 0; color: white; font-size: 14px; text-align: center; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); position: relative; overflow: hidden;">' +
                '<div id="tgai-article-progress-spinner" style="display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,0.3); border-radius: 50%; border-top-color: white; animation: tgai-spin 1s ease-in-out infinite; margin-right: 10px; vertical-align: middle;"></div>' +
                '<span id="tgai-article-progress-message" style="vertical-align: middle; font-weight: 500;">🤖 Connecting to AI server...</span>' +
                '<div id="tgai-article-progress-bar" style="position: absolute; bottom: 0; left: 0; height: 3px; background: rgba(255,255,255,0.8); width: 10%; transition: width 0.3s ease;"></div>' +
            '</div>');
            // Insert after the form's submit button container
            $btn.closest('.submit').after($progressDiv);
        }
        
        // Start progress messages animation
        startArticleProgressMessages();
        
        $.ajax({
            url: talkgenai_ajax.ajax_url,
            type: 'POST',
            timeout: 180000, // 3 minutes timeout for article generation
            data: {
                action: 'talkgenai_generate_article',
                nonce: talkgenai_ajax.nonce,
                app_id: appId,
                length: length,
                instructions: instructions,
                app_url: appUrl
            },
            success: function(response) {
                // Stop progress animation
                stopArticleProgressMessages();
                
                console.log('Full response:', response);
                if (response.success) {
                    // Check if response.data.article exists (new structure) or response.data.content (old structure)
                    const articleContent = response.data.article ? response.data.article.content : response.data.content;
                    const metaDescription = response.data.article ? response.data.article.meta_description : response.data.meta_description;
                    
                    // Debug: Check if we have the right structure
                    console.log('Response data:', response.data);
                    console.log('Article object:', response.data.article);
                    console.log('Article content:', articleContent);
                    console.log('Meta description:', metaDescription);
                    
                    console.log('Response structure:', response.data);
                    console.log('Article object:', response.data.article);
                    
                    console.log('Article content:', articleContent);
                    console.log('Meta description:', metaDescription);
                    
                    // Display HTML content in visual tab
                    $content.html(articleContent);
                    
                    // Display raw HTML code in code tab
                    $('#article-code').text(articleContent);
                    
                    // Handle meta description
                    if (metaDescription && metaDescription.trim()) {
                        $('#meta-description-text').val(metaDescription);
                        $('#meta-description-length').text(metaDescription.length);
                        $('.talkgenai-meta-description-section').show();
                        console.log('Meta description displayed:', metaDescription);
                    } else {
                        // Try alternative structure
                        const altMetaDescription = response.data.meta_description || response.data.article?.meta_description;
                        if (altMetaDescription && altMetaDescription.trim()) {
                            $('#meta-description-text').val(altMetaDescription);
                            $('#meta-description-length').text(altMetaDescription.length);
                            $('.talkgenai-meta-description-section').show();
                            console.log('Meta description displayed (alternative):', altMetaDescription);
                        } else {
                            $('.talkgenai-meta-description-section').hide();
                            console.log('No meta description found in any structure');
                        }
                    }
                    
                    $resultArea.show();
                    
                    // Show success message with additional info
                    let successMsg = 'Article generated successfully!';
                    if (response.data.word_count) {
                        successMsg += ` (${response.data.word_count} words)`;
                    }
                    if (response.data.server_response_time) {
                        successMsg += ` - Generated in ${response.data.server_response_time.toFixed(2)}s`;
                    }
                    showNotification(successMsg, 'success');
                } else {
                    // Check if this is a structured error with HTML message (e.g., insufficient credits)
                    if (response.data && response.data.ai_message) {
                        const isHtml = response.data.is_html || (response.data.ai_message && response.data.ai_message.trim().startsWith('<'));
                        
                        // Show error notification
                        showNotification(response.data.message || 'Article generation failed', 'error');
                        
                        // Display HTML error message in the article content area
                        if (isHtml) {
                            $content.html(response.data.ai_message);
                            $resultArea.show();
                        }
                    } else {
                        showNotification(response.data.message || 'Article generation failed', 'error');
                    }
                }
            },
            error: function(xhr, status, error) {
                // Stop progress animation
                stopArticleProgressMessages();
                
                if (status === 'timeout') {
                    showNotification('Article generation timed out. The server is still processing - please wait and try again in a few minutes.', 'error');
                } else {
                    // Try to extract structured error from response
                    let errorMessage = 'Article generation failed: ' + error;
                    try {
                        if (xhr.responseJSON && xhr.responseJSON.data) {
                            if (xhr.responseJSON.data.ai_message) {
                                const isHtml = xhr.responseJSON.data.is_html || 
                                             (xhr.responseJSON.data.ai_message && xhr.responseJSON.data.ai_message.trim().startsWith('<'));
                                
                                errorMessage = xhr.responseJSON.data.message || errorMessage;
                                
                                // Display HTML error message in the article content area
                                if (isHtml) {
                                    $content.html(xhr.responseJSON.data.ai_message);
                                    $resultArea.show();
                                }
                            } else if (xhr.responseJSON.data.message) {
                                errorMessage = xhr.responseJSON.data.message;
                            }
                        }
                    } catch (e) {
                        // Ignore parsing errors
                    }
                    showNotification(errorMessage, 'error');
                }
            },
            complete: function() {
                $btn.prop('disabled', false).html('<span class="dashicons dashicons-edit"></span> Generate Article');
            }
        });
    }
    
    // Initialize articles page when document is ready
    $(document).ready(function() {
        initializeArticlesPage();
    });
    
    // Expose preview helpers globally so inline admin PHP can call them
    try {
        window.showPreview = showPreview;
        window.updatePreview = updatePreview;
    } catch(e) { /* no-op */ }

})(jQuery);
