/**
 * JavaScript for On-Page Optimization functionality
 * 
 * Handles the Associations Management table and modal
 */
(function($) {
    'use strict';

    const AUDIT_STOPWORDS = new Set([
        'si', 'sau', 'de', 'din', 'cu', 'la', 'in', 'pe', 'pentru', 'prin', 'un', 'o', 'ale', 'al', 'ai', 'a',
        'the', 'and', 'for', 'to', 'of', 'by', 'on', 'in'
    ]);

    // Track save operation status
    let isSaveInProgress = false;
    let currentEntityTab = 'all';
    let entityInventoryCache = null;
    let entityKeywordMap = {};
    let unlinkedRowsCache = [];
    let noContentRowsCache = [];
    let unlinkedSuggestionMap = {};
    let unlinkedBatchCursor = 0;
    let unlinkedContentBatchCursor = 0;
    let unlinkedSettingsCache = null;
    let inventoryRequestInFlight = false;
    const RECENT_CONTENT_GEN_STORAGE_KEY = 'aysa_recent_content_generated_v1';
    const RECENT_CONTENT_GEN_RETENTION_MS = 60 * 60 * 1000; // 1 hour
    let recentContentGeneratedMap = {};
    const ONPAGE_DEBUG = window.AYSA_DEBUG === true;

    function onPageDebugLog() {
        if (!ONPAGE_DEBUG) {
            return;
        }
        console.log.apply(console, arguments);
    }

    function requestAssociationThroughWordPress(operation, associationId, payload) {
        return $.ajax({
            url: aysa_params.ajax_url,
            method: 'POST',
            dataType: 'json',
            data: {
                action: 'aysa_onpage_association_proxy',
                nonce: aysa_params.nonce,
                operation: operation,
                association_id: associationId,
                payload: payload ? JSON.stringify(payload) : ''
            }
        }).then(function(response) {
            if (!response || !response.success || !response.data || !response.data.association) {
                return $.Deferred().reject(response).promise();
            }
            return response.data.association;
        });
    }

    function loadRecentContentGeneratedMap() {
        try {
            const raw = window.localStorage.getItem(RECENT_CONTENT_GEN_STORAGE_KEY);
            if (!raw) return {};
            const parsed = JSON.parse(raw);
            return parsed && typeof parsed === 'object' ? parsed : {};
        } catch (e) {
            return {};
        }
    }

    function saveRecentContentGeneratedMap() {
        try {
            window.localStorage.setItem(RECENT_CONTENT_GEN_STORAGE_KEY, JSON.stringify(recentContentGeneratedMap || {}));
        } catch (e) {
            // ignore storage failures
        }
    }

    function pruneRecentContentGeneratedMap() {
        const now = Date.now();
        const cleaned = {};
        Object.keys(recentContentGeneratedMap || {}).forEach((key) => {
            const ts = parseInt(recentContentGeneratedMap[key], 10) || 0;
            if (ts > 0 && (now - ts) <= RECENT_CONTENT_GEN_RETENTION_MS) {
                cleaned[key] = ts;
            }
        });
        recentContentGeneratedMap = cleaned;
        saveRecentContentGeneratedMap();
    }

    function markRecentlyGeneratedRow(rowKey) {
        if (!rowKey) return;
        pruneRecentContentGeneratedMap();
        recentContentGeneratedMap[String(rowKey)] = Date.now();
        saveRecentContentGeneratedMap();
    }

    function getRecentlyGeneratedTimestamp(rowKey) {
        if (!rowKey) return 0;
        pruneRecentContentGeneratedMap();
        const ts = parseInt((recentContentGeneratedMap || {})[String(rowKey)] || 0, 10) || 0;
        if (ts <= 0) return 0;
        if ((Date.now() - ts) > RECENT_CONTENT_GEN_RETENTION_MS) {
            delete recentContentGeneratedMap[String(rowKey)];
            saveRecentContentGeneratedMap();
            return 0;
        }
        return ts;
    }

    recentContentGeneratedMap = loadRecentContentGeneratedMap();
    pruneRecentContentGeneratedMap();

    // Initialize page functionality
    function initializeAssociationsTable() {
        // Check if we need to invalidate stale cache (older than 1 hour)
        const cachedData = getCachedAssociations();
        if (cachedData && !isCacheValid(cachedData.timestamp, 60 * 60 * 1000)) {
            onPageDebugLog('Cache is stale (older than 1 hour), invalidating...');
            invalidateAssociationsCache();
        }
        
        // Initialize status management first
        initializeStatusManagement().then(() => {
            // Fetch associations data
            fetchAssociations();
            
            // Initialize filters
            initializeFilters();
            initializeUnlinkedKeywordSuggestions();
        }).catch(error => {
            console.warn('Status management initialization failed, continuing without it:', error);
            // Fetch associations data even if status management fails
            fetchAssociations();
            
            // Initialize filters
            initializeFilters();
            initializeUnlinkedKeywordSuggestions();
        });
        
        // Tab functionality in modal
        $(document).on('click', '.aysa-modal-tab', function() {
            $('.aysa-modal-tab').removeClass('active');
            $(this).addClass('active');
            
            const tabId = $(this).data('tab');
            $('.aysa-modal-tab-content').removeClass('active');
            $(`#${tabId}-content`).addClass('active');
        });
        
        // Open modal when clicking View Details
        $(document).on('click', '.aysa-view-association-details', function() {
            const associationId = $(this).data('id');
            openAssociationModal(associationId);
        });
        
        // Navigate to associated page when clicking Navigate
        $(document).on('click', '.aysa-navigate-to-page', function(e) {
            e.preventDefault();
            e.stopPropagation();
            const $btn = $(this);
            const associationId = $btn.data('id');
            const entityType = $btn.data('entity-type');
            const entityId = parseInt($btn.data('entity-id'), 10) || 0;
            const editUrl = decodeHtmlEntities(String($btn.data('edit-url') || '').trim());
            
            onPageDebugLog('Navigate button clicked:', {
                associationId: associationId,
                entityType: entityType,
                entityId: entityId,
                editUrl: editUrl
            });

            if (isValidEditorUrl(editUrl)) {
                window.open(editUrl, '_blank');
                return;
            }

            const buildStrictFallbackEditorUrl = function(type, id) {
                if (!id || id <= 0) return '';
                const t = String(type || '').toLowerCase();
                if (t.includes('cat') || t === 'category' || t === 'term') {
                    return `${window.location.origin}/wp-admin/term.php?taxonomy=product_cat&tag_ID=${id}&post_type=product`;
                }
                return `${window.location.origin}/wp-admin/post.php?post=${id}&action=edit`;
            };

            const openIfValid = function(url) {
                const safeUrl = decodeHtmlEntities(String(url || '').trim());
                if (!isValidEditorUrl(safeUrl)) {
                    return false;
                }
                $btn.attr('data-edit-url', safeUrl);
                window.open(safeUrl, '_blank');
                return true;
            };

            $.ajax({
                url: aysa_params.ajax_url,
                type: 'POST',
                data: {
                    action: 'aysa_get_entity_details',
                    nonce: aysa_params.nonce,
                    entity_type: entityType,
                    entity_id: entityId
                },
                success: function(response) {
                    const resolvedEditUrl = response && response.success && response.data ? decodeHtmlEntities(String(response.data.edit_url || '')) : '';
                    if (openIfValid(resolvedEditUrl)) {
                        return;
                    }
                    const fallback = buildStrictFallbackEditorUrl(entityType, entityId);
                    if (openIfValid(fallback)) {
                        return;
                    }
                    showNotificationModal('Error', 'Could not determine the edit URL for this item.', 'error');
                },
                error: function() {
                    const fallback = buildStrictFallbackEditorUrl(entityType, entityId);
                    if (openIfValid(fallback)) {
                        return;
                    }
                    showNotificationModal('Error', 'Could not resolve the edit URL.', 'error');
                }
            });
            return;
            
            // If we have all the data directly in the button, use that
            if (entityType && entityId) {
                const associationData = {
                    associatedOn: entityType,
                    associatedValue: entityId
                };
                navigateToAssociatedPage(associationData);
                return;
            }
            
            // Otherwise, fetch the full association data
            requestAssociationThroughWordPress('get', associationId)
                .done(function(association) {
                    navigateToAssociatedPage(association);
                })
                .fail(function(error) {
                    console.error('Error fetching association details:', error);
                    showNotificationModal('Error', 'Error loading association details. Please try again.', 'error');
                });
        });
        
        // Close modal
        $(document).on('click', '.aysa-modal-close, #aysa-modal-close-btn, #cancel-edit-btn', function() {
            closeModal();
        });
        
        // Close modal when clicking outside
        $(window).on('click', function(event) {
            if ($(event.target).hasClass('aysa-modal')) {
                closeModal();
            }
        });
        
        // Save changes for optimization sections - using event delegation
        $(document).on('click', '.aysa-modal-save', function(e) {
            e.preventDefault();
            if (isSaveInProgress) return;
            
            const section = $(this).data('section');
            const associationId = $('#aysa-association-modal').data('association-id');
            
            onPageDebugLog('Save button clicked:', {
                section: section,
                associationId: associationId
            });
            
            if (!section || !associationId) {
                console.error('Missing required data for save:', {
                    section: section,
                    associationId: associationId
                });
                showNotificationModal('Error', 'Missing required data for save operation.', 'error');
                return;
            }
            
            saveChanges(section, associationId);
        });
        
        // Save association edits - use event delegation for dynamically created buttons
        $(document).on('click', '#save-association-btn', function(e) {
            e.preventDefault();
            const associationId = $('#aysa-association-modal').data('association-id');
            
            
            
            if (!associationId) {
                console.error('Missing association ID');
                showNotificationModal('Error', 'Missing association ID. Please try again.', 'error');
                return;
            }
            
            saveAssociationEdits(associationId);
        });

    }
    
    // Function to fetch associations
    function fetchAssociations() {
        // Check if we have cached associations and they're still valid
        const cachedAssociations = getCachedAssociations();
        if (cachedAssociations && isCacheValid(cachedAssociations.timestamp)) {
            const cachedData = Array.isArray(cachedAssociations.data) ? cachedAssociations.data : [];
            const cachedOnlyMain = cachedData.length > 0 && cachedData.every((assoc) => {
                const flag = assoc && assoc.isMain;
                return flag === true || flag === 1 || String(flag).toLowerCase() === 'true';
            });
            if (!cachedOnlyMain) {
                onPageDebugLog('Using cached associations data');
                window.aysaAssociations = cachedData;
                displayAssociations(cachedData);
                updateEntityCoverageAndUnlinked(cachedData);
                return;
            }
            onPageDebugLog('Invalidating legacy main-only associations cache');
            invalidateAssociationsCache();
        }
        
        // Show a loading message
        $('#aysa-associations-table tbody').html(`
            <tr>
                <td colspan="6" class="aysa-loading-cell">
                    Loading associations... Please wait.
                </td>
            </tr>
        `);
        
        // Source of truth is local WordPress AJAX (local-first), not direct API calls.
        const projectId =
            parseInt($('#aysa-current-project-id').val(), 10) ||
            parseInt(localStorage.getItem('aysa_project_id'), 10) ||
            parseInt(aysa_params?.project_id || 0, 10) ||
            0;

        $.ajax({
            url: aysa_params.ajax_url,
            type: 'POST',
            dataType: 'json',
            data: {
                action: 'aysa_onpage_get_associations',
                nonce: aysa_params.nonce,
                project_id: projectId
            },
            success: function(response) {
                const pageItems = response && response.success && response.data && Array.isArray(response.data.associations)
                    ? response.data.associations
                    : [];

                window.aysaAssociations = pageItems;
                saveAssociationsToCache(pageItems);
                // Keep load path resilient and fast: render immediately from local associations.
                // Status records can be created lazily by explicit actions later.
                displayAssociations(pageItems);
                updateEntityCoverageAndUnlinked(pageItems);
            },
            error: function(xhr) {
                console.error('Error fetching associations via local AJAX:', xhr);
                $('#aysa-associations-table tbody').html(`
                    <tr>
                        <td colspan="6" class="aysa-error-cell">
                            Error loading associations from local database. Please try again.
                        </td>
                    </tr>
                `);
            }
        });
    }
    
    // Function to get cached associations with timestamp validation
    function getCachedAssociations() {
        try {
            const cached = localStorage.getItem('aysa_associations_cache');
            if (cached) {
                return JSON.parse(cached);
            }
        } catch (e) {
            console.warn('Failed to parse cached associations:', e);
            localStorage.removeItem('aysa_associations_cache');
        }
        return null;
    }
    
    // Function to check if cache is still valid (5 minutes by default)
    function isCacheValid(timestamp, maxAge = 5 * 60 * 1000) {
        if (!timestamp) return false;
        const now = Date.now();
        const cacheAge = now - timestamp;
        return cacheAge < maxAge;
    }
    
    // Function to save associations to cache
    function saveAssociationsToCache(associations) {
        try {
            const cacheData = {
                data: associations,
                timestamp: Date.now()
            };
            localStorage.setItem('aysa_associations_cache', JSON.stringify(cacheData));
        } catch (e) {
            console.warn('Failed to save associations to cache:', e);
        }
    }
    
    // Function to invalidate associations cache
    function invalidateAssociationsCache() {
        localStorage.removeItem('aysa_associations_cache');
    }
    
    // Function to create initial status records for associations that don't have any status yet
    function createInitialStatusRecords(associations) {
        return new Promise((resolve) => {
            if (!associations || associations.length === 0) {
                resolve();
                return;
            }
            
            // Prepare associations data for the AJAX call
            const associationsData = associations.map(association => ({
                id: association.id,
                score: association.score || null
            }));
            
            // Use the nonce from aysaParams (aysa-ajax-nonce) which is available on this page
            const nonce = (window.aysaParams && window.aysaParams.nonce) || 
                         (window.aysa_params && window.aysa_params.nonce) || 
                         $('#aysa-nonce').val() || 
                         $('input[name="aysa_nonce"]').val() || 
                         'fallback_nonce';
            
            $.ajax({
                url: ajaxurl || (window.aysa_params && window.aysa_params.ajax_url),
                type: 'POST',
                data: {
                    action: 'aysa_create_initial_statuses',
                    nonce: nonce,
                    associations: associationsData
                },
                success: function(response) {
                    if (response.success) {
                        onPageDebugLog('Initial status records created:', response.data.message);
                        if (response.data.success_count > 0) {
                            // Reload statuses from database to get the newly created records
                            loadStatusesFromDatabase().then(() => {
                                resolve();
                            });
                        } else {
                            resolve();
                        }
                    } else {
                        console.warn('Failed to create initial status records:', response.data.message);
                        resolve(); // Continue even if this fails
                    }
                },
                error: function(xhr, status, error) {
                    console.warn('Error creating initial status records:', error);
                    resolve(); // Continue even if this fails
                }
            });
        });
    }
    
    // Status management with localStorage and database fallback
    let storedStatuses = {};
    let databaseStatuses = {};
    let statusesLoaded = false;
    
    // Function to get stored status with fallback
    function getStoredStatus(associationId) {
        // First try localStorage cache
        if (storedStatuses[associationId]) {
            return storedStatuses[associationId];
        }
        
        // Then try database cache
        if (databaseStatuses[associationId]) {
            return databaseStatuses[associationId];
        }
        
        // Fallback: try to load from localStorage directly
        try {
            const stored = localStorage.getItem('aysa_association_statuses');
            if (stored) {
                const statuses = JSON.parse(stored);
                if (statuses[associationId]) {
                    storedStatuses[associationId] = statuses[associationId];
                    return statuses[associationId];
                }
            }
        } catch (e) {
            console.warn('Error parsing stored statuses:', e);
        }
        
        return null;
    }
    
    // Function to store status in both localStorage and database
    function storeStatus(associationId, status) {
        // Update local cache
        storedStatuses[associationId] = status;
        
        // Store in localStorage
        try {
            let existingStatuses = {};
            const existing = localStorage.getItem('aysa_association_statuses');
            if (existing) {
                existingStatuses = JSON.parse(existing);
            }
            existingStatuses[associationId] = status;
            localStorage.setItem('aysa_association_statuses', JSON.stringify(existingStatuses));
        } catch (e) {
            console.warn('Failed to save to localStorage:', e);
        }
        
        // Store in database
        updateStatusInDatabase(associationId, status);
    }
    
    // Function to load statuses from localStorage
    function loadStatusesFromLocalStorage() {
        try {
            const stored = localStorage.getItem('aysa_association_statuses');
            if (stored) {
                const parsed = JSON.parse(stored);
                Object.assign(storedStatuses, parsed);
            }
        } catch (e) {
            console.warn('Failed to load from localStorage:', e);
        }
    }
    
    // Function to load statuses from database
    function loadStatusesFromDatabase() {
        return new Promise((resolve) => {
            // Use the nonce from aysaParams (aysa-ajax-nonce) which is available on this page
            const nonce = (window.aysaParams && window.aysaParams.nonce) || 
                         (window.aysa_params && window.aysa_params.nonce) || 
                         $('#aysa-nonce').val() || 
                         $('input[name="aysa_nonce"]').val() || 
                         'fallback_nonce';
            
            $.ajax({
                url: ajaxurl || (window.aysa_params && window.aysa_params.ajax_url),
                type: 'POST',
                data: {
                    action: 'aysa_get_all_association_statuses',
                    nonce: nonce
                },
                success: function(response) {
                    if (response.success && response.data && response.data.statuses) {
                        databaseStatuses = response.data.statuses;
                        
                        // Merge with localStorage (localStorage takes precedence)
                        Object.keys(databaseStatuses).forEach(id => {
                            if (!storedStatuses[id]) {
                                storedStatuses[id] = databaseStatuses[id];
                            }
                        });
                        
                        // Update localStorage with merged data
                        try {
                            localStorage.setItem('aysa_association_statuses', JSON.stringify(storedStatuses));
                        } catch (e) {
                            console.warn('Failed to update localStorage:', e);
                        }
                    } else {
                        console.warn('Failed to load statuses from database:', response);
                    }
                    resolve(storedStatuses);
                },
                error: function(xhr, status, error) {
                    console.warn('Database status load failed:', error);
                    resolve(storedStatuses);
                }
            });
        });
    }
    
    // Function to update status in database
    function updateStatusInDatabase(associationId, status) {
        const nonce = (window.aysaParams && window.aysaParams.nonce) || 
                     (window.aysa_params && window.aysa_params.nonce) || 
                     $('#aysa-nonce').val() || 
                     $('input[name="aysa_nonce"]').val() || 
                     'fallback_nonce';
        
        $.ajax({
            url: ajaxurl || (window.aysa_params && window.aysa_params.ajax_url),
            type: 'POST',
            data: {
                action: 'aysa_update_association_status',
                association_id: associationId,
                status: status,
                nonce: nonce
            },
            success: function(response) {
                if (response.success) {
                    databaseStatuses[associationId] = status;
                } else {
                    console.warn('Failed to update status in database:', response);
                }
            },
            error: function(xhr, status, error) {
                console.warn('Database status update failed:', error);
            }
        });
    }
    
    // Function to initialize status management
    async function initializeStatusManagement() {
        if (statusesLoaded) {
            return storedStatuses;
        }
        
        // Load from localStorage first
        loadStatusesFromLocalStorage();
        
        // Then load from database and merge
        await loadStatusesFromDatabase();
        
        statusesLoaded = true;
        return storedStatuses;
    }
    
    function escapeHtml(value) {
        return String(value || '')
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#039;');
    }

    function normalizeEntityGroup(entityType) {
        const type = String(entityType || '').toLowerCase();
        if (type.includes('product')) return 'product';
        if (type.includes('category') || type.includes('cat') || type.includes('product_cat')) return 'category';
        if (type.includes('page')) return 'other';
        if (type === 'post' || type.includes('article')) return 'article';
        return 'other';
    }

    function detectRowEntityGroup($row) {
        const attrGroup = String($row.attr('data-entity-group') || '').trim();
        if (attrGroup && attrGroup !== 'undefined' && attrGroup !== 'null') {
            return normalizeEntityGroup(attrGroup);
        }
        const associatedOnCell = String($row.attr('data-entity-type') || '').trim();
        return normalizeEntityGroup(associatedOnCell);
    }

    function buildEntityKeywordMap(associations) {
        const map = {};
        const normalize = function(value) {
            return String(value || '').trim().toLowerCase();
        };
        const splitKeywords = function(value) {
            return String(value || '')
                .split(',')
                .map((part) => String(part || '').trim())
                .filter((part) => part !== '');
        };
        (associations || []).forEach((association) => {
            const entityType = association.associatedOn || association.entityType || '';
            const entityId = association.associatedValue || association.idIntern || '';
            const keyword = String(association.keyword || '').trim();
            if (!entityType || !entityId || !keyword) {
                return;
            }
            const group = normalizeEntityGroup(entityType);
            const key = `${group}:${String(entityId)}`;
            if (!map[key]) {
                map[key] = {
                    main: new Set(),
                    secondary: new Set(),
                    _mainNorm: new Set(),
                    _secondaryNorm: new Set()
                };
            }

            const isMain = association.isMain === true || association.isMain === 1 || String(association.isMain).toLowerCase() === 'true';
            const normKeyword = normalize(keyword);
            if (normKeyword) {
                if (isMain) {
                    if (!map[key]._mainNorm.has(normKeyword)) {
                        map[key]._mainNorm.add(normKeyword);
                        map[key].main.add(keyword);
                    }
                } else {
                    if (!map[key]._secondaryNorm.has(normKeyword)) {
                        map[key]._secondaryNorm.add(normKeyword);
                        map[key].secondary.add(keyword);
                    }
                }
            }

            // Parse API secondary keywords list if present on the association payload.
            splitKeywords(association.secondKeyword || association.second_keyword).forEach((secondaryKeyword) => {
                const normalizedSecondary = normalize(secondaryKeyword);
                if (!normalizedSecondary) return;
                if (normalizedSecondary === normKeyword) return;
                if (map[key]._mainNorm.has(normalizedSecondary)) return;
                if (map[key]._secondaryNorm.has(normalizedSecondary)) return;
                map[key]._secondaryNorm.add(normalizedSecondary);
                map[key].secondary.add(secondaryKeyword);
            });
        });
        return map;
    }

    function renderEntityKeywordsChips(entityType, entityId) {
        const key = `${normalizeEntityGroup(entityType)}:${String(entityId || '')}`;
        const entry = entityKeywordMap[key] || null;
        const mainKeywords = entry ? Array.from(entry.main).sort((a, b) => a.localeCompare(b)) : [];
        const secondaryKeywords = entry ? Array.from(entry.secondary).sort((a, b) => a.localeCompare(b)) : [];
        if (!mainKeywords.length && !secondaryKeywords.length) {
            return '<span class="aysa-unlinked-chip">No keywords</span>';
        }
        let html = '';
        if (mainKeywords.length) {
            html += `<div class="aysa-keyword-group aysa-keyword-group-main"><strong class="aysa-keyword-title aysa-keyword-title-main">Main keyword:</strong> ${mainKeywords.map((keyword) => `<span class="aysa-unlinked-chip aysa-unlinked-chip-main">${escapeHtml(keyword)}</span>`).join('')}</div>`;
        } else {
            html += `<div class="aysa-keyword-group aysa-keyword-group-main"><strong class="aysa-keyword-title aysa-keyword-title-main">Main keyword:</strong> <span class="aysa-unlinked-chip aysa-unlinked-chip-main">-</span></div>`;
        }
        if (secondaryKeywords.length) {
            html += `<div class="aysa-keyword-group aysa-keyword-group-secondary"><strong class="aysa-keyword-title aysa-keyword-title-secondary">Secondary keywords:</strong> ${secondaryKeywords.map((keyword) => `<span class="aysa-unlinked-chip aysa-unlinked-chip-secondary">${escapeHtml(keyword)}</span>`).join('')}</div>`;
        } else {
            html += `<div class="aysa-keyword-group aysa-keyword-group-secondary"><strong class="aysa-keyword-title aysa-keyword-title-secondary">Secondary keywords:</strong> <span class="aysa-unlinked-chip aysa-unlinked-chip-secondary">-</span></div>`;
        }
        return html;
    }

    function getEntityKeywordsList(entityType, entityId) {
        const key = `${normalizeEntityGroup(entityType)}:${String(entityId || '')}`;
        const entry = entityKeywordMap[key] || null;
        if (!entry) return { main: [], secondary: [], all: [] };
        const main = Array.from(entry.main || []).map((k) => String(k || '').trim()).filter(Boolean);
        const secondary = Array.from(entry.secondary || []).map((k) => String(k || '').trim()).filter(Boolean);
        const seen = new Set();
        const all = [];
        [...main, ...secondary].forEach((kw) => {
            const norm = normalizeKeywordValue(kw);
            if (!norm || seen.has(norm)) return;
            seen.add(norm);
            all.push(kw);
        });
        return { main, secondary, all };
    }

    function renderEntityKeywordsInline(entityType, entityId, fallbackKeyword = '') {
        const lists = getEntityKeywordsList(entityType, entityId);
        if (lists.all.length) {
            return lists.all.map((kw) => `<span class="aysa-unlinked-chip">${escapeHtml(kw)}</span>`).join('');
        }
        const fallback = String(fallbackKeyword || '').trim();
        if (fallback) {
            return `<span class="aysa-unlinked-chip aysa-unlinked-chip-main">${escapeHtml(fallback)}</span>`;
        }
        return '<span class="aysa-unlinked-chip">No keywords</span>';
    }

    function getUniqueAssociationsByEntity(associations) {
        const grouped = {};
        const priority = function(association) {
            const isMain = association && (association.isMain === true || association.isMain === 1 || String(association.isMain).toLowerCase() === 'true');
            if (isMain) return 0;
            return 1;
        };
        const stableId = function(association) {
            const raw = association && (association.id || association.Id || '');
            const n = parseInt(raw, 10);
            return Number.isFinite(n) ? n : Number.MAX_SAFE_INTEGER;
        };

        (associations || []).forEach((association) => {
            const entityType = String(association.associatedOn || association.entityType || '').trim();
            const entityId = String(association.associatedValue || association.idIntern || '').trim();
            if (!entityType || !entityId) return;
            const key = `${normalizeEntityGroup(entityType)}:${entityId}`;
            if (!grouped[key]) {
                grouped[key] = association;
                return;
            }

            const current = grouped[key];
            const currentPriority = priority(current);
            const incomingPriority = priority(association);
            if (incomingPriority < currentPriority) {
                grouped[key] = association;
                return;
            }
            if (incomingPriority === currentPriority && stableId(association) < stableId(current)) {
                grouped[key] = association;
            }
        });

        return Object.values(grouped);
    }

    function normalizeAuditText(value) {
        return String(value || '')
            .toLowerCase()
            .normalize('NFD')
            .replace(/[\u0300-\u036f]/g, '')
            .replace(/[^a-z0-9]+/g, ' ')
            .trim();
    }

    function tokenizeAudit(value) {
        return normalizeAuditText(value)
            .split(/\s+/)
            .map((part) => part.trim())
            .filter((part) => part.length >= 2 && !AUDIT_STOPWORDS.has(part));
    }

    function computeOverlapRatio(sourceTokens, targetTokens) {
        if (!sourceTokens.length || !targetTokens.length) {
            return 0;
        }
        const sourceSet = new Set(sourceTokens);
        const targetSet = new Set(targetTokens);
        let hits = 0;
        sourceSet.forEach((token) => {
            if (targetSet.has(token)) {
                hits += 1;
            }
        });
        return hits / Math.max(sourceSet.size, 1);
    }

    function resolveAuditStatus(score) {
        if (score >= 75) {
            return { status: 'good', cls: 'good', label: 'Good' };
        }
        if (score >= 45) {
            return { status: 'needs-work', cls: 'warning', label: 'Needs Work' };
        }
        return { status: 'poor', cls: 'bad', label: 'Poor' };
    }

    function buildAssociationAudit(association, entityData, entityType, isOrphan) {
        if (isOrphan) {
            return {
                score: 0,
                status: 'poor',
                cls: 'bad',
                label: 'Poor',
                reason: 'Orphan association: entity missing.'
            };
        }

        const keyword = String((association && association.keyword) || '').trim();
        const title = String((entityData && entityData.name) || '').trim();
        const url = String((entityData && entityData.url) || '').trim();
        const keywordNorm = normalizeAuditText(keyword);
        const titleNorm = normalizeAuditText(title);
        const urlNorm = normalizeAuditText(url.replace(/^https?:\/\//i, ''));
        const keywordTokens = tokenizeAudit(keyword);
        const titleTokens = tokenizeAudit(title);
        const urlTokens = tokenizeAudit(urlNorm);

        if (!keywordNorm || !titleNorm) {
            return {
                score: 20,
                status: 'poor',
                cls: 'bad',
                label: 'Poor',
                reason: 'Missing keyword or entity title.'
            };
        }

        const overlapTitle = computeOverlapRatio(keywordTokens, titleTokens);
        const overlapUrl = computeOverlapRatio(keywordTokens, urlTokens);
        const exactInTitle = titleNorm.includes(keywordNorm) ? 1 : 0;
        const exactInUrl = urlNorm.includes(keywordNorm) ? 1 : 0;

        let score = 0;
        score += overlapTitle * 55;
        score += overlapUrl * 25;
        score += exactInTitle * 14;
        score += exactInUrl * 6;

        if (keywordTokens.length <= 1) {
            score -= 12;
        }

        const typeNorm = String(entityType || '').toLowerCase();
        if (typeNorm === 'product' && /buchet|trandafir|aranjament|flori/.test(keywordNorm)) {
            score += 5;
        }
        if (typeNorm === 'category' && /categorie|colectie|tipuri|modele/.test(keywordNorm)) {
            score += 4;
        }
        if (typeNorm === 'post' && /ghid|sfaturi|cum|top|ce este/.test(keywordNorm)) {
            score += 4;
        }

        score = Math.max(0, Math.min(100, Math.round(score)));
        const resolved = resolveAuditStatus(score);

        let reason = 'Semantic/title-url alignment looks acceptable.';
        if (score < 45) {
            reason = 'Weak match between keyword and entity title/URL.';
        } else if (score < 75) {
            reason = 'Partial match; review manually.';
        }

        return {
            score: score,
            status: resolved.status,
            cls: resolved.cls,
            label: resolved.label,
            reason: reason
        };
    }

    function applyAssociationAuditToRow($row, audit) {
        if (!$row || !$row.length) return;
        $row.attr('data-audit-score', String(audit.score));
        $row.attr('data-association-quality', audit.status);
    }

    function getSeoStatusFromEntityScore(score) {
        const value = parseInt(score, 10);
        if (!Number.isFinite(value)) {
            return { statusClass: 'warning', statusText: 'Needs Work', statusData: 'needs-work' };
        }
        if (value >= 80) {
            return { statusClass: 'good', statusText: 'Good', statusData: 'good' };
        }
        if (value < 50) {
            return { statusClass: 'bad', statusText: 'Poor', statusData: 'poor' };
        }
        return { statusClass: 'warning', statusText: 'Needs Work', statusData: 'needs-work' };
    }

    function normalizeContentSource(sourceValue) {
        const normalized = String(sourceValue || '').toLowerCase();
        if (normalized === 'aysa_generated') {
            return { key: 'aysa_generated', label: 'AYSA Generated', cls: 'aysa-source-generated' };
        }
        if (normalized === 'original') {
            return { key: 'original', label: 'Original', cls: 'aysa-source-original' };
        }
        return { key: 'unknown', label: 'Unknown', cls: 'aysa-source-unknown' };
    }

    function normalizeIntentBadge(intentValue) {
        const normalized = String(intentValue || '').trim().toLowerCase();
        if (!normalized || normalized === 'loading') {
            return { label: 'Unknown', cls: 'aysa-intent-unknown' };
        }
        return {
            label: normalized.charAt(0).toUpperCase() + normalized.slice(1),
            cls: `aysa-intent-${normalized.replace(/[^a-z0-9_-]/g, '-')}`
        };
    }

    function applyEntitySeoSnapshotToRow($row, entityData, storedStatus) {
        if (!$row || !$row.length) return;
        const source = normalizeContentSource(entityData && entityData.content_source);
        const scoreRaw = entityData && entityData.seo_score;
        const score = Number.isFinite(parseInt(scoreRaw, 10)) ? parseInt(scoreRaw, 10) : null;

        const $statusCell = $row.find('.aysa-optimized-status-cell').first();
        const $statusBadge = $statusCell.find('.aysa-status').first();
        const $scorePill = $statusCell.find('.aysa-seo-score-pill').first();
        $statusCell.attr('data-content-source', source.key);
        if ($scorePill.length) {
            $scorePill.replaceWith(
                score === null
                    ? '<span class="aysa-seo-score-pill">N/A</span>'
                    : `<span class="aysa-seo-score-pill">${score}/100</span>`
            );
        }

        // Keep manual "Processed" override, otherwise derive SEO status from stored entity score.
        if (String(storedStatus || '').toLowerCase() === 'processed') {
            $statusBadge.removeClass('good warning bad').addClass('good').text('Processed').attr('data-status', 'processed');
            $row.attr('data-status', 'processed');
            return;
        }

        const resolved = getSeoStatusFromEntityScore(score);
        $statusBadge
            .removeClass('good warning bad')
            .addClass(resolved.statusClass)
            .text(resolved.statusText)
            .attr('data-status', resolved.statusData);
        $row.attr('data-status', resolved.statusData);
    }

    function markAssociationRowOrphan($row, associationId, entityId) {
        if (!$row || !$row.length) return;
        const audit = {
            score: 0,
            status: 'poor',
            cls: 'bad',
            label: 'Poor',
            reason: `Orphan association: entity ${entityId} no longer exists.`
        };
        applyAssociationAuditToRow($row, audit);
        const $statusCell = $row.find('.aysa-optimized-status-cell').first();
        $statusCell.attr('data-content-source', 'unknown');
        const $scorePill = $statusCell.find('.aysa-seo-score-pill').first();
        if ($scorePill.length) {
            $scorePill.replaceWith('<span class="aysa-seo-score-pill">N/A</span>');
        }
        const $statusBadge = $statusCell.find('.aysa-status').first();
        $statusBadge.removeClass('good warning').addClass('bad').text('Poor').attr('data-status', 'poor');
        $row.attr('data-status', 'poor');
        $row.attr('data-orphan', '1');

        const $actions = $row.find('.aysa-action-buttons');
        // Keep orphan cleanup out of the client-facing workflow.
    }

    function getSuggestionAjaxContext() {
        const ajaxUrl = (aysa_params && aysa_params.ajax_url) || (window.aysaParams && window.aysaParams.ajax_url) || window.ajaxurl || '';
        const nonce = (aysa_params && aysa_params.nonce) || (window.aysaParams && window.aysaParams.nonce) || '';
        const hiddenProjectId = parseInt($('#aysa-current-project-id').val(), 10) || 0;
        const localProjectId = parseInt(localStorage.getItem('aysa_project_id') || '0', 10) || 0;
        const projectId = hiddenProjectId > 0 ? hiddenProjectId : localProjectId;
        return { ajaxUrl, nonce, projectId };
    }

    function getExistingKeywordsFromAssociations() {
        const out = [];
        const seen = new Set();
        const addKeyword = function(raw) {
            const keyword = String(raw || '').trim();
            if (!keyword) return;
            const norm = normalizeKeywordValue(keyword);
            if (!norm || seen.has(norm)) return;
            seen.add(norm);
            out.push(keyword);
        };
        const splitKeywords = function(value) {
            return String(value || '')
                .split(',')
                .map((part) => String(part || '').trim())
                .filter((part) => part !== '');
        };
        (window.aysaAssociations || []).forEach((association) => {
            if (!association) return;
            addKeyword(association.keyword);
            splitKeywords(association.secondKeyword || association.second_keyword).forEach(addKeyword);
            splitKeywords(association.meta_keywords || association.metaKeywords).forEach(addKeyword);
        });
        return out;
    }

    function getEntityKeywordsForRow(row) {
        if (!row) return [];
        const key = `${normalizeEntityGroup(row.group || '')}:${String(row.id || '')}`;
        const entry = entityKeywordMap[key] || null;
        if (!entry) return [];
        const keywords = [];
        const seen = new Set();
        const pushUnique = function(value) {
            const keyword = String(value || '').trim();
            const norm = normalizeKeywordValue(keyword);
            if (!norm || seen.has(norm)) return;
            seen.add(norm);
            keywords.push(keyword);
        };
        Array.from(entry.main || []).forEach(pushUnique);
        Array.from(entry.secondary || []).forEach(pushUnique);
        return keywords;
    }

    function readSuggestionSettingsFromInputs() {
        const maxKeywords = Math.max(1, Math.min(10, parseInt($('#aysa-unlinked-max-keywords').val(), 10) || 3));
        const minWords = Math.max(1, Math.min(6, parseInt($('#aysa-unlinked-min-words').val(), 10) || 2));
        const maxWords = Math.max(minWords, Math.min(8, parseInt($('#aysa-unlinked-max-words').val(), 10) || 4));
        const minScore = Math.max(0, Math.min(100, parseFloat($('#aysa-unlinked-min-score').val()) || 20));
        return {
            max_keywords: maxKeywords,
            min_keyword_words: minWords,
            max_keyword_words: maxWords,
            min_score: minScore,
            billing_multiplier: 5
        };
    }

    function applySuggestionSettingsToInputs(settings) {
        const merged = Object.assign({
            max_keywords: 3,
            min_keyword_words: 2,
            max_keyword_words: 4,
            min_score: 20
        }, settings || {});
        $('#aysa-unlinked-max-keywords').val(merged.max_keywords);
        $('#aysa-unlinked-min-words').val(merged.min_keyword_words);
        $('#aysa-unlinked-max-words').val(merged.max_keyword_words);
        $('#aysa-unlinked-min-score').val(merged.min_score);
    }

    function updateSuggestionStatus(message, level) {
        const $status = $('#aysa-suggest-keywords-status');
        if (!$status.length) return;
        const normalized = String(level || 'info');
        $status.removeClass('aysa-status-info aysa-status-ok aysa-status-warn aysa-status-error')
            .addClass(
                normalized === 'ok'
                    ? 'aysa-status-ok'
                    : normalized === 'warn'
                        ? 'aysa-status-warn'
                        : normalized === 'error'
                            ? 'aysa-status-error'
                            : 'aysa-status-info'
            )
            .text(message);
    }

    function updateContentGenStatus(message, level) {
        const $status = $('#aysa-content-gen-status');
        if (!$status.length) return;
        const normalized = String(level || 'info');
        $status.removeClass('aysa-status-info aysa-status-ok aysa-status-warn aysa-status-error')
            .addClass(
                normalized === 'ok'
                    ? 'aysa-status-ok'
                    : normalized === 'warn'
                        ? 'aysa-status-warn'
                        : normalized === 'error'
                            ? 'aysa-status-error'
                            : 'aysa-status-info'
            )
            .text(message);
    }

    function renderUnlinkedSuggestionCell(state) {
        if (!state) {
            return '<span class="aysa-suggestion-meta">No suggestion yet</span>';
        }
        const status = String(state.status || '').toLowerCase();
        if (status === 'processing') {
            return '<span class="aysa-suggestion-meta">Processing...</span>';
        }
        if (status === 'error') {
            return `<span class="aysa-suggestion-meta">${escapeHtml(state.error || 'Suggestion failed')}</span>`;
        }
        const sourceLabel = state.operation === 'content_gen'
            ? 'Source: content gen'
            : state.source === 'fallback' || state.source === 'hybrid_fallback'
                ? 'Source: local fallback'
                : 'Source: API broker';
        const readyLabel = state.operation === 'content_gen' && state.content_generated
            ? 'Content generated'
            : 'Ready';
        const saveLabel = state.saved === true
            ? `Association saved${state.selected_keyword ? ` (${escapeHtml(state.selected_keyword)})` : ''}${state.save_note ? ` - ${escapeHtml(state.save_note)}` : ''}`
            : state.saved === 'pending'
                ? 'Saving association...'
            : state.saved === false
                ? `Association not saved: ${escapeHtml(state.save_error || 'unknown error')}`
                : readyLabel;
        const suggestions = Array.isArray(state.suggestions) ? state.suggestions : [];
        if (!suggestions.length) {
            if (state.saved === true || state.saved === 'pending' || state.saved === false) {
                return `<span class="aysa-suggestion-meta">${sourceLabel} | ${saveLabel}</span>`;
            }
            return '<span class="aysa-suggestion-meta">No suggestion above threshold</span>';
        }
        const chips = suggestions
            .map((s) => `<span class="aysa-suggestion-keyword">${escapeHtml(s.keyword)} (${escapeHtml(String(s.score))})</span>`)
            .join('');
        return chips + `<div class="aysa-suggestion-meta">${sourceLabel} | ${saveLabel}</div>`;
    }

    function renderUnlinkedCostCell(state) {
        if (!state) {
            return '<span class="aysa-suggestion-meta">-</span>';
        }
        const hasCost = state.billable_cost_usd !== undefined && state.billable_cost_usd !== null && state.billable_cost_usd !== '';
        if (!hasCost) {
            return '<span class="aysa-suggestion-meta">-</span>';
        }
        const amount = Number(state.billable_cost_usd);
        if (Number.isNaN(amount)) {
            return '<span class="aysa-suggestion-meta">-</span>';
        }
        return `<span class="aysa-cost-pill">$${amount.toFixed(6)}</span>`;
    }

    function updateUnlinkedRowCells(rowKey) {
        const state = unlinkedSuggestionMap[rowKey] || null;
        const selectors = ['#aysa-unlinked-entities-table tbody tr', '#aysa-no-content-entities-table tbody tr'];
        selectors.forEach((selector) => {
            const $row = $(selector).filter(function() {
                return String($(this).attr('data-unlinked-key') || '') === String(rowKey);
            }).first();
            if (!$row.length) return;
            $row.find('td:eq(3)').html(renderUnlinkedSuggestionCell(state));
            $row.find('td:eq(4)').html(renderUnlinkedCostCell(state));
        });
    }

    function mapGroupToAssociationType(group, sourceType) {
        const g = normalizeEntityGroup(group);
        const src = String(sourceType || '').toLowerCase();
        if (g === 'product') return 'Product';
        if (g === 'category') return 'Category';
        if (g === 'article') return 'Post';
        if (g === 'other') {
            // "Other" includes pages + many technical/custom post types.
            // Only map types that the association API supports.
            if (src === 'page') return 'Page';
            if (src === 'post') return 'Post';
            if (src === 'product' || src.indexOf('product') !== -1) return 'Product';
            if (src === 'category' || src === 'product_cat' || src.indexOf('cat') !== -1) return 'Category';
            // Fallback for custom post types edited via post.php
            return 'Page';
        }
        return '';
    }

    function buildAssociationPayloadForRow(row, suggestion, projectId) {
        const keyword = String((suggestion && suggestion.keyword) || '').trim();
        if (!keyword) return null;
        const associatedType = mapGroupToAssociationType(row.group, row.source_type);
        if (!associatedType) {
            return null;
        }
        const payload = {
            IdIntern: parseInt(row.id, 10) || 0,
            AssociatedOn: associatedType,
            Value: String(row.name || ''),
            Link: String(row.url || row.edit_url || ''),
            Keyword: keyword,
            IsMain: true,
            IsTracked: true,
            SecondKeyword: ''
        };
        if (projectId && Number(projectId) > 0) {
            payload.ProjectId = Number(projectId);
        }
        if (!payload.IdIntern || !payload.AssociatedOn) {
            return null;
        }
        return payload;
    }

    function upsertAssociationForRow(row, suggestion, projectId) {
        const payload = buildAssociationPayloadForRow(row, suggestion, projectId);
        if (!payload) {
            return $.Deferred().reject(
                `Unsupported entity type for auto-association (group: ${String(row && row.group ? row.group : '')}, source: ${String(row && row.source_type ? row.source_type : '')}).`
            ).promise();
        }
        const ajaxUrl = (aysa_params && aysa_params.ajax_url) || (window.aysa_ajax && window.aysa_ajax.ajax_url) || (window.aysaParams && window.aysaParams.ajax_url) || window.ajaxurl || '';
        const nonce = (aysa_params && aysa_params.nonce) || (window.aysa_ajax && window.aysa_ajax.nonce) || (window.aysaParams && window.aysaParams.nonce) || '';
        if (!ajaxUrl || !nonce) {
            return $.Deferred().reject('Missing AJAX context for upsert').promise();
        }
        return $.ajax({
            url: ajaxUrl,
            method: 'POST',
            timeout: 15000,
            dataType: 'json',
            data: {
                action: 'aysa_keyword_discovery_upsert_association',
                nonce: nonce,
                id_intern: payload.IdIntern,
                associated_on: payload.AssociatedOn,
                project_id: payload.ProjectId || projectId || 0,
                keyword: payload.Keyword,
                value: payload.Value
            }
        }).then(function(response) {
            if (!response || response.success !== true) {
                let msg = 'upsert failed';
                if (response && response.data !== undefined && response.data !== null) {
                    if (typeof response.data === 'string') {
                        msg = String(response.data);
                    } else if (response.data && response.data.message) {
                        msg = String(response.data.message);
                    } else if (response.data && response.data.api_error) {
                        msg = String(response.data.api_error);
                    } else {
                        try {
                            msg = JSON.stringify(response.data);
                        } catch (e) {
                            msg = 'upsert failed';
                        }
                    }
                }
                return $.Deferred().reject(msg).promise();
            }
            return response;
        });
    }

    function normalizeKeywordValue(value) {
        return String(value || '').trim().toLowerCase();
    }

    function extractAjaxErrorText(xhr) {
        if (typeof xhr === 'string') return xhr;
        if (xhr && xhr.responseJSON) {
            if (xhr.responseJSON.data && xhr.responseJSON.data.message) return String(xhr.responseJSON.data.message);
            if (xhr.responseJSON.data && xhr.responseJSON.data.api_error) return String(xhr.responseJSON.data.api_error);
            if (typeof xhr.responseJSON.data === 'string') return String(xhr.responseJSON.data);
            if (xhr.responseJSON.message) return String(xhr.responseJSON.message);
            if (xhr.responseJSON.error) return String(xhr.responseJSON.error);
            if (xhr.responseJSON.data) {
                try {
                    return JSON.stringify(xhr.responseJSON.data);
                } catch (e) {
                    return String(xhr.responseJSON.data);
                }
            }
        }
        if (xhr && xhr.responseText) {
            try {
                const parsed = JSON.parse(xhr.responseText);
                if (parsed && parsed.data && parsed.data.message) return String(parsed.data.message);
                if (parsed && parsed.data && parsed.data.api_error) return String(parsed.data.api_error);
                if (parsed && typeof parsed.data === 'string') return String(parsed.data);
                if (parsed && parsed.message) return String(parsed.message);
                if (parsed && parsed.error) return String(parsed.error);
            } catch (e) {
                return String(xhr.responseText);
            }
            return String(xhr.responseText);
        }
        if (xhr && xhr.statusText) {
            return String(xhr.statusText);
        }
        return 'upsert failed';
    }

    function isDuplicateKeywordError(message) {
        const text = String(message || '').toLowerCase();
        return text.includes('already associated') ||
            text.includes('already linked') ||
            text.includes('duplicate') ||
            text.includes('already exists');
    }

    function saveAssociationWithAlternatives(row, suggestions, projectId, usedKeywords, existingKeywordsSet) {
        const dfd = $.Deferred();
        const seenLocal = new Set();
        const rejectedKeywords = new Set();
        const queue = (Array.isArray(suggestions) ? suggestions : [])
            .filter((s) => s && String(s.keyword || '').trim() !== '')
            .filter((s) => {
                const norm = normalizeKeywordValue(s.keyword);
                if (!norm || seenLocal.has(norm)) return false;
                seenLocal.add(norm);
                return true;
            });

        const tryNext = function(index, lastError) {
            if (index >= queue.length) {
                const remainingSuggestions = queue.filter((item) => {
                    const norm = normalizeKeywordValue(item && item.keyword);
                    return norm && !rejectedKeywords.has(norm);
                });
                dfd.reject({
                    message: lastError || 'All suggested keywords are already associated or invalid.',
                    remainingSuggestions: remainingSuggestions
                });
                return;
            }
            const candidate = queue[index];
            const normKeyword = normalizeKeywordValue(candidate.keyword);
            if (!normKeyword || usedKeywords.has(normKeyword)) {
                if (normKeyword) {
                    rejectedKeywords.add(normKeyword);
                }
                tryNext(index + 1, lastError);
                return;
            }
            upsertAssociationForRow(row, candidate, projectId)
                .done(function(response) {
                    usedKeywords.add(normKeyword);
                    const secondaryCandidates = queue.filter((item) => {
                        const itemNorm = normalizeKeywordValue(item && item.keyword);
                        return itemNorm && !rejectedKeywords.has(itemNorm) && itemNorm !== normKeyword;
                    });
                    const savedSecondary = [];
                    const failedSecondary = [];
                    const saveSecondaryAt = function(secIndex) {
                        if (secIndex >= secondaryCandidates.length) {
                            dfd.resolve({
                                response: response,
                                keyword: candidate.keyword,
                                secondary_keywords_saved: savedSecondary,
                                secondary_keywords_failed: failedSecondary,
                                remainingSuggestions: failedSecondary.map((item) => ({ keyword: item.keyword }))
                            });
                            return;
                        }
                        const sec = secondaryCandidates[secIndex];
                        const secNorm = normalizeKeywordValue(sec && sec.keyword);
                        if (!secNorm || usedKeywords.has(secNorm)) {
                            rejectedKeywords.add(secNorm);
                            saveSecondaryAt(secIndex + 1);
                            return;
                        }
                        upsertAssociationForRow(row, sec, projectId)
                            .done(function() {
                                usedKeywords.add(secNorm);
                                savedSecondary.push(String(sec.keyword || ''));
                                saveSecondaryAt(secIndex + 1);
                            })
                            .fail(function(secErr) {
                                const secErrText = extractAjaxErrorText(secErr);
                                if (isDuplicateKeywordError(secErrText)) {
                                    rejectedKeywords.add(secNorm);
                                    saveSecondaryAt(secIndex + 1);
                                    return;
                                }
                                failedSecondary.push({ keyword: String(sec.keyword || ''), error: secErrText });
                                saveSecondaryAt(secIndex + 1);
                            });
                    };
                    saveSecondaryAt(0);
                })
                .fail(function(xhr) {
                    const errText = extractAjaxErrorText(xhr);
                    if (isDuplicateKeywordError(errText)) {
                        if (normKeyword) {
                            rejectedKeywords.add(normKeyword);
                        }
                        tryNext(index + 1, errText);
                        return;
                    }
                    const remainingSuggestions = queue.filter((item) => {
                        const itemNorm = normalizeKeywordValue(item && item.keyword);
                        return itemNorm && !rejectedKeywords.has(itemNorm);
                    });
                    dfd.reject({
                        message: errText,
                        remainingSuggestions: remainingSuggestions
                    });
                });
        };

        tryNext(0, '');
        return dfd.promise();
    }

    function updateEntityCoverageAndUnlinked(associations) {
        if (inventoryRequestInFlight) {
            return;
        }
        const renderInventoryFallback = function(reason) {
            const hasExistingInventory = !!(entityInventoryCache && ['product', 'category', 'article', 'other'].some((key) => {
                return Array.isArray(entityInventoryCache[key]) && entityInventoryCache[key].length > 0;
            }));
            if (hasExistingInventory) {
                console.warn('Skipping inventory fallback because cached inventory is already available:', reason || 'unknown');
                return;
            }
            console.warn('Entities inventory unavailable for coverage:', reason || 'unknown');
            entityInventoryCache = { product: [], category: [], article: [], other: [] };
            noContentRowsCache = [];
            unlinkedRowsCache = [];
            $('#aysa-unlinked-entities-table tbody').html(
                '<tr><td colspan="6" class="aysa-empty-cell">Inventory unavailable right now. Retry in a few seconds.</td></tr>'
            );
            $('#aysa-no-content-entities-table tbody').html(
                '<tr><td colspan="6" class="aysa-empty-cell">Cannot load AYSA content inventory right now. Retry in a few seconds.</td></tr>'
            );
            renderCoverageSummary(associations, entityInventoryCache);
            updateEntityTabCounts(associations, entityInventoryCache);
        };

        const requestInventory = function(attempt) {
            $.ajax({
                url: aysa_params.ajax_url,
                type: 'POST',
                dataType: 'json',
                timeout: 30000,
                data: {
                    action: 'aysa_get_entities_inventory',
                    nonce: aysa_params.nonce
                },
                success: function(response) {
                    inventoryRequestInFlight = false;
                    if (!response || !response.success || !response.data || !response.data.inventory) {
                        renderInventoryFallback('invalid inventory response');
                        return;
                    }
                    entityInventoryCache = response.data.inventory;
                    renderCoverageSummary(associations, entityInventoryCache);
                    renderUnlinkedEntitiesTable(associations, entityInventoryCache);
                    updateEntityTabCounts(associations, entityInventoryCache);
                },
                error: function(xhr, textStatus) {
                    const shouldRetry = attempt < 1 && (textStatus === 'timeout' || textStatus === 'abort' || textStatus === 'parsererror' || (xhr && xhr.status === 0));
                    if (shouldRetry) {
                        setTimeout(function() {
                            requestInventory(attempt + 1);
                        }, 500);
                        return;
                    }
                    inventoryRequestInFlight = false;
                    const status = xhr && xhr.status ? xhr.status : 'unknown';
                    renderInventoryFallback(`ajax_error_${status}_${String(textStatus || '')}`);
                }
            });
        };

        inventoryRequestInFlight = true;
        requestInventory(0);
    }

    function calculateCoverageSummary(associations, inventory) {
        const associatedKeys = new Set();
        (associations || []).forEach((association) => {
            const entityType = association.associatedOn || association.entityType || '';
            const entityId = association.associatedValue || association.idIntern || '';
            if (!entityId) return;
            associatedKeys.add(`${normalizeEntityGroup(entityType)}:${String(entityId)}`);
        });

        const groups = ['product', 'category', 'article', 'other'];
        const summary = {};
        groups.forEach((group) => {
            const entities = Array.isArray(inventory[group]) ? inventory[group] : [];
            let withKeywords = 0;
            entities.forEach((entity) => {
                if (associatedKeys.has(`${group}:${String(entity.id)}`)) {
                    withKeywords += 1;
                }
            });
            const total = entities.length;
            summary[group] = {
                total: total,
                withKeywords: withKeywords,
                withoutKeywords: Math.max(total - withKeywords, 0)
            };
        });
        return summary;
    }

    function renderCoverageSummary(associations, inventory) {
        const summary = calculateCoverageSummary(associations, inventory);
        const labels = {
            product: 'Products',
            category: 'Categories',
            article: 'Articles',
            other: 'Other entities'
        };

        const html = Object.keys(labels).map((group) => {
            const item = summary[group];
            const coverage = item.total > 0 ? Math.round((item.withKeywords / item.total) * 100) : 0;
            return `
                <div class="aysa-coverage-card">
                    <h4>${labels[group]}</h4>
                    <p class="aysa-coverage-meta">Coverage: <strong>${coverage}%</strong></p>
                    <p class="aysa-coverage-meta">Linked: ${item.withKeywords} / ${item.total}</p>
                    <p class="aysa-coverage-meta">Without keywords: ${item.withoutKeywords}</p>
                </div>
            `;
        }).join('');

        $('#aysa-coverage-summary').html(html);
    }

    function buildNoContentRowsFromInventory(inventory) {
        const rows = [];
        const seen = new Set();
        ['product', 'category', 'other'].forEach((group) => {
            const entities = Array.isArray(inventory && inventory[group]) ? inventory[group] : [];
            entities.forEach((entity) => {
                if (!entity || !entity.id) return;
                if (isSystemEntityForContentGen(entity)) return;
                const key = `${group}:${String(entity.id)}`;
                if (seen.has(key)) return;
                seen.add(key);

                // Business rule: "No Content" means content source is not AYSA-generated yet.
                const source = normalizeContentSource(entity && entity.content_source);
                const recentGeneratedAt = getRecentlyGeneratedTimestamp(key);
                if (source.key === 'aysa_generated' && !recentGeneratedAt) return;

                rows.push({
                    group: group,
                    id: entity.id,
                    name: entity.name || '(Untitled)',
                    source_type: entity.source_type || '',
                    edit_url: entity.edit_url || '',
                    url: entity.url || '',
                    slug: entity.slug || '',
                    excerpt: entity.excerpt || '',
                    content: entity.content || '',
                    categories: Array.isArray(entity.categories) ? entity.categories : [],
                    recently_generated: !!recentGeneratedAt,
                    recently_generated_at: recentGeneratedAt || 0
                });
            });
        });
        return rows;
    }

    function isSystemEntityForContentGen(entity) {
        if (!entity) return false;
        const slug = String(entity.slug || '').toLowerCase().trim();
        const title = String(entity.name || entity.title || '').toLowerCase().trim().replace(/\s+/g, ' ');
        const url = String(entity.url || '').toLowerCase().trim();
        const sourceType = String(entity.source_type || '').toLowerCase().trim();

        const blockedSlugs = new Set([
            'cart',
            'mycart',
            'checkout',
            'my-account',
            'myaccount',
            'wishlist',
            'order-pay',
            'order-received',
            'lost-password',
            'reset-password'
        ]);
        const blockedTitles = new Set([
            'cart',
            'cos',
            'checkout',
            'my account',
            'contul meu',
            'wishlist'
        ]);
        const blockedUrlParts = [
            '/cart',
            '/checkout',
            '/my-account',
            '/myaccount',
            '/wishlist',
            'add-to-cart=',
            '/order-pay/',
            '/order-received/',
            '/lost-password/',
            '/reset-password/'
        ];

        if (blockedSlugs.has(slug) || blockedTitles.has(title)) {
            return true;
        }
        if (blockedUrlParts.some((part) => part && url.includes(part))) {
            return true;
        }
        if (sourceType === 'other' && (!slug || !title)) {
            return true;
        }
        return false;
    }

    function renderUnlinkedEntitiesTable(associations, inventory) {
        const associatedKeys = new Set();
        (associations || []).forEach((association) => {
            const entityType = association.associatedOn || association.entityType || '';
            const entityId = association.associatedValue || association.idIntern || '';
            if (!entityId) return;
            associatedKeys.add(`${normalizeEntityGroup(entityType)}:${String(entityId)}`);
        });

        const rows = [];
        ['product', 'category', 'article', 'other'].forEach((group) => {
            const entities = Array.isArray(inventory[group]) ? inventory[group] : [];
            entities.forEach((entity) => {
                const key = `${group}:${String(entity.id)}`;
                if (associatedKeys.has(key)) return;
                rows.push({
                    group: group,
                    id: entity.id,
                    name: entity.name || '(Untitled)',
                    source_type: entity.source_type || '',
                    edit_url: entity.edit_url || '',
                    url: entity.url || '',
                    slug: entity.slug || '',
                    excerpt: entity.excerpt || '',
                    content: entity.content || '',
                    categories: Array.isArray(entity.categories) ? entity.categories : []
                });
            });
        });

        unlinkedRowsCache = rows;
        noContentRowsCache = buildNoContentRowsFromInventory(inventory);
        if (unlinkedBatchCursor >= unlinkedRowsCache.length) {
            unlinkedBatchCursor = 0;
        }
        if (unlinkedContentBatchCursor >= noContentRowsCache.length) {
            unlinkedContentBatchCursor = 0;
        }

        renderNoContentEntitiesTable(noContentRowsCache);

        if (!rows.length) {
            $('#aysa-unlinked-entities-table tbody').html(
                '<tr><td colspan="6" class="aysa-empty-cell">Great: all detected entities already have keyword associations.</td></tr>'
            );
            return;
        }

        const groupLabels = {
            product: 'Product',
            category: 'Category',
            article: 'Article',
            other: 'Other'
        };

        const buildUnlinkedEditUrl = function(row) {
            if (!row || !row.id) {
                return '#';
            }
            const existing = decodeHtmlEntities(String(row.edit_url || '').trim());
            if (isValidEditorUrl(existing)) {
                return existing;
            }

            const entityId = encodeURIComponent(String(row.id));
            const sourceType = String(row.source_type || '').toLowerCase();
            if (row.group === 'product') {
                return `${window.location.origin}/wp-admin/post.php?post=${entityId}&action=edit`;
            }
            if (row.group === 'article') {
                return `${window.location.origin}/wp-admin/post.php?post=${entityId}&action=edit`;
            }
            if (row.group === 'category') {
                if (sourceType === 'category') {
                    return `${window.location.origin}/wp-admin/term.php?taxonomy=category&tag_ID=${entityId}&post_type=post`;
                }
                return `${window.location.origin}/wp-admin/term.php?taxonomy=product_cat&tag_ID=${entityId}&post_type=product`;
            }
            return `${window.location.origin}/wp-admin/post.php?post=${entityId}&action=edit`;
        };

        const html = rows.map((row) => {
            const actionUrl = buildUnlinkedEditUrl(row);
            const actionLabel = 'Edit entity';
            const key = `${row.group}:${row.id}`;
            const state = unlinkedSuggestionMap[key] || null;
            const suggestionsHtml = renderUnlinkedSuggestionCell(state);
            const costHtml = renderUnlinkedCostCell(state);
            return `
                <tr data-unlinked-key="${escapeHtml(key)}">
                    <td>${groupLabels[row.group]}</td>
                    <td>${escapeHtml(row.name)}</td>
                    <td>${escapeHtml(row.id)}</td>
                    <td>${suggestionsHtml}</td>
                    <td>${costHtml}</td>
                    <td><button type="button" class="aysa-btn aysa-btn-small aysa-btn-secondary aysa-unlinked-edit-entity" data-edit-url="${escapeHtml(actionUrl)}">${actionLabel}</button></td>
                </tr>
            `;
        }).join('');

        $('#aysa-unlinked-entities-table tbody').html(html);
    }

    function renderNoContentEntitiesTable(rows) {
        const dataRows = Array.isArray(rows) ? rows : [];
        if (!dataRows.length) {
            $('#aysa-no-content-entities-table tbody').html(
                '<tr><td colspan="6" class="aysa-empty-cell">Great: all eligible entities already have AYSA-generated content.</td></tr>'
            );
            return;
        }

        const groupLabels = {
            product: 'Product',
            category: 'Category',
            article: 'Article',
            other: 'Other'
        };

        const buildEditUrl = function(row) {
            if (!row || !row.id) {
                return '#';
            }
            const existing = decodeHtmlEntities(String(row.edit_url || '').trim());
            if (isValidEditorUrl(existing)) {
                return existing;
            }
            const entityId = encodeURIComponent(String(row.id));
            const sourceType = String(row.source_type || '').toLowerCase();
            if (row.group === 'product') {
                return `${window.location.origin}/wp-admin/post.php?post=${entityId}&action=edit`;
            }
            if (row.group === 'article') {
                return `${window.location.origin}/wp-admin/post.php?post=${entityId}&action=edit`;
            }
            if (row.group === 'category') {
                if (sourceType === 'category') {
                    return `${window.location.origin}/wp-admin/term.php?taxonomy=category&tag_ID=${entityId}&post_type=post`;
                }
                return `${window.location.origin}/wp-admin/term.php?taxonomy=product_cat&tag_ID=${entityId}&post_type=product`;
            }
            if (sourceType === 'page' || sourceType === 'post') {
                return `${window.location.origin}/wp-admin/post.php?post=${entityId}&action=edit`;
            }
            return `${window.location.origin}/wp-admin/post.php?post=${entityId}&action=edit`;
        };

        const html = dataRows.map((row) => {
            const actionUrl = buildEditUrl(row);
            const key = `${row.group}:${row.id}`;
            const state = unlinkedSuggestionMap[key] || null;
            let suggestionsHtml = renderUnlinkedSuggestionCell(state);
            if (row && row.recently_generated && !state) {
                const fullKeywords = renderEntityKeywordsInline(row.group, row.id, '');
                suggestionsHtml = `${fullKeywords}<div class="aysa-suggestion-meta">Generated recently (kept visible for 1h)</div>`;
            } else if (!state) {
                const lists = getEntityKeywordsList(row.group, row.id);
                if (lists.all.length) {
                    suggestionsHtml = `${renderEntityKeywordsInline(row.group, row.id, '')}<div class="aysa-suggestion-meta">Existing keyword associations</div>`;
                }
            }
            const costHtml = renderUnlinkedCostCell(state);
            return `
                <tr data-unlinked-key="${escapeHtml(key)}">
                    <td>${groupLabels[row.group]}</td>
                    <td>${escapeHtml(row.name)}</td>
                    <td>${escapeHtml(row.id)}</td>
                    <td>${suggestionsHtml}</td>
                    <td>${costHtml}</td>
                    <td><button type="button" class="aysa-btn aysa-btn-small aysa-btn-secondary aysa-unlinked-edit-entity" data-edit-url="${escapeHtml(actionUrl)}">Edit entity</button></td>
                </tr>
            `;
        }).join('');

        $('#aysa-no-content-entities-table tbody').html(html);
    }

    function updateEntityTabCounts(associations, inventory) {
        const summary = calculateCoverageSummary(associations, inventory);
        const unlinkedTotal = ['product', 'category', 'article', 'other']
            .reduce((sum, key) => sum + (summary[key] ? summary[key].withoutKeywords : 0), 0);
        const noContentTotal = Array.isArray(noContentRowsCache) ? noContentRowsCache.length : 0;
        const linkedTotal = (associations || []).length;

        // Tab counts should reflect visible association rows, not unique entities.
        const associationCounts = {
            product: 0,
            category: 0,
            article: 0,
            other: 0
        };
        (associations || []).forEach((association) => {
            const group = normalizeEntityGroup(association.associatedOn || association.entityType || '');
            if (Object.prototype.hasOwnProperty.call(associationCounts, group)) {
                associationCounts[group] += 1;
            }
        });

        const counts = {
            all: linkedTotal,
            product: associationCounts.product,
            category: associationCounts.category,
            article: associationCounts.article,
            other: associationCounts.other,
            unlinked: unlinkedTotal,
            'no-content': noContentTotal
        };

        $('.aysa-entity-tab').each(function() {
            const $tab = $(this);
            const tab = $tab.data('entity-tab');
            const label = String($tab.text()).replace(/\s\(\d+\)$/, '');
            const count = counts[tab] || 0;
            $tab.text(`${label} (${count})`);
        });
    }

    function saveSuggestionSettings(projectId, settings) {
        return $.ajax({
            url: getSuggestionAjaxContext().ajaxUrl,
            type: 'POST',
            dataType: 'json',
            data: {
                action: 'aysa_keyword_suggestions_save_settings',
                nonce: getSuggestionAjaxContext().nonce,
                project_id: projectId,
                settings: JSON.stringify(settings || {})
            }
        });
    }

    function initializeUnlinkedKeywordSuggestions() {
        const $btn = $('#aysa-suggest-keywords-btn');
        const $contentBtn = $('#aysa-content-gen-btn');
        if (!$btn.length) {
            return;
        }

        const ctx = getSuggestionAjaxContext();
        if (!ctx.ajaxUrl || !ctx.nonce || !ctx.projectId) {
            updateSuggestionStatus('Missing project or auth context. Reload page.', 'error');
            return;
        }

        const $settingsInputs = $('#aysa-unlinked-max-keywords, #aysa-unlinked-min-words, #aysa-unlinked-max-words, #aysa-unlinked-min-score');
        let saveTimer = null;

        const loadSettings = function() {
            return $.ajax({
                url: ctx.ajaxUrl,
                type: 'POST',
                dataType: 'json',
                data: {
                    action: 'aysa_keyword_suggestions_get_settings',
                    nonce: ctx.nonce,
                    project_id: ctx.projectId
                }
            }).done(function(response) {
                if (response && response.success && response.data && response.data.settings) {
                    unlinkedSettingsCache = response.data.settings;
                    applySuggestionSettingsToInputs(unlinkedSettingsCache);
                }
                updateSuggestionStatus('Suggest: all No Keywords rows/click', 'info');
                updateContentGenStatus('Generate content for up to 3 eligible rows per run.', 'info');
            }).fail(function() {
                updateSuggestionStatus('Could not load suggestion settings, using defaults.', 'warn');
                updateContentGenStatus('Could not load content generation settings. Using safe defaults.', 'warn');
            });
        };

        $(document)
            .off('click.aysaUnlinkedEdit', '.aysa-unlinked-edit-entity')
            .on('click.aysaUnlinkedEdit', '.aysa-unlinked-edit-entity', function() {
                const url = decodeHtmlEntities(String($(this).data('edit-url') || '').trim());
                if (!isValidEditorUrl(url)) {
                    updateSuggestionStatus('Cannot open entity editor: missing edit URL.', 'error');
                    return;
                }
                window.location.href = url;
            });

        const queueSettingsSave = function() {
            if (saveTimer) {
                clearTimeout(saveTimer);
            }
            saveTimer = setTimeout(function() {
                const settings = readSuggestionSettingsFromInputs();
                unlinkedSettingsCache = Object.assign({}, unlinkedSettingsCache || {}, settings);
                saveSuggestionSettings(ctx.projectId, unlinkedSettingsCache).fail(function() {
                    updateSuggestionStatus('Settings were not saved to API (using local values).', 'warn');
                });
            }, 450);
        };

        const isEligibleForContentGen = function(row) {
            if (!row || String(row.group || '') === 'article') {
                return false;
            }
            if (String(row.group || '') === 'other' && String(row.source_type || '').toLowerCase() !== 'page') {
                return false;
            }
            const rowKey = `${normalizeEntityGroup(row.group || '')}:${String(row.id || '')}`;
            if (row && row.recently_generated) {
                return false;
            }
            if (getRecentlyGeneratedTimestamp(rowKey)) {
                return false;
            }
            return true;
        };

        const pickContentBatchRows = function() {
            if (!Array.isArray(noContentRowsCache) || !noContentRowsCache.length) {
                return [];
            }
            const total = noContentRowsCache.length;
            if (unlinkedContentBatchCursor >= total) {
                unlinkedContentBatchCursor = 0;
            }
            const picked = [];
            let visited = 0;
            while (visited < total && picked.length < 3) {
                const idx = (unlinkedContentBatchCursor + visited) % total;
                const row = noContentRowsCache[idx];
                visited += 1;
                if (!isEligibleForContentGen(row)) {
                    continue;
                }
                picked.push(row);
            }
            if (visited > 0) {
                unlinkedContentBatchCursor = (unlinkedContentBatchCursor + visited) % total;
            }
            return picked;
        };

        const runBatch = function() {
            if (!$btn.length || $btn.prop('disabled')) {
                return;
            }
            if (!Array.isArray(unlinkedRowsCache) || !unlinkedRowsCache.length) {
                updateSuggestionStatus('No entities in No Keywords list.', 'warn');
                return;
            }

            const allRows = unlinkedRowsCache.slice();
            if (!allRows.length) {
                updateSuggestionStatus('No entities available for processing.', 'warn');
                return;
            }

            unlinkedBatchCursor = 0;

            allRows.forEach((row) => {
                const rowKey = `${row.group}:${row.id}`;
                const previous = unlinkedSuggestionMap[rowKey] || {};
                unlinkedSuggestionMap[rowKey] = Object.assign({}, previous, {
                    status: 'processing',
                    error: ''
                });
                updateUnlinkedRowCells(rowKey);
            });

            const requestSettings = Object.assign({}, unlinkedSettingsCache || {}, readSuggestionSettingsFromInputs(), {
                billing_multiplier: 5
            });
            $btn.prop('disabled', true).text('Suggesting...');
            const chunkSize = 10;
            const chunks = [];
            for (let i = 0; i < allRows.length; i += chunkSize) {
                chunks.push(allRows.slice(i, i + chunkSize));
            }
            const overall = {
                updated: 0,
                saved: 0,
                billable: 0,
                failed_chunks: 0,
                fallback: false,
                fallback_reason: '',
                request_ids: []
            };
            const usedKeywords = new Set();
            const existingKeywordsSet = new Set();

            const processChunk = function(chunkIndex) {
                if (chunkIndex >= chunks.length) {
                    const sourceInfo = overall.fallback ? 'source: fallback' : 'source: broker';
                    const fallbackInfo = overall.fallback && overall.fallback_reason
                        ? ` | reason: ${String(overall.fallback_reason)}`
                        : '';
                    const failInfo = overall.failed_chunks > 0 ? ` | failed chunks: ${overall.failed_chunks}` : '';
                    updateSuggestionStatus(
                        `Done: ${overall.updated}/${allRows.length} rows | saved: ${overall.saved}/${Math.max(overall.updated, 1)} | ${sourceInfo}${fallbackInfo}${failInfo} | consumption: $${overall.billable.toFixed(6)}`,
                        overall.updated > 0 ? 'ok' : 'warn'
                    );
                    if (overall.saved > 0) {
                        invalidateAssociationsCache();
                        fetchAssociations();
                    }
                    $btn.prop('disabled', false).text('Suggest Keyword Ideas');
                    return;
                }

                const batchRows = chunks[chunkIndex];
                const from = chunkIndex * chunkSize + 1;
                const to = from + batchRows.length - 1;
                updateSuggestionStatus(
                    `Processing batch ${chunkIndex + 1}/${chunks.length} (${from}-${to} of ${allRows.length})...`,
                    'info'
                );

                const entities = batchRows.map((row) => ({
                    id: row.id,
                    type: row.group,
                    title: row.name,
                    slug: row.slug || '',
                    excerpt: row.excerpt || '',
                    content: row.content || '',
                    categories: Array.isArray(row.categories) ? row.categories : []
                }));

                $.ajax({
                    url: ctx.ajaxUrl,
                    type: 'POST',
                    dataType: 'json',
                    data: {
                        action: 'aysa_keyword_suggestions_batch',
                        nonce: ctx.nonce,
                        project_id: ctx.projectId,
                        entities: JSON.stringify(entities),
                        existing_keywords: JSON.stringify([]),
                        settings: JSON.stringify(requestSettings)
                    }
                }).done(function(response) {
                    if (!response || !response.success) {
                        const errText = response && response.data ? String(response.data) : 'Request failed';
                        batchRows.forEach((row) => {
                            const rowKey = `${row.group}:${row.id}`;
                            unlinkedSuggestionMap[rowKey] = {
                                status: 'error',
                                suggestions: [],
                                billable_cost_usd: null,
                                error: errText
                            };
                            updateUnlinkedRowCells(rowKey);
                        });
                        overall.failed_chunks += 1;
                        processChunk(chunkIndex + 1);
                        return;
                    }

                    const payload = response.data || null;
                    const results = payload && Array.isArray(payload.results) ? payload.results : [];
                    const isFallback = !!(payload && payload.fallback);
                    const sourceType = isFallback ? 'fallback' : 'broker';
                    let chunkUpdated = 0;
                    const savePromises = [];

                    if (payload && payload.totals) {
                        overall.billable += Number(payload.totals.billable_cost_usd || 0);
                    }
                    if (payload && payload.request_id) {
                        overall.request_ids.push(String(payload.request_id));
                    }
                    if (isFallback) {
                        overall.fallback = true;
                        if (payload && payload.fallback_reason) {
                            overall.fallback_reason = String(payload.fallback_reason);
                        }
                    }

                    results.forEach((result) => {
                        const rowKey = `${normalizeEntityGroup(result.entity_type || '')}:${String(result.entity_id || '')}`;
                        if (!rowKey || rowKey.indexOf(':') === -1) return;
                        const safeSuggestions = Array.isArray(result.suggestions) ? result.suggestions : [];
                        unlinkedSuggestionMap[rowKey] = {
                            status: 'ready',
                            suggestions: safeSuggestions,
                            billable_cost_usd: result.costs ? result.costs.billable_cost_usd : null,
                            ai_cost_usd: result.costs ? result.costs.ai_cost_usd : null,
                            model: result.costs ? result.costs.model : '',
                            request_id: payload.request_id || '',
                            source: sourceType
                        };
                        updateUnlinkedRowCells(rowKey);
                        if (safeSuggestions.length) {
                            const targetRow = batchRows.find((r) => `${r.group}:${r.id}` === rowKey);
                            if (targetRow) {
                                const prev = unlinkedSuggestionMap[rowKey] || {};
                                unlinkedSuggestionMap[rowKey] = Object.assign({}, prev, { saved: 'pending', save_error: '' });
                                updateUnlinkedRowCells(rowKey);
                                const savePromise = saveAssociationWithAlternatives(
                                    targetRow,
                                    safeSuggestions,
                                    ctx.projectId,
                                    usedKeywords,
                                    existingKeywordsSet
                                )
                                    .done(function(data) {
                                        const prevState = unlinkedSuggestionMap[rowKey] || {};
                                        const remaining = data && Array.isArray(data.remainingSuggestions)
                                            ? data.remainingSuggestions
                                            : (prevState.suggestions || []);
                                        unlinkedSuggestionMap[rowKey] = Object.assign({}, prevState, {
                                            saved: true,
                                            save_error: '',
                                            selected_keyword: data && data.keyword ? String(data.keyword) : '',
                                            suggestions: remaining
                                        });
                                        updateUnlinkedRowCells(rowKey);
                                        overall.saved += 1;
                                    })
                                    .fail(function(err) {
                                        const errText = typeof err === 'string'
                                            ? err
                                            : String(err && err.message ? err.message : 'upsert failed');
                                        const prevState = unlinkedSuggestionMap[rowKey] || {};
                                        const remaining = err && Array.isArray(err.remainingSuggestions)
                                            ? err.remainingSuggestions
                                            : (prevState.suggestions || []);
                                        unlinkedSuggestionMap[rowKey] = Object.assign({}, prevState, { saved: false, save_error: errText });
                                        unlinkedSuggestionMap[rowKey].suggestions = remaining;
                                        updateUnlinkedRowCells(rowKey);
                                    });
                                savePromises.push(savePromise);
                            }
                        }
                        chunkUpdated += 1;
                    });

                    batchRows.forEach((row) => {
                        const rowKey = `${row.group}:${row.id}`;
                        if (!unlinkedSuggestionMap[rowKey] || unlinkedSuggestionMap[rowKey].status === 'processing') {
                            unlinkedSuggestionMap[rowKey] = {
                                status: 'error',
                                suggestions: [],
                                billable_cost_usd: null,
                                error: 'No result returned for this row.'
                            };
                            updateUnlinkedRowCells(rowKey);
                        }
                    });

                    const nextChunk = function() {
                        overall.updated += chunkUpdated;
                        processChunk(chunkIndex + 1);
                    };

                    if (savePromises.length) {
                        $.when.apply($, savePromises).always(nextChunk);
                    } else {
                        nextChunk();
                    }
                }).fail(function(xhr) {
                    const errText = xhr && xhr.responseJSON && xhr.responseJSON.data ? String(xhr.responseJSON.data) : 'Request failed';
                    batchRows.forEach((row) => {
                        const rowKey = `${row.group}:${row.id}`;
                        unlinkedSuggestionMap[rowKey] = {
                            status: 'error',
                            suggestions: [],
                            billable_cost_usd: null,
                            error: errText
                        };
                        updateUnlinkedRowCells(rowKey);
                    });
                    overall.failed_chunks += 1;
                    processChunk(chunkIndex + 1);
                });
            };

            processChunk(0);
        };

        const applyContentGenResults = function(payload, batchRows) {
            const results = payload && Array.isArray(payload.results) ? payload.results : [];
            let updated = 0;
            let savedCount = 0;
            let reusedCount = 0;
            let generatedCount = 0;
            const savePromises = [];
            const usedKeywords = new Set();
            const existingKeywordsSet = new Set(
                getExistingKeywordsFromAssociations().map((k) => normalizeKeywordValue(k)).filter(Boolean)
            );

            results.forEach((result) => {
                const rowKey = `${normalizeEntityGroup(result.entity_type || '')}:${String(result.entity_id || '')}`;
                if (!rowKey || rowKey.indexOf(':') === -1) return;
                const safeSuggestions = Array.isArray(result.suggestions) ? result.suggestions : [];
                const resultMessage = String(result && result.message ? result.message : '').trim();
                const skippedByBackend = safeSuggestions.length === 0 && !result.content_generated && /^Skipped:/i.test(resultMessage);
                if (result.content_generated) {
                    generatedCount += 1;
                    markRecentlyGeneratedRow(rowKey);
                }
                unlinkedSuggestionMap[rowKey] = {
                    status: skippedByBackend ? 'error' : 'ready',
                    operation: 'content_gen',
                    suggestions: safeSuggestions,
                    billable_cost_usd: result.costs ? result.costs.billable_cost_usd : null,
                    ai_cost_usd: result.costs ? result.costs.ai_cost_usd : null,
                    model: result.costs ? result.costs.model : '',
                    request_id: payload && payload.job_id ? payload.job_id : '',
                    source: 'content_gen',
                    content_generated: !!result.content_generated,
                    error: skippedByBackend ? resultMessage : ''
                };
                updateUnlinkedRowCells(rowKey);

                if (safeSuggestions.length) {
                    let targetRow = batchRows.find((r) => `${r.group}:${r.id}` === rowKey);
                    if (!targetRow) {
                        const parts = String(rowKey).split(':');
                        const rowGroup = parts[0] || 'other';
                        const rowId = parseInt(parts[1] || '0', 10) || 0;
                        targetRow = {
                            group: rowGroup,
                            id: rowId,
                            source_type: rowGroup === 'other' ? 'page' : rowGroup
                        };
                    }
                    if (targetRow) {
                        const existingEntityKeywords = getEntityKeywordsForRow(targetRow);
                        if (existingEntityKeywords.length > 0) {
                            const prevState = unlinkedSuggestionMap[rowKey] || {};
                            unlinkedSuggestionMap[rowKey] = Object.assign({}, prevState, {
                                saved: true,
                                save_error: '',
                                selected_keyword: existingEntityKeywords[0],
                                save_note: 'existing keywords reused'
                            });
                            updateUnlinkedRowCells(rowKey);
                            reusedCount += 1;
                            updated += 1;
                            return;
                        }
                        const prev = unlinkedSuggestionMap[rowKey] || {};
                        unlinkedSuggestionMap[rowKey] = Object.assign({}, prev, { saved: 'pending', save_error: '' });
                        updateUnlinkedRowCells(rowKey);
                        const savePromise = saveAssociationWithAlternatives(
                            targetRow,
                            safeSuggestions,
                            ctx.projectId,
                            usedKeywords,
                            existingKeywordsSet
                        )
                            .done(function(data) {
                                const prevState = unlinkedSuggestionMap[rowKey] || {};
                                const remaining = data && Array.isArray(data.remainingSuggestions)
                                    ? data.remainingSuggestions
                                    : (prevState.suggestions || []);
                                unlinkedSuggestionMap[rowKey] = Object.assign({}, prevState, {
                                    saved: true,
                                    save_error: '',
                                    selected_keyword: data && data.keyword ? String(data.keyword) : '',
                                    suggestions: remaining
                                });
                                updateUnlinkedRowCells(rowKey);
                                savedCount += 1;
                            })
                            .fail(function(err) {
                                const errText = typeof err === 'string'
                                    ? err
                                    : String(err && err.message ? err.message : 'upsert failed');
                                const prevState = unlinkedSuggestionMap[rowKey] || {};
                                const remaining = err && Array.isArray(err.remainingSuggestions)
                                    ? err.remainingSuggestions
                                    : (prevState.suggestions || []);
                                unlinkedSuggestionMap[rowKey] = Object.assign({}, prevState, { saved: false, save_error: errText });
                                unlinkedSuggestionMap[rowKey].suggestions = remaining;
                                updateUnlinkedRowCells(rowKey);
                            });
                        savePromises.push(savePromise);
                    }
                }
                updated += 1;
            });

            const finalizeStatus = function() {
                const totalBillable = payload && payload.totals ? Number(payload.totals.billable_cost_usd || 0) : 0;
                const requestId = payload && payload.job_id ? ` | job: ${payload.job_id}` : '';
                const batchSize = (Array.isArray(batchRows) && batchRows.length > 0) ? batchRows.length : updated;
                const updatedSafe = updated > 0 ? updated : 1;
                updateContentGenStatus(
                    `Content generation completed: ${updated}/${batchSize} | generated: ${generatedCount}/${updatedSafe} | saved: ${savedCount}/${updatedSafe} | reused: ${reusedCount}${totalBillable > 0 ? ` | estimated usage tracked${requestId}` : ''}`,
                    updated > 0 ? 'ok' : 'warn'
                );
                if (generatedCount > 0 || savedCount > 0) {
                    invalidateAssociationsCache();
                    fetchAssociations();
                }
            };

            if (savePromises.length) {
                $.when.apply($, savePromises).always(finalizeStatus);
            } else {
                finalizeStatus();
            }
        };

        const pollContentGenQueue = function(batchRows, attemptsLeft) {
            if (attemptsLeft <= 0) {
                updateContentGenStatus('Content generation timed out while waiting for the background queue.', 'error');
                $contentBtn.prop('disabled', false).text('Generate Content');
                return;
            }
            $.ajax({
                url: ctx.ajaxUrl,
                type: 'POST',
                dataType: 'json',
                data: {
                    action: 'aysa_content_generation_status',
                    nonce: ctx.nonce,
                    project_id: ctx.projectId
                }
            }).done(function(response) {
                if (!response || !response.success || !response.data) {
                    updateContentGenStatus('Content generation status check failed.', 'error');
                    $contentBtn.prop('disabled', false).text('Generate Content');
                    return;
                }
                const statusPayload = response.data;
                const status = String(statusPayload.status || '').toLowerCase();
                const processed = parseInt(statusPayload.processed || 0, 10) || 0;
                const total = parseInt(statusPayload.total || batchRows.length, 10) || batchRows.length;
                const fallbackTick = !!statusPayload.fallback_tick;
                if (status === 'running') {
                    updateContentGenStatus(`Generating content in the background: ${processed}/${total} processed...${fallbackTick ? ' Working through the fallback worker as well.' : ''}`, 'info');
                    window.setTimeout(function() {
                        pollContentGenQueue(batchRows, attemptsLeft - 1);
                    }, 2000);
                    return;
                }
                if (status === 'failed') {
                    const lastError = String(statusPayload.last_error || 'Queue failed');
                    updateContentGenStatus(`Content generation failed: ${lastError}`, 'error');
                    $contentBtn.prop('disabled', false).text('Generate Content');
                    return;
                }
                applyContentGenResults(statusPayload, batchRows);
                $contentBtn.prop('disabled', false).text('Generate Content');
            }).fail(function(xhr) {
                const errText = xhr && xhr.responseJSON && xhr.responseJSON.data ? String(xhr.responseJSON.data) : 'Status request failed';
                updateContentGenStatus(`Content generation status failed: ${errText}`, 'error');
                $contentBtn.prop('disabled', false).text('Generate Content');
            });
        };

        const checkExistingContentGenJob = function() {
            $.ajax({
                url: ctx.ajaxUrl,
                type: 'POST',
                dataType: 'json',
                data: {
                    action: 'aysa_content_generation_status',
                    nonce: ctx.nonce,
                    project_id: ctx.projectId
                }
            }).done(function(response) {
                if (!response || !response.success || !response.data) {
                    return;
                }
                const payload = response.data;
                const status = String(payload.status || '').toLowerCase();
                const processed = parseInt(payload.processed || 0, 10) || 0;
                const total = parseInt(payload.total || 0, 10) || 0;
                if (status === 'running') {
                    $contentBtn.prop('disabled', true).text('Running...');
                    updateContentGenStatus(`Content generation queue resumed: ${processed}/${total} processed...`, 'info');
                    pollContentGenQueue([], 120);
                    return;
                }
                if (status === 'completed') {
                    const finishedAt = String(payload.finished_at || '').trim();
                    const generated = payload.totals ? parseInt(payload.totals.generated || 0, 10) || 0 : 0;
                    const billable = payload.totals ? Number(payload.totals.billable_cost_usd || 0) : 0;
                    updateContentGenStatus(
                        `Last content generation run completed: ${processed}/${total} processed | generated: ${generated}${billable > 0 ? ' | usage tracked' : ''}${finishedAt ? ` | finished: ${finishedAt}` : ''}`,
                        'ok'
                    );
                }
            });
        };

        const runContentGenBatch = function() {
            if (!$contentBtn.length || $contentBtn.prop('disabled')) {
                return;
            }
            if (!Array.isArray(noContentRowsCache) || !noContentRowsCache.length) {
                updateContentGenStatus('No content gaps are currently ready for generation.', 'warn');
                return;
            }

            const batchRows = pickContentBatchRows();
            if (!batchRows.length) {
                updateContentGenStatus('No eligible items are ready for content generation right now.', 'warn');
                return;
            }

            batchRows.forEach((row) => {
                const rowKey = `${row.group}:${row.id}`;
                const previous = unlinkedSuggestionMap[rowKey] || {};
                unlinkedSuggestionMap[rowKey] = Object.assign({}, previous, {
                    status: 'processing',
                    operation: 'content_gen',
                    content_generated: false,
                    error: ''
                });
                updateUnlinkedRowCells(rowKey);
            });

            const requestSettings = Object.assign({}, unlinkedSettingsCache || {}, readSuggestionSettingsFromInputs(), {
                billing_multiplier: 5
            });

            const entities = batchRows.map((row) => ({
                id: row.id,
                type: row.group,
                source_type: row.source_type || '',
                title: row.name,
                slug: row.slug || '',
                url: row.url || '',
                excerpt: row.excerpt || '',
                content: row.content || '',
                categories: Array.isArray(row.categories) ? row.categories : [],
                preferred_keywords: getEntityKeywordsForRow(row)
            }));

            $contentBtn.prop('disabled', true).text('Queued...');
            updateContentGenStatus(`Queued content generation for ${batchRows.length} items. Processing will continue automatically in the background.`, 'info');

            $.ajax({
                url: ctx.ajaxUrl,
                type: 'POST',
                dataType: 'json',
                data: {
                    action: 'aysa_content_generation_enqueue',
                    nonce: ctx.nonce,
                    project_id: ctx.projectId,
                    entities: JSON.stringify(entities),
                    existing_keywords: JSON.stringify(getExistingKeywordsFromAssociations()),
                    settings: JSON.stringify(requestSettings)
                }
            }).done(function(response) {
                if (!response || !response.success) {
                    const errText = response && response.data ? String(response.data) : 'Request failed';
                    batchRows.forEach((row) => {
                        const rowKey = `${row.group}:${row.id}`;
                        unlinkedSuggestionMap[rowKey] = {
                            status: 'error',
                            operation: 'content_gen',
                            suggestions: [],
                            billable_cost_usd: null,
                            error: errText
                        };
                        updateUnlinkedRowCells(rowKey);
                    });
                    updateContentGenStatus(`Content generation failed: ${errText}`, 'error');
                    $contentBtn.prop('disabled', false).text('Generate Content');
                    return;
                }

                pollContentGenQueue(batchRows, 90);
            }).fail(function(xhr) {
                const errText = xhr && xhr.responseJSON && xhr.responseJSON.data ? String(xhr.responseJSON.data) : 'Request failed';
                batchRows.forEach((row) => {
                    const rowKey = `${row.group}:${row.id}`;
                    unlinkedSuggestionMap[rowKey] = {
                        status: 'error',
                        operation: 'content_gen',
                        suggestions: [],
                        billable_cost_usd: null,
                        error: errText
                    };
                    updateUnlinkedRowCells(rowKey);
                });
                updateContentGenStatus(`Content generation failed: ${errText}`, 'error');
                $contentBtn.prop('disabled', false).text('Generate Content');
            });
        };

        $btn.off('click.aysaSuggest').on('click.aysaSuggest', runBatch);
        $contentBtn.off('click.aysaContentGen').on('click.aysaContentGen', runContentGenBatch);
        $settingsInputs.off('input.aysaSuggest change.aysaSuggest').on('input.aysaSuggest change.aysaSuggest', queueSettingsSave);
        loadSettings().always(function() {
            checkExistingContentGenJob();
        });
    }

    // Function to display associations in the table
    function displayAssociations(associations) {
        entityKeywordMap = buildEntityKeywordMap(associations);
        const uniqueAssociations = getUniqueAssociationsByEntity(associations);

        if (!associations || associations.length === 0 || uniqueAssociations.length === 0) {
            // Check if this is first load - try to fetch from API if cache is empty
            const cachedData = getCachedAssociations();
            if (!cachedData) {
                $('#aysa-associations-table tbody').html(`
                    <tr>
                        <td colspan="6" class="aysa-empty-cell">
                            No associations found. <a href="#" onclick="fetchAssociations(); return false;">Refresh</a> or create associations to optimize your content.
                        </td>
                    </tr>
                `);
            } else {
                $('#aysa-associations-table tbody').html(`
                    <tr>
                        <td colspan="6" class="aysa-empty-cell">
                            No associations found. Create associations to optimize your content.
                        </td>
                    </tr>
                `);
            }
            $('#aysa-coverage-summary').html('');
            $('#aysa-unlinked-entities-table tbody').html('<tr><td colspan="6" class="aysa-empty-cell">No entities to show.</td></tr>');
            $('#aysa-no-content-entities-table tbody').html('<tr><td colspan="6" class="aysa-empty-cell">No entities to show.</td></tr>');
            return;
        }
        
        let tableContent = '';
        
        uniqueAssociations.forEach(function(association) {
            // Status is SEO workflow state (stored), not live-computed entity score.
            const storedStatus = getStoredStatus(association.id);
            let statusClass = 'warning';
            let statusText = 'Not optimized yet';
            
            if (storedStatus) {
                // Use stored status
                switch(storedStatus) {
                    case 'processed':
                        statusClass = 'good';
                        statusText = 'Optimized';
                        break;
                    case 'poor':
                        statusClass = 'bad';
                        statusText = 'Needs work';
                        break;
                    default:
                        statusClass = 'warning';
                        statusText = 'Not optimized yet';
                }
            }
            
            // Ensure we have id values, handling different API response formats
            const id = association.id || '';
            const entityType = association.associatedOn || association.entityType || '';
            const entityId = association.associatedValue || association.idIntern || '';
            const entityGroup = normalizeEntityGroup(entityType);
            
            if (window.AYSA_DEBUG === true) {
                console.log('Row data:', {
                    id: id,
                    entityType: entityType,
                    entityId: entityId,
                    keyword: association.keyword
                });
            }
            const intentBadge = normalizeIntentBadge(association.intent || association.searchIntent || association.search_intent || '');
            const sourceBadge = normalizeContentSource(association.content_source || association.contentSource || '');
            const scoreRaw = parseInt(association.seo_score ?? association.seoScore, 10);
            const scoreLabel = Number.isFinite(scoreRaw) ? `${scoreRaw}/100` : 'N/A';
            const mainKeyword = association.keyword || '';

            // Create row with initial non-dummy values
            const rowId = `aysa-row-${id}`;
            tableContent += `
                <tr id="${rowId}" data-id="${id}" data-status="${statusText.toLowerCase().replace(/\s+/g, '-')}" data-entity-group="${entityGroup}" data-entity-type="${entityType}" data-main-keyword="${$('<div>').text(mainKeyword).html()}">
                    <td>${entityType}</td>
                    <td class="aysa-entity-cell" data-entity-type="${entityType}" data-entity-id="${entityId}">
                        <span class="aysa-status warning">Resolving entity...</span>
                    </td>
                    <td class="aysa-main-keyword-cell">${renderEntityKeywordsInline(entityType, entityId, mainKeyword)}</td>
                    <td class="aysa-entity-keywords">${renderEntityKeywordsChips(entityType, entityId)}</td>
                    <td class="aysa-optimized-status-cell"><span class="aysa-status ${statusClass}" data-status="${statusText.toLowerCase().replace(/\s+/g, '-')}">${statusText}</span><div class="aysa-optimized-meta"><span class="aysa-seo-score-pill">${scoreLabel}</span></div></td>
                    <td>
                        <div class="aysa-action-buttons">
                            <button type="button" class="aysa-btn aysa-btn-small aysa-btn-secondary aysa-navigate-to-page" 
                                    data-id="${id}" 
                                    data-entity-type="${entityType}" 
                                    data-entity-id="${entityId}"
                                    data-edit-url=""
                                    title="Edit this ${entityType.toLowerCase()}">
                                Edit
                            </button>
                            <button type="button" class="aysa-btn aysa-btn-small aysa-btn-primary aysa-btn-generate" title="Optimize this page" data-id="${id}">
                                Optimize
                            </button>
                        </div>
                    </td>
                </tr>
            `;
        });
        
        $('#aysa-associations-table tbody').html(tableContent);
        
        // Load entity details for each row
        loadEntityDetails();
        updateEntityCoverageAndUnlinked(associations);
        if (typeof window.applyFilters === 'function') {
            window.applyFilters();
        }
    }
    
    // Function to load entity details and update the Associated Element column
    function loadEntityDetails() {
        $('.aysa-entity-cell').each(function() {
            const $cell = $(this);
            const $row = $cell.closest('tr');
            const associationId = parseInt($row.attr('data-id'), 10) || 0;
            const entityType = $cell.data('entity-type');
            const entityId = $cell.data('entity-id');
            
            if (!entityType || !entityId) {
                $cell.html('<span class="aysa-error">Invalid entity</span>');
                markAssociationRowOrphan($row, associationId, entityId || 'unknown');
                return;
            }
            
            // Make AJAX request to get entity details
            $.ajax({
                url: aysa_params.ajax_url,
                type: 'POST',
                data: {
                    action: 'aysa_get_entity_details',
                    nonce: aysa_params.nonce,
                    entity_type: entityType,
                    entity_id: entityId
                },
                success: function(response) {
                    if (response.success && response.data) {
                        const entityData = response.data;
                        const $row = $cell.closest('tr');
                        const associationId = parseInt($row.attr('data-id'), 10) || 0;
                        let linkHtml = '';
                        
                        if (entityData.name && entityData.url) {
                            linkHtml = `<a href="${entityData.url}" target="_blank" title="View ${entityData.name}">${entityData.name}</a>`;
                            
                            // Add edit link if available
                            if (entityData.edit_url) {
                                linkHtml += ` <a href="${entityData.edit_url}" target="_blank" class="aysa-edit-link" title="Edit ${entityData.name}">
                                    <span class="dashicons dashicons-edit"></span>
                                </a>`;
                            }
                        } else {
                            linkHtml = `<span class="aysa-error">Entity not found (ID: ${entityId})</span>`;
                            markAssociationRowOrphan($row, associationId, entityId);
                        }
                        
                        $cell.html(linkHtml);
                        const $urlCell = $row.find('.aysa-entity-url-cell');
                        if (entityData.url) {
                            $urlCell.html(`<a href="${entityData.url}" target="_blank" title="${entityData.url}">${entityData.url}</a>`);
                        } else {
                            $urlCell.html('<span class="aysa-error">URL unavailable</span>');
                        }
                        const $goBtn = $row.find('.aysa-navigate-to-page').first();
                        if ($goBtn.length) {
                            if (entityData.edit_url) {
                                $goBtn.attr('data-edit-url', entityData.edit_url);
                            } else {
                                $goBtn.attr('data-edit-url', '');
                            }
                        }
                        if (entityData.name && entityData.url) {
                            const association = (window.aysaAssociations || []).find((item) => parseInt(item.id, 10) === associationId) || {};
                            const storedStatus = getStoredStatus(associationId);
                            const audit = buildAssociationAudit(association, entityData, entityType, false);
                            applyAssociationAuditToRow($row, audit);
                            applyEntitySeoSnapshotToRow($row, entityData, storedStatus);
                            $row.attr('data-orphan', '0');
                        }
                    } else {
                        $cell.html(`<span class="aysa-error">Error loading entity (ID: ${entityId})</span>`);
                        $row.find('.aysa-entity-url-cell').html('<span class="aysa-error">URL unavailable</span>');
                        markAssociationRowOrphan($row, associationId, entityId);
                    }
                },
                error: function() {
                    $cell.html(`<span class="aysa-error">Failed to load entity (ID: ${entityId})</span>`);
                    $row.find('.aysa-entity-url-cell').html('<span class="aysa-error">URL unavailable</span>');
                    markAssociationRowOrphan($row, associationId, entityId);
                }
            });
        });
    }
    
    // Function to get class for internal links
    function getInternalLinksClass(count) {
        if (count >= 10) return 'good';
        if (count >= 5) return 'warning';
        return 'bad';
    }
    
    // Function to get class for external links
    function getExternalLinksClass(count) {
        if (count >= 7) return 'good';
        if (count >= 3) return 'warning';
        return 'bad';
    }
    
    // Function to get class for social mentions
    function getSocialMentionsClass(count) {
        if (count >= 20) return 'good';
        if (count >= 10) return 'warning';
        return 'bad';
    }
    
    // Function to get class for bounce rate
    function getBounceRateClass(rate) {
        if (rate < 40) return 'good';
        if (rate < 60) return 'warning';
        return 'bad';
    }
    
    // Function to get class for CTR
    function getCTRClass(rate) {
        if (rate > 4.0) return 'good';
        if (rate > 2.0) return 'warning';
        return 'bad';
    }
    
    // Function to open association modal
    function openAssociationModal(associationId) {
        // If modal is already open, close it first
        if ($('#aysa-association-modal').is(':visible')) {
            closeModal();
        }
        
        // Get association details
        requestAssociationThroughWordPress('get', associationId)
            .done(function(association) {
                openDetailsModal(association);
            })
            .fail(function(error) {
                console.error('Error fetching association details:', error);
                showNotificationModal('Error', 'Error loading association details. Please try again.', 'error');
            });
    }
    
    // Function to open details modal
    function openDetailsModal(association) {
        
        
        // Reset modal to original state with tabs
        resetModalToDefault();
        
        // Populate with data
        populateModalData(association);
        $('#aysa-association-modal').data('association-id', association.id);
        $('#aysa-association-modal').data('mode', 'details');
        $('#aysa-association-modal').show();
    }
    
    // Function to reset modal to its default state
    function resetModalToDefault() {
        const defaultModalContent = `
            <div class="aysa-modal-content">
                <div class="aysa-modal-header">
                    <h2 class="aysa-modal-title">Association Details</h2>
                    <span class="aysa-modal-close">&times;</span>
                </div>
                <div class="aysa-modal-body">
                    <!-- Tabs for different optimization sections -->
                    <div class="aysa-modal-tabs">
                        <button class="aysa-modal-tab active" data-tab="title-tags">Title Tags</button>
                        <button class="aysa-modal-tab" data-tab="meta-descriptions">Meta Descriptions</button>
                        <button class="aysa-modal-tab" data-tab="content-optimization">Content Optimization</button>
                        <button class="aysa-modal-tab" data-tab="links-social">Links & Social</button>
                        <button class="aysa-modal-tab" data-tab="edit-association">Edit Association</button>
                    </div>
                    
                    <!-- Title Tags Tab Content -->
                    <div class="aysa-modal-tab-content active" id="title-tags-content">
                        <h3>Title Tags Optimization</h3>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Current Title:</div>
                            <div class="aysa-modal-info-value" id="current-title"></div>
                        </div>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Length:</div>
                            <div class="aysa-modal-info-value" id="title-length"></div>
                        </div>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Status:</div>
                            <div class="aysa-modal-info-value" id="title-status"></div>
                        </div>
                        <div class="aysa-modal-edit-section">
                            <h4>Suggested Optimization</h4>
                            <textarea id="title-suggestion" class="aysa-modal-textarea"></textarea>
                            <button class="aysa-btn aysa-btn-primary aysa-modal-save" data-section="title">Save Changes</button>
                        </div>
                    </div>
                    
                    <!-- Meta Descriptions Tab Content -->
                    <div class="aysa-modal-tab-content" id="meta-descriptions-content">
                        <h3>Meta Descriptions Optimization</h3>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Current Description:</div>
                            <div class="aysa-modal-info-value" id="current-description"></div>
                        </div>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Length:</div>
                            <div class="aysa-modal-info-value" id="description-length"></div>
                        </div>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Status:</div>
                            <div class="aysa-modal-info-value" id="description-status"></div>
                        </div>
                        <div class="aysa-modal-edit-section">
                            <h4>Suggested Optimization</h4>
                            <textarea id="description-suggestion" class="aysa-modal-textarea"></textarea>
                            <button class="aysa-btn aysa-btn-primary aysa-modal-save" data-section="description">Save Changes</button>
                        </div>
                    </div>
                    
                    <!-- Content Optimization Tab Content -->
                    <div class="aysa-modal-tab-content" id="content-optimization-content">
                        <h3>Content Optimization</h3>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Word Count:</div>
                            <div class="aysa-modal-info-value" id="word-count"></div>
                        </div>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Keyword Density:</div>
                            <div class="aysa-modal-info-value" id="keyword-density"></div>
                        </div>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Status:</div>
                            <div class="aysa-modal-info-value" id="content-status"></div>
                        </div>
                        <div class="aysa-modal-edit-section">
                            <h4>Content Improvement Suggestions</h4>
                            <ul id="content-suggestions" class="aysa-modal-suggestions-list"></ul>
                        </div>
                    </div>
                    
                    <!-- Links & Social Tab Content -->
                    <div class="aysa-modal-tab-content" id="links-social-content">
                        <h3>Links & Social Metrics</h3>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Internal Links:</div>
                            <div class="aysa-modal-info-value" id="internal-links-count"></div>
                        </div>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">External Links (Backlinks):</div>
                            <div class="aysa-modal-info-value" id="external-links-count"></div>
                        </div>
                        <div class="aysa-modal-info-row">
                            <div class="aysa-modal-info-label">Social Mentions:</div>
                            <div class="aysa-modal-info-value" id="social-mentions-count"></div>
                        </div>
                        <div class="aysa-modal-edit-section">
                            <h4>Links Assessment</h4>
                            <div id="links-analysis" class="aysa-modal-analysis">
                                <p>Internal links analysis will be displayed here based on the current metrics.</p>
                                <p>External links (backlinks) overview and opportunities will be shown here.</p>
                                <p>Social mentions breakdown by platform will be provided here.</p>
                            </div>
                        </div>
                    </div>
                    
                    <!-- Edit Association Tab Content -->
                    <div class="aysa-modal-tab-content" id="edit-association-content">
                        <h3>Edit Association Details</h3>
                        <div class="aysa-edit-form">
                            <div class="aysa-form-group">
                                <label for="edit-keyword">Keyword:</label>
                                <input type="text" id="edit-keyword" class="aysa-form-control">
                            </div>
                            <div class="aysa-form-group">
                                <label for="edit-associated-on">Associated On:</label>
                                <select id="edit-associated-on" class="aysa-form-control">
                                    <option value="Product">Product</option>
                                    <option value="Category">Category</option>
                                    <option value="Page">Page</option>
                                </select>
                            </div>
                            <div class="aysa-form-group">
                                <label for="edit-associated-value">Associated Value:</label>
                                <input type="text" id="edit-associated-value" class="aysa-form-control">
                            </div>
                            <div class="aysa-form-group">
                                <label for="edit-meta-title">Meta Title:</label>
                                <input type="text" id="edit-meta-title" class="aysa-form-control">
                                <div class="aysa-character-count">0/60</div>
                            </div>
                            <div class="aysa-form-group">
                                <label for="edit-meta-description">Meta Description:</label>
                                <textarea id="edit-meta-description" class="aysa-form-control" rows="3"></textarea>
                                <div class="aysa-character-count">0/160</div>
                            </div>
                            <button class="aysa-btn aysa-btn-primary" id="save-association-btn">Save Changes</button>
                        </div>
                    </div>
                </div>
                <div class="aysa-modal-footer">
                    <button class="aysa-btn aysa-btn-secondary" id="aysa-modal-close-btn">Close</button>
                </div>
            </div>
        `;
        
        // Replace the modal content with the default
        $('#aysa-association-modal').html(defaultModalContent);
    }
    
    // Function to populate modal data
    function populateModalData(association) {
        // Title Tab
        $('#current-title').text(association.metaTitle || 'No title available');
        $('#title-length').text(association.metaTitle ? association.metaTitle.length : 0);
        $('#title-status').html(`<span class="aysa-status ${getTitleStatusClass(association)}">${getTitleStatusText(association)}</span>`);
        $('#title-suggestion').val(association.titleSuggestion || '');
        
        // Description Tab
        $('#current-description').text(association.metaDescription || 'No description available');
        $('#description-length').text(association.metaDescription ? association.metaDescription.length : 0);
        $('#description-status').html(`<span class="aysa-status ${getDescriptionStatusClass(association)}">${getDescriptionStatusText(association)}</span>`);
        $('#description-suggestion').val(association.descriptionSuggestion || '');
        
        // Content Tab - real values only (no synthetic fallback)
        const wordCountRaw = parseInt(association.wordCount, 10);
        const densityRaw = parseFloat(association.keywordDensity);
        const hasWordCount = Number.isFinite(wordCountRaw) && wordCountRaw > 0;
        const hasDensity = Number.isFinite(densityRaw) && densityRaw >= 0;
        const wordCount = hasWordCount ? wordCountRaw : null;
        const keywordDensity = hasDensity ? densityRaw.toFixed(1) : null;
        
        $('#word-count').text(wordCount === null ? 'N/A' : (wordCount + ' words'));
        $('#keyword-density').text(keywordDensity === null ? 'N/A' : (keywordDensity + '%'));
        $('#content-status').html(
            wordCount === null
                ? '<span class="aysa-status warning">No Data</span>'
                : `<span class="aysa-status ${getContentStatusClass({wordCount: wordCount})}">${getContentStatusText({wordCount: wordCount})}</span>`
        );
        
        // Content suggestions
        let suggestionsHtml = '';
        if (association.contentSuggestions && association.contentSuggestions.length) {
            association.contentSuggestions.forEach(function(suggestion) {
                suggestionsHtml += `<li>${suggestion}</li>`;
            });
        } else {
            suggestionsHtml = '<li>No content suggestions available from real data yet.</li>';
        }
        $('#content-suggestions').html(suggestionsHtml);

        // Links & Social Tab
        // Real metrics only (no generated counts)
        const internalLinks = Number.isFinite(parseInt(association.internalLinks, 10)) ? parseInt(association.internalLinks, 10) : null;
        const externalLinks = Number.isFinite(parseInt(association.externalLinks, 10)) ? parseInt(association.externalLinks, 10) : null;
        const socialMentions = Number.isFinite(parseInt(association.socialMentions, 10)) ? parseInt(association.socialMentions, 10) : null;
        
        $('#internal-links-count').html(
            internalLinks === null ? '<span class="aysa-status warning">N/A</span>' : `<span class="aysa-status ${getInternalLinksClass(internalLinks)}">${internalLinks}</span>`
        );
        $('#external-links-count').html(
            externalLinks === null ? '<span class="aysa-status warning">N/A</span>' : `<span class="aysa-status ${getExternalLinksClass(externalLinks)}">${externalLinks}</span>`
        );
        $('#social-mentions-count').html(
            socialMentions === null ? '<span class="aysa-status warning">N/A</span>' : `<span class="aysa-status ${getSocialMentionsClass(socialMentions)}">${socialMentions}</span>`
        );
        
        if (internalLinks === null && externalLinks === null && socialMentions === null) {
            $('#links-analysis').html('<div class="aysa-links-analysis"><p>No link/social analytics available from real data.</p></div>');
        } else {
            $('#links-analysis').html(generateLinksAnalysis(association, internalLinks || 0, externalLinks || 0, socialMentions || 0));
        }

        // Edit Association Tab
        $('#edit-keyword').val(association.keyword || '');
        $('#edit-associated-on').val(association.associatedOn || 'Product');
        $('#edit-associated-value').val(association.associatedValue || '');
        $('#edit-meta-title').val(association.metaTitle || '');
        $('#edit-meta-description').val(association.metaDescription || '');
        
        // Update character counts
        const titleLength = association.metaTitle ? association.metaTitle.length : 0;
        const descLength = association.metaDescription ? association.metaDescription.length : 0;
        $('#edit-meta-title').next('.aysa-character-count').text(`${titleLength}/60`);
        $('#edit-meta-description').next('.aysa-character-count').text(`${descLength}/160`);
        
        // Add character counter functionality for edit inputs
        $('#edit-meta-title, #edit-meta-description').off('input').on('input', function() {
            const max = $(this).attr('id') === 'edit-meta-title' ? 60 : 160;
            const current = $(this).val().length;
            $(this).next('.aysa-character-count').text(`${current}/${max}`);
            
            // Add visual indicator for character count
            if (current > max) {
                $(this).next('.aysa-character-count').addClass('aysa-character-count-exceeded');
            } else {
                $(this).next('.aysa-character-count').removeClass('aysa-character-count-exceeded');
            }
        });
        
    }
    
    // Function to generate links and social analysis HTML
    function generateLinksAnalysis(association, internalLinks, externalLinks, socialMentions) {
        let html = '<div class="aysa-links-analysis">';
        
        // Internal links analysis
        html += '<div class="aysa-links-section">';
        html += '<h5>Internal Links Analysis</h5>';
        
        if (internalLinks >= 10) {
            html += '<p class="aysa-good-text">✅ Good: Your content has a healthy number of internal links.</p>';
        } else if (internalLinks >= 5) {
            html += '<p class="aysa-warning-text">⚠️ Moderate: Consider adding more internal links to strengthen your site structure.</p>';
        } else {
            html += '<p class="aysa-bad-text">❌ Poor: Your content needs more internal links. Add links to related content on your site.</p>';
        }
        
        html += '<p>Suggested action: ';
        if (internalLinks < 10) {
            html += `Link this ${entityType.toLowerCase()} to other relevant content about "${keyword}" on your site.`;
        } else {
            html += 'Maintain the current internal linking structure and ensure links are relevant.';
        }
        html += '</p>';
        html += '</div>';
        
        // External links analysis
        html += '<div class="aysa-links-section">';
        html += '<h5>External Links (Backlinks) Analysis</h5>';
        
        if (externalLinks >= 7) {
            html += '<p class="aysa-good-text">✅ Good: Your content has a strong backlink profile.</p>';
        } else if (externalLinks >= 3) {
            html += '<p class="aysa-warning-text">⚠️ Moderate: Your backlink profile could be improved.</p>';
        } else {
            html += '<p class="aysa-bad-text">❌ Poor: Your content has very few backlinks. Focus on building more.</p>';
        }
        html += '<p>Detailed backlink source data is not available in this view yet.</p>';
        html += '</div>';
        
        // Social mentions analysis
        html += '<div class="aysa-links-section">';
        html += '<h5>Social Mentions Analysis</h5>';
        
        if (socialMentions >= 20) {
            html += '<p class="aysa-good-text">✅ Good: Your content has significant social media presence.</p>';
        } else if (socialMentions >= 10) {
            html += '<p class="aysa-warning-text">⚠️ Moderate: Your content has some social media visibility but could improve.</p>';
        } else {
            html += '<p class="aysa-bad-text">❌ Poor: Your content lacks social media mentions. Consider promoting it more.</p>';
        }
        html += '<p>Detailed platform-level mention data is not available in this view yet.</p>';
        html += '</div>';
        
        html += '</div>';
        return html;
    }
    
    // Helper functions for status classes and text
    function getTitleStatusClass(association) {
        return getStatusClassByLength(association.metaTitle ? association.metaTitle.length : 0, 50, 60);
    }
    
    function getDescriptionStatusClass(association) {
        return getStatusClassByLength(association.metaDescription ? association.metaDescription.length : 0, 150, 160);
    }
    
    function getContentStatusClass(association) {
        if (!association.wordCount) return 'warning';
        return association.wordCount > 300 ? 'good' : 'warning';
    }
    
    function getStatusClassByLength(length, minOptimal, maxOptimal) {
        if (length >= minOptimal && length <= maxOptimal) return 'good';
        if (length > 0 && (length < minOptimal || length > maxOptimal)) return 'warning';
        return 'bad';
    }
    
    function getTitleStatusText(association) {
        const length = association.metaTitle ? association.metaTitle.length : 0;
        if (length >= 50 && length <= 60) return 'Good';
        if (length > 0 && length < 50) return 'Too Short';
        if (length > 60) return 'Too Long';
        return 'Missing';
    }
    
    function getDescriptionStatusText(association) {
        const length = association.metaDescription ? association.metaDescription.length : 0;
        if (length >= 150 && length <= 160) return 'Good';
        if (length > 0 && length < 150) return 'Too Short';
        if (length > 160) return 'Too Long';
        return 'Missing';
    }
    
    function getContentStatusText(association) {
        if (!association.wordCount) return 'No Data';
        return association.wordCount > 300 ? 'Good' : 'Needs More Content';
    }
    
    // Function to close modal
    function closeModal() {
        const modal = $('#aysa-association-modal');
        modal.hide();
        
        // Reset the save in progress flag
        isSaveInProgress = false;
        
        // Clear the modal content to ensure a fresh start next time
        setTimeout(function() {
            resetModalToDefault();
        }, 300); // Small delay to ensure the hide animation completes
    }
    
    // Function to save changes
    function saveChanges(section, associationId) {
        if (isSaveInProgress) return;
        isSaveInProgress = true;
        
        
        
        let updateData = {};
        let $saveBtn = $('.aysa-modal-save[data-section="' + section + '"]');
        let originalText = $saveBtn.text();
        
        // Show loading state
        $saveBtn.text('Saving...').prop('disabled', true);
        
        switch(section) {
            case 'title':
                updateData.metaTitle = $('#title-suggestion').val();
                break;
            case 'description':
                updateData.metaDescription = $('#description-suggestion').val();
                break;
            case 'content':
                // Handle content optimization if needed
                break;
        }
        
        // Get project ID from localStorage
        const projectId = localStorage.getItem('aysa_project_id');
        
        // Get the full association data first
        requestAssociationThroughWordPress('get', associationId)
            .done(function(association) {
                // Merge the existing data with update data
                const updatedAssociation = { ...association, ...updateData };
                
                // Add projectId if available and not already present
                if (projectId && !updatedAssociation.projectId) {
                    updatedAssociation.projectId = parseInt(projectId);
                }
                
                
                
                // Update via the upsert endpoint
                requestAssociationThroughWordPress('upsert', associationId, updatedAssociation)
                    .done(function(response) {
                        
                        
                        // Show success notification
                        showNotificationModal('Success', 'Changes saved successfully!', 'success');
                        
                        // Reset button state
                        $saveBtn.text(originalText).prop('disabled', false);
                        
                        // Refresh the table data
                        fetchAssociations();
                        
                        // Update the modal data
                        populateModalData(response);
                        
                        // Reset save in progress flag
                        isSaveInProgress = false;
                    })
                    .fail(function(error) {
                        console.error('Error saving changes:', error);
                        showNotificationModal('Error', 'Error saving changes. Please try again.', 'error');
                        $saveBtn.text(originalText).prop('disabled', false);
                        isSaveInProgress = false;
                    });
            })
            .fail(function(error) {
                console.error('Error fetching association details:', error);
                showNotificationModal('Error', 'Error loading association details. Please try again.', 'error');
                $saveBtn.text(originalText).prop('disabled', false);
                isSaveInProgress = false;
            });
    }
    
    // Function to show a notification modal
    function showNotificationModal(title, message, type) {
        // Hide any existing modal first
        const existingModal = $('#aysa-notification-modal');
        if (existingModal.length) {
            existingModal.remove();
        }
        
        // Create modal HTML
        const modalHTML = `
            <div id="aysa-notification-modal" class="aysa-modal">
                <div class="aysa-modal-content aysa-notification-modal-content">
                    <div class="aysa-modal-header">
                        <h2 class="aysa-modal-title">${title}</h2>
                        <span class="aysa-modal-close">&times;</span>
                    </div>
                    <div class="aysa-modal-body">
                        <div class="aysa-notification-message ${type}">
                            <i class="aysa-icon ${type === 'success' ? 'aysa-icon-success' : 'aysa-icon-error'}"></i>
                            <p>${message}</p>
                        </div>
                    </div>
                    <div class="aysa-modal-footer">
                        <button class="aysa-btn aysa-btn-primary" id="aysa-notification-close-btn">OK</button>
                    </div>
                </div>
            </div>
        `;
        
        // Add modal to body
        $('body').append(modalHTML);
        
        // Show modal
        $('#aysa-notification-modal').show();
        
        // Add event listeners to close modal
        $('.aysa-modal-close, #aysa-notification-close-btn').on('click', function() {
            $('#aysa-notification-modal').hide().remove();
        });
        
        // Close on click outside
        $(window).on('click', function(event) {
            if ($(event.target).is('#aysa-notification-modal')) {
                $('#aysa-notification-modal').hide().remove();
            }
        });
    }
    
    // Function to save association edits
    function saveAssociationEdits(associationId) {
        if (isSaveInProgress) return;
        
        
        
        // Show loading state
        const $saveBtn = $('#save-association-btn');
        const originalText = $saveBtn.text();
        $saveBtn.text('Saving...').prop('disabled', true);
        isSaveInProgress = true;
        
        // Get values from form
        const keyword = $('#edit-keyword').val().trim();
        const associatedOn = $('#edit-associated-on').val();
        const associatedValue = $('#edit-associated-value').val().trim();
        const metaTitle = $('#edit-meta-title').val().trim();
        const metaDescription = $('#edit-meta-description').val().trim();
        
        // Validate inputs
        if (!keyword || !associatedOn || !associatedValue) {
            showNotificationModal('Error', 'Please fill in all required fields (Keyword, Associated On, and Associated Value).', 'error');
            $saveBtn.text(originalText).prop('disabled', false);
            isSaveInProgress = false;
            return;
        }
        
        // Get project ID from localStorage
        const projectId = localStorage.getItem('aysa_project_id');
        
        // Create the update data object
        const updateData = {
            keyword: keyword,
            associatedOn: associatedOn,
            associatedValue: associatedValue,
            metaTitle: metaTitle,
            metaDescription: metaDescription
        };
        
        // Add projectId if available
        if (projectId) {
            updateData.projectId = parseInt(projectId);
        }
        
        
        
        // First, get the full association data
        requestAssociationThroughWordPress('get', associationId)
            .done(function(association) {
                // Merge existing data with updates
                const updatedAssociation = {
                    ...association,
                    ...updateData
                };
                
                // Ensure projectId is set if available
                if (projectId && !updatedAssociation.projectId) {
                    updatedAssociation.projectId = parseInt(projectId);
                }
                
                
                
                // Save via the API
                requestAssociationThroughWordPress('upsert', associationId, updatedAssociation)
                    .done(function(response) {
                        
                        showNotificationModal('Success', 'Association updated successfully!', 'success');
                        
                        // Update the modal with new data
                        populateModalData(response);
                        
                        // Update the table row with new title if available
                        if (response.metaTitle || response.entityTitle || response.productTitle) {
                            const newTitle = response.metaTitle || response.entityTitle || response.productTitle;
                            updateAssociationTitleInTable(associationId, newTitle);
                        }
                        
                        // Refresh the specific row's entity details to get the latest title
                        const $row = $(`tr[data-id="${associationId}"], #aysa-row-${associationId}`);
                        if ($row.length > 0) {
                            const $entityCell = $row.find('.aysa-entity-cell');
                            const entityType = $entityCell.data('entity-type');
                            const entityId = $entityCell.data('entity-id');
                            
                            if (entityType && entityId) {
                                $entityCell.html('<span class="aysa-loading">Updating...</span>');
                                refreshSingleEntityDetails(entityType, entityId, $entityCell)
                                    .then(() => {
                                        console.log('✅ Entity details refreshed after save');
                                    })
                                    .catch((error) => {
                                        console.error('Error refreshing entity details:', error);
                                    });
                            }
                        }
                        
                        // Reset button and flags
                        $saveBtn.text(originalText).prop('disabled', false);
                        isSaveInProgress = false;
                        
                        // Close the modal after a short delay
                        setTimeout(function() {
                            closeModal();
                        }, 1000);
                    })
                    .fail(function(error) {
                        console.error('Error updating association:', error);
                        showNotificationModal('Error', 'Error updating association. Please try again.', 'error');
                        $saveBtn.text(originalText).prop('disabled', false);
                        isSaveInProgress = false;
                    });
            })
            .fail(function(error) {
                console.error('Error fetching association details:', error);
                showNotificationModal('Error', 'Error loading association details. Please try again.', 'error');
                $saveBtn.text(originalText).prop('disabled', false);
                isSaveInProgress = false;
            });
    }

    // Function to get the WordPress admin base URL
    function getWordPressAdminBaseUrl() {
        // Get the current URL
        const currentUrl = window.location.href;
        
        // Find the base WordPress URL (up to wp-admin)
        const wpAdminIndex = currentUrl.indexOf('/wp-admin/');
        if (wpAdminIndex !== -1) {
            return currentUrl.substring(0, wpAdminIndex) + '/wp-admin/';
        }
        
        // Fallback: try to construct from hostname
        return window.location.protocol + '//' + window.location.hostname + '/wp-admin/';
    }

    // Function to navigate to the associated page of the keyword
    function navigateToAssociatedPage(association) {
        
        
        try {
            // Make sure we have all data necessary
            if (!association) {
                showNotificationModal('Error', 'Association data is missing', 'error');
                return;
            }
            
            // Check for associatedOn and handle alternative formats
            let entityType = association.associatedOn || association.entityType;
            let entityId = association.associatedValue || association.idIntern;
            
            
            
            // Validate we have the minimum required data
            if (!entityType || !entityId) {
                console.error('Missing navigation data:', { 
                    entityType: entityType, 
                    entityId: entityId, 
                    association: association 
                });
                
                // If we have the associationId but missing other data, try to fetch it
                if (association.id) {
                    requestAssociationThroughWordPress('get', association.id)
                        .done(function(fullAssociation) {
                            navigateToAssociatedPage(fullAssociation);
                        })
                        .fail(function() {
                            showNotificationModal('Error', 'Cannot navigate: Failed to get association details', 'error');
                        });
                    return;
                }
                
                showNotificationModal('Error', 'Cannot navigate: Missing entity type or ID', 'error');
                return;
            }
            
            const openEditor = function(url) {
                if (!url) {
                    showNotificationModal('Error', 'Cannot navigate: missing editor URL', 'error');
                    return;
                }
                showNotificationModal('Navigating', `Editing ${entityType}...`, 'success');
                setTimeout(function() {
                    window.open(url, '_blank');
                }, 350);
            };

            const buildFallbackEditUrl = function() {
                let fallbackUrl = '';
                switch(entityType) {
                    case 'Category':
                        fallbackUrl = `${window.location.origin}/wp-admin/term.php?taxonomy=product_cat&tag_ID=${entityId}&post_type=product&wp_http_referer=${encodeURIComponent('/wp-admin/edit-tags.php?taxonomy=product_cat&post_type=product')}`;
                        break;
                    case 'Product':
                    case 'Page':
                    case 'Post':
                    case 'Article':
                    case 'Other':
                        fallbackUrl = `${window.location.origin}/wp-admin/post.php?post=${entityId}&action=edit`;
                        break;
                    default:
                        if (String(entityType).toLowerCase().includes('cat')) {
                            fallbackUrl = `${window.location.origin}/wp-admin/term.php?taxonomy=product_cat&tag_ID=${entityId}&post_type=product&wp_http_referer=${encodeURIComponent('/wp-admin/edit-tags.php?taxonomy=product_cat&post_type=product')}`;
                        } else {
                            fallbackUrl = `${window.location.origin}/wp-admin/post.php?post=${entityId}&action=edit`;
                        }
                }
                return fallbackUrl;
            };

            // Prefer backend-resolved edit URL for taxonomy/post-type accuracy.
            $.ajax({
                url: aysa_params.ajax_url,
                type: 'POST',
                data: {
                    action: 'aysa_get_entity_details',
                    nonce: aysa_params.nonce,
                    entity_type: entityType,
                    entity_id: entityId
                },
                success: function(response) {
                    if (response && response.success && response.data && response.data.edit_url) {
                        openEditor(String(response.data.edit_url));
                        return;
                    }
                    openEditor(buildFallbackEditUrl());
                },
                error: function() {
                    openEditor(buildFallbackEditUrl());
                }
            });
        } catch (e) {
            console.error('Error navigating to page:', e);
            showNotificationModal('Error', 'Error navigating to page: ' + e.message, 'error');
        }
    }

    // Function to initialize filters
    function initializeFilters() {
        const $keywordFilter = $('#keyword-filter');
        const $associatedOnFilter = $('#associated-on-filter');
        const $statusFilter = $('#status-filter');
        const $linksFilter = $('#links-filter');
        const $socialFilter = $('#social-filter');
        const $clearFilters = $('#clear-filters');
        const $entityTabs = $('.aysa-entity-tab');
        const $unlinkedPanel = $('#aysa-unlinked-entities-panel');
        const $noContentPanel = $('#aysa-no-content-panel');
        const $associationsTableWrap = $('#aysa-associations-table').closest('.aysa-table-responsive');
        
        // Keep default as All Statuses for novice UX
        $statusFilter.val('');
        
        // Function to apply filters
        function applyFilters() {
            const keywordValue = $keywordFilter.val().toLowerCase();
            const associatedOnValue = $associatedOnFilter.val();
            const statusValue = $statusFilter.val();
            const linksValue = $linksFilter.val();
            const socialValue = $socialFilter.val();
            
            let visibleRows = 0;
            $('#aysa-associations-table tbody tr').each(function() {
                const $row = $(this);
                
                // Skip loading and error rows
                if ($row.hasClass('aysa-loading-row') || $row.find('.aysa-error-cell').length) {
                    return;
                }
                
                const keyword = String($row.attr('data-main-keyword') || $row.find('.aysa-main-keyword-cell').text() || '').toLowerCase();
                const associatedOn = String($row.attr('data-entity-type') || '').trim();
                const rowEntityGroup = detectRowEntityGroup($row);
                
                // Get status from data attribute
                const rowStatus = $row.attr('data-status') || '';
                let matchesStatus = true;
                
                if (statusValue) {
                    switch(statusValue) {
                        case 'needs-work':
                            matchesStatus = rowStatus === 'needs-work';
                            break;
                        case 'poor':
                            matchesStatus = rowStatus === 'poor';
                            break;
                        case 'processed':
                            matchesStatus = rowStatus === 'processed';
                            break;
                        case 'good':
                            matchesStatus = rowStatus === 'good';
                            break;
                        default:
                            matchesStatus = true; // All statuses
                    }
                }
                
                // Check internal/external links (these columns are commented out, so skip for now)
                const internalCount = 0; // parseInt($row.find('td:eq(4)').text(), 10) || 0;
                const externalCount = 0; // parseInt($row.find('td:eq(5)').text(), 10) || 0;
                
                // Check social mentions (these columns are commented out, so skip for now)
                const socialCount = 0; // parseInt($row.find('td:eq(6)').text(), 10) || 0;
                
                // Match for links filter
                let matchesLinks = true;
                if (linksValue) {
                    if (linksValue === 'high-internal') {
                        matchesLinks = internalCount >= 10;
                    } else if (linksValue === 'low-internal') {
                        matchesLinks = internalCount < 10;
                    } else if (linksValue === 'high-external') {
                        matchesLinks = externalCount >= 7;
                    } else if (linksValue === 'low-external') {
                        matchesLinks = externalCount < 7;
                    }
                }
                
                // Match for social filter
                let matchesSocial = true;
                if (socialValue) {
                    if (socialValue === 'high-social') {
                        matchesSocial = socialCount >= 20;
                    } else if (socialValue === 'low-social') {
                        matchesSocial = socialCount < 20;
                    }
                }
                
                const matchesKeyword = !keywordValue || keyword.includes(keywordValue);
                // When using entity tabs, tab filter has priority over dropdown Associated On.
                const applyAssociatedOnDropdown = currentEntityTab === 'all' || currentEntityTab === 'unlinked' || currentEntityTab === 'no-content';
                const matchesAssociatedOn = !applyAssociatedOnDropdown ||
                    !associatedOnValue ||
                    associatedOn.toLowerCase() === String(associatedOnValue).toLowerCase();
                const matchesEntityTab = currentEntityTab === 'all' ||
                    currentEntityTab === 'unlinked' ||
                    currentEntityTab === 'no-content' ||
                    rowEntityGroup === currentEntityTab;
                
                if (matchesKeyword && matchesAssociatedOn && matchesStatus && matchesLinks && matchesSocial && matchesEntityTab) {
                    $row.show();
                    visibleRows += 1;
                } else {
                    $row.hide();
                }
            });

            if (currentEntityTab === 'unlinked') {
                $unlinkedPanel.show();
                $noContentPanel.hide();
                $associationsTableWrap.hide();
            } else if (currentEntityTab === 'no-content') {
                $unlinkedPanel.hide();
                $noContentPanel.show();
                $associationsTableWrap.hide();
            } else {
                $unlinkedPanel.hide();
                $noContentPanel.hide();
                $associationsTableWrap.show();
            }

            // User feedback for empty tab/filter results
            $('#aysa-associations-table tbody tr.aysa-filter-empty-row').remove();
            if (currentEntityTab !== 'unlinked' && currentEntityTab !== 'no-content' && visibleRows === 0) {
                const detectedTypes = [];
                $('#aysa-associations-table tbody tr').each(function() {
                    const $row = $(this);
                    if ($row.hasClass('aysa-loading-row') || $row.hasClass('aysa-filter-empty-row')) {
                        return;
                    }
                    const t = detectRowEntityGroup($row);
                    if (t && detectedTypes.indexOf(t) === -1) {
                        detectedTypes.push(t);
                    }
                });
                const typesInfo = detectedTypes.length ? ` Detected types: ${detectedTypes.join(', ')}.` : '';
                $('#aysa-associations-table tbody').append(
                    `<tr class="aysa-filter-empty-row"><td colspan="6" class="aysa-empty-cell">No associations match this tab/filter yet.${typesInfo}</td></tr>`
                );
            }
        }
        
        // Attach event listeners
        $keywordFilter.on('input', applyFilters);
        $associatedOnFilter.on('change', applyFilters);
        $statusFilter.on('change', applyFilters);
        $linksFilter.on('change', applyFilters);
        $socialFilter.on('change', applyFilters);

        $entityTabs.on('click', function() {
            $entityTabs.removeClass('active');
            $(this).addClass('active');
            currentEntityTab = String($(this).data('entity-tab') || 'all');

            // Prevent "tab looks empty" confusion caused by stale filters.
            $keywordFilter.val('');
            $associatedOnFilter.val('');
            $statusFilter.val('');
            $linksFilter.val('');
            $socialFilter.val('');
            applyFilters();
        });
        
        // Clear filters
        $clearFilters.on('click', function() {
            $keywordFilter.val('');
            $associatedOnFilter.val('');
            $statusFilter.val('');
            $linksFilter.val('');
            $socialFilter.val('');
            currentEntityTab = 'all';
            $entityTabs.removeClass('active');
            $entityTabs.filter('[data-entity-tab="all"]').addClass('active');
            applyFilters();
        });
        
        // Make applyFilters globally accessible
        window.applyFilters = applyFilters;
        applyFilters();
    }

    function decodeHtmlEntities(value) {
        const raw = String(value || '');
        if (!raw) return '';
        return raw
            .replace(/&amp;/gi, '&')
            .replace(/&#038;/gi, '&')
            .replace(/&#38;/gi, '&');
    }

    function isValidEditorUrl(url) {
        const candidate = decodeHtmlEntities(String(url || '').trim()).toLowerCase();
        if (!candidate) {
            return false;
        }
        if (candidate.includes('/wp-admin/edit.php')) {
            return false;
        }
        if (candidate.includes('/wp-admin/edit-tags.php')) {
            return false;
        }
        if (candidate.includes('/wp-admin/post.php') && candidate.includes('action=edit') && candidate.includes('post=')) {
            return true;
        }
        if (candidate.includes('/wp-admin/term.php') && candidate.includes('tag_id=')) {
            return true;
        }
        return false;
    }

    // Function to show SEO generation confirmation modal
    function showSEOGenerationModal(associationId, keyword, entityType) {
        // Remove existing modal if any
        $('#aysa-seo-generation-modal').remove();
        
        // Create modal HTML
        const modalHTML = `
            <div id="aysa-seo-generation-modal" class="aysa-modal">
                <div class="aysa-modal-content">
                    <div class="aysa-modal-header">
                        <h2 class="aysa-modal-title">Generate SEO Suggestions</h2>
                        <span class="aysa-modal-close">&times;</span>
                    </div>
                    <div class="aysa-modal-body">
                        <p>Generate SEO suggestions for the following keyword:</p>
                        <div class="aysa-modal-form-group">
                            <label for="seo-keyword-input">Main Keyword:</label>
                            <div class="aysa-keyword-input-container">
                                <input type="text" id="seo-keyword-input" class="aysa-modal-input" value="${keyword}" readonly>
                                <button type="button" id="edit-keyword-btn" class="aysa-btn aysa-btn-secondary aysa-btn-small">Edit</button>
                            </div>
                        </div>
                        <div class="aysa-modal-form-group">
                            <label>Entity Type:</label>
                            <span class="aysa-entity-type">${entityType}</span>
                        </div>
                    </div>
                    <div class="aysa-modal-footer">
                        <button class="aysa-btn aysa-btn-secondary" id="cancel-seo-generation">Cancel</button>
                        <button class="aysa-btn aysa-btn-primary" id="confirm-seo-generation" data-association-id="${associationId}">Generate SEO</button>
                    </div>
                </div>
            </div>
        `;
        
        // Add modal to page
        $('body').append(modalHTML);
        
        // Show modal
        $('#aysa-seo-generation-modal').show();
        
        // Event handlers for modal
        $('#aysa-seo-generation-modal .aysa-modal-close, #cancel-seo-generation').on('click', function() {
            $('#aysa-seo-generation-modal').remove();
        });
        
        // Edit keyword button handler
        $('#edit-keyword-btn').on('click', function() {
            const $input = $('#seo-keyword-input');
            const $btn = $(this);
            
            if ($input.prop('readonly')) {
                $input.prop('readonly', false).focus();
                $btn.text('Save');
            } else {
                $input.prop('readonly', true);
                $btn.text('Edit');
            }
        });
        
        // Confirm generation handler
        $('#confirm-seo-generation').on('click', function() {
            const finalKeyword = $('#seo-keyword-input').val().trim();
            const associationId = $(this).data('association-id');
            
            if (!finalKeyword) {
                showNotificationModal('Error', 'Please enter a keyword', 'error');
                return;
            }
            
            // Show loading state
            const $btn = $(this);
            const originalText = $btn.text();
            $btn.text('Generating...').prop('disabled', true);
            
            // Generate and persist the review item in the real backend queue.
            generateSEOContentWithHistory(finalKeyword, entityType, associationId)
                .then(async function() {
                    // Close modal
                    $('#aysa-seo-generation-modal').remove();

                    // Move directly to the real review queue and reload it from storage.
                    $('.aysa-tab-button[data-tab="review-queue"]').click();
                    await loadAutomatedPendingReviews();

                    // Show success message
                    showNotificationModal('Success', `SEO suggestions were added to the review queue for keyword: "${finalKeyword}"`, 'success');
                })
                .catch(function(error) {
                    console.error('SEO generation failed:', error);
                    showNotificationModal('Error', 'Failed to generate SEO content. Please try again.', 'error');
                })
                .finally(function() {
                    $btn.text(originalText).prop('disabled', false);
                });
        });
    }
    
    // Function to add entry to Pending Reviews
    function addToPendingReviews(keyword, entityType, associationId, seoResult) {
        const pendingTable = $('#aysa-pending-reviews-table tbody');
        const associationItem = (window.aysaAssociations || []).find((a) => parseInt(a.id, 10) === parseInt(associationId, 10));
        const wpEntityId = associationItem?.associatedValue || associationItem?.idIntern || associationItem?.IdIntern || 0;
        
        // Remove loading row if it exists
        pendingTable.find('.aysa-loading-row').remove();
        
        // Create new pending review entry
        const currentDate = new Date();
        const generatedTime = currentDate.toLocaleDateString() + ' ' + currentDate.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
        
        const pendingRow = `
            <tr data-association-id="${associationId}" data-keyword="${keyword}" data-entity-type="${entityType}">
                <td>Content for "${keyword}"</td>
                <td>${entityType}</td>
                <td>${keyword}</td>
                <td>${generatedTime}</td>
                <td>
                    <button class="aysa-btn aysa-btn-secondary aysa-btn-preview" title="Preview Changes">
                        <span class="dashicons dashicons-visibility"></span> Preview
                    </button>
                </td>
                <td>
                    <div class="aysa-action-buttons">
                        <button class="aysa-btn aysa-btn-success aysa-btn-approve" title="Apply Changes">
                            <span class="dashicons dashicons-controls-play"></span> Apply
                        </button>
                        <button class="aysa-btn aysa-btn-icon aysa-btn-history" title="View History" data-id="${wpEntityId || associationId}" data-association-id="${associationId}" data-entity-id="${wpEntityId}" data-entity-type="${entityType}">
                            <span class="dashicons dashicons-backup"></span>
                        </button>
                        <button class="aysa-btn aysa-btn-danger aysa-btn-reject" title="Reject Changes">
                            <span class="dashicons dashicons-dismiss"></span> Reject
                        </button>
                    </div>
                </td>
            </tr>
        `;
        
        // Store SEO result data for later use
        const $row = $(pendingRow);
        $row.data('seo-result', seoResult);
        
        // Remove placeholder rows before adding a real review item.
        pendingTable.find('.aysa-empty-state, .aysa-loading-row, .aysa-error').closest('tr').remove();

        // Add to table
        pendingTable.append($row);
        
        // Switch directly to the review queue so the user can inspect the new draft.
        $('.aysa-tab-button[data-tab="review-queue"]').click();
    }

    function getAutomationNonce() {
        return (window.aysaAutomationConfig && window.aysaAutomationConfig.nonce)
            || (window.aysa_ajax && window.aysa_ajax.nonce)
            || (window.aysaParams && window.aysaParams.nonce)
            || (window.aysa_params && window.aysa_params.nonce)
            || '';
    }

    function parseWordPressDateTime(value) {
        if (!value) {
            return null;
        }

        const normalized = String(value).trim().replace(' ', 'T');
        const parsed = new Date(normalized);
        if (!Number.isNaN(parsed.getTime())) {
            return parsed;
        }

        return null;
    }
    
    // Function to load automated pending reviews
    function loadAutomatedPendingReviews() {
        const pendingTable = $('#aysa-pending-reviews-table tbody');
        
        // Show loading state
        pendingTable.html('<tr class="aysa-loading-row"><td colspan="6"><div class="aysa-loading-spinner"></div><span>Loading automated pending reviews...</span></td></tr>');
        
        // Make AJAX call to get automated pending reviews
        $.ajax({
            url: ajaxurl,
            type: 'POST',
            data: {
                action: 'aysa_get_pending_automated_seo_reviews',
                nonce: getAutomationNonce()
            },
            success: function(response) {
                const reviews = Array.isArray(response.data)
                    ? response.data
                    : Object.values(response.data || {});

                if (response.success && reviews.length > 0) {
                    pendingTable.empty();
                    
                    reviews.forEach(function(review) {
                        const reviewDate = parseWordPressDateTime(review.generated_at || review.created_at);
                        const generatedTime = reviewDate
                            ? `${reviewDate.toLocaleDateString()} ${reviewDate.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}`
                            : 'Recently generated';
                        
                        const pendingRow = `
                            <tr data-association-id="${review.entity_id}" data-keyword="${review.keyword}" data-entity-type="${review.entity_type}" data-review-id="${review.id}">
                                <td>${review.title}</td>
                                <td>${review.entity_type}</td>
                                <td>${review.keyword}</td>
                                <td>${generatedTime} <span class="aysa-automated-badge">Auto</span></td>
                                <td>
                                    <button class="aysa-btn aysa-btn-secondary aysa-btn-preview" title="Preview Changes">
                                        <span class="dashicons dashicons-visibility"></span> Preview
                                    </button>
                                </td>
                                <td>
                                    <div class="aysa-action-buttons">
                                        <button class="aysa-btn aysa-btn-success aysa-btn-approve-automated" title="Apply Changes">
                                            <span class="dashicons dashicons-controls-play"></span> Apply
                                        </button>
                                        <button class="aysa-btn aysa-btn-icon aysa-btn-history" title="View History" data-id="${review.entity_id}">
                                            <span class="dashicons dashicons-backup"></span>
                                        </button>
                                        <button class="aysa-btn aysa-btn-danger aysa-btn-reject-automated" title="Reject Changes">
                                            <span class="dashicons dashicons-dismiss"></span> Reject
                                        </button>
                                    </div>
                                </td>
                            </tr>
                        `;
                        
                        const $row = $(pendingRow);
                        // Store the complete review data for later use
                        $row.data('review-data', review);
                        $row.data('seo-result', review.seo_data || review.suggestions || {});
                        pendingTable.append($row);
                    });
                } else {
                    pendingTable.html('<tr><td colspan="6"><span class="aysa-empty-state">No pending reviews yet.</span></td></tr>');
                }
            },
            error: function(xhr, status, error) {
                console.error('Failed to load automated pending reviews:', error);
                pendingTable.html('<tr><td colspan="6"><span class="aysa-error">Failed to load pending reviews</span></td></tr>');
            }
        });
    }
    
    // Function to add entry to Optimization Schedule tab (for approved items)
    async function addToOptimizationSchedule(keyword, entityType, associationId, seoResult = null) {
        onPageDebugLog('🔍 [DEBUG] addToOptimizationSchedule called with:', {
            keyword: keyword,
            entityType: entityType,
            associationId: associationId,
            seoResult: seoResult,
            seoResultKeys: seoResult ? Object.keys(seoResult) : 'null'
        });
        
        const scheduleTable = $('#aysa-schedule-table tbody');
        
        // Remove loading row if it exists
        scheduleTable.find('.aysa-loading-row').remove();
        
        // Create new schedule entry
        const currentDate = new Date();
        const nextRun = new Date(currentDate.getTime() + 24 * 60 * 60 * 1000); // Tomorrow
        
        const scheduleRow = `
            <tr data-association-id="${associationId}" data-seo-result='${seoResult ? JSON.stringify(seoResult) : ""}' data-keyword="${keyword}" data-entity-type="${entityType}">
                <td>SEO for "${keyword}"</td>
                <td>Keyword Optimization</td>
                <td>One-time</td>
                <td>${nextRun.toLocaleDateString()} ${nextRun.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</td>
                <td>-</td>
                <td><span class="aysa-status-badge aysa-status-pending">Pending</span></td>
                <td>
                    <div class="aysa-action-buttons">
                        <button class="aysa-btn aysa-btn-icon aysa-btn-preview" title="Preview SEO">
                            <span class="dashicons dashicons-visibility"></span>
                        </button>
                        <button class="aysa-btn aysa-btn-icon aysa-btn-apply" title="Apply SEO">
                            <span class="dashicons dashicons-yes"></span>
                        </button>
                    </div>
                </td>
            </tr>
        `;
        
        // Add to table
        scheduleTable.append(scheduleRow);
        
        // Event delegation for dynamically added buttons
        $(document).off('click', '.aysa-btn-preview').on('click', '.aysa-btn-preview', function() {
            const $row = $(this).closest('tr');
            const seoResultData = $row.data('seo-result');
            const rowKeyword = $row.data('keyword');
            const rowAssociationId = $row.data('association-id');
            onPageDebugLog('🔍 [DEBUG] Preview button clicked:', {
                rowKeyword: rowKeyword,
                rowAssociationId: rowAssociationId,
                seoResultData: seoResultData,
                seoResultDataType: typeof seoResultData
            });
            const rowEntityType = $row.data('entity-type');
            showSEOPreviewModal(rowAssociationId, rowKeyword, seoResultData, rowEntityType);
        });
        
        $(document).off('click', '.aysa-btn-apply').on('click', '.aysa-btn-apply', function() {
            const $row = $(this).closest('tr');
            const seoResultData = $row.data('seo-result');
            const rowKeyword = $row.data('keyword');
            const rowEntityType = $row.data('entity-type');
            const rowAssociationId = $row.data('association-id');
            applySEOChanges(rowAssociationId, rowKeyword, rowEntityType, seoResultData);
        });
        
        // Navigate to Optimization Schedule tab
        const scheduleTab = document.querySelector('[data-tab="optimization-schedule"]');
        if (scheduleTab) {
            scheduleTab.click();
        }
        
        // Show success message
        showNotificationModal(
            'SEO Generated Successfully!',
            `SEO suggestions for "${keyword}" have been added to the Optimization Schedule. You can now preview and apply the changes.`,
            'success'
        );
    }

    // Function to show SEO preview modal
    function showSEOPreviewModal(associationId, keyword, seoResult, explicitEntityType = null) {
        // Handle case where seoResult might be a JSON string
        if (typeof seoResult === 'string') {
            try {
                seoResult = JSON.parse(seoResult);
            } catch (e) {
                console.error('❌ [ERROR] Failed to parse seoResult JSON:', e);
                seoResult = {};
            }
        }
        
        // Debug: Log the seoResult to see what data is available
        onPageDebugLog('🔍 [DEBUG] showSEOPreviewModal called with:', {
            associationId: associationId,
            keyword: keyword,
            seoResult: seoResult,
            seoResultKeys: Object.keys(seoResult || {})
        });
        
        // Get entity type from association
        const association = (window.aysaAssociations || []).find((a) => parseInt(a.id, 10) === parseInt(associationId, 10));
        const entityType = explicitEntityType || association?.AssociatedOn || association?.associatedOn || 'product';
        
        onPageDebugLog('🔍 [DEBUG] Entity type detected:', entityType);
        onPageDebugLog('🔍 [DEBUG] Association found:', association);
        
        // Build preview sections based on entity type and available data
        let previewSections = '';
        const renderedKeys = new Set();
        
        // Always show basic SEO fields
        if (seoResult.metaTitle) {
            renderedKeys.add('metaTitle');
            previewSections += `
                <div class="aysa-preview-section">
                    <h3>Meta Title</h3>
                    <div class="aysa-preview-suggested">
                        <strong>Generated:</strong> <span>${seoResult.metaTitle}</span>
                    </div>
                </div>
            `;
        }
        
        if (seoResult.metaDescription) {
            renderedKeys.add('metaDescription');
            previewSections += `
                <div class="aysa-preview-section">
                    <h3>Meta Description</h3>
                    <div class="aysa-preview-suggested">
                        <strong>Generated:</strong> <span>${seoResult.metaDescription}</span>
                    </div>
                </div>
            `;
        }
        
        if (seoResult.metaKeywords) {
            renderedKeys.add('metaKeywords');
            previewSections += `
                <div class="aysa-preview-section">
                    <h3>Meta Keywords</h3>
                    <div class="aysa-preview-suggested">
                        <strong>Generated:</strong> <span>${seoResult.metaKeywords}</span>
                    </div>
                </div>
            `;
        }
        
        // Show ALL available data from seoResult (dynamic approach)
        onPageDebugLog('🔍 [DEBUG] Building preview sections for all available data...');
        
        // Define field mappings for better display names based on AysaSeoGenerator results
        const fieldMappings = {
            // Basic SEO fields (from AysaSeoGenerator)
            'metaTitle': 'Meta Title',
            'metaDescription': 'Meta Description', 
            'metaKeywords': 'Meta Keywords',
            'secondKeyword': 'Secondary Keywords',
            'secondaryKeywords': 'Secondary Keywords',
            
            // Entity fields (from AysaSeoGenerator)
            'entityTitle': 'Entity Title',
            'entityContent': 'Entity Content',
            'shortDescription': 'Short Description',
            'permalink': 'Permalink',
            
            // Product-specific fields
            'productTitle': 'Product Title',
            'productDescription': 'Product Description',
            'productPermalink': 'Product Permalink',
            
            // Category-specific fields
            'categoryTitle': 'Category Title',
            'categoryDescription': 'Category Description',
            'categoryPermalink': 'Category Permalink',
            
            // Content fields
            'seoOptimizedContent': 'SEO Optimized Content',
            'content': 'Content',
            'contentTitle': 'Content Title',
            'contentDescription': 'Content Description',
            
            // Other possible fields
            'title': 'Title',
            'description': 'Description',
            'keywords': 'Keywords',
            'slug': 'Slug'
        };
        
        // Loop through all available fields in seoResult
        onPageDebugLog('🔍 [DEBUG] All seoResult entries:', Object.entries(seoResult));
        onPageDebugLog('🔍 [DEBUG] seoResult keys:', Object.keys(seoResult));
        onPageDebugLog('🔍 [DEBUG] seoResult values:', Object.values(seoResult));
        
        for (const [key, value] of Object.entries(seoResult)) {
            onPageDebugLog('🔍 [DEBUG] Checking field:', key, '=', value, 'Type:', typeof value, 'Length:', typeof value === 'string' ? value.length : 'N/A');
            
            if (value && value !== '' && value !== null && value !== undefined) {
                if (renderedKeys.has(key)) {
                    continue;
                }
                const displayName = fieldMappings[key] || key.charAt(0).toUpperCase() + key.slice(1);
                
                onPageDebugLog('🔍 [DEBUG] Adding field to preview:', key, '=', value);
                
                if (key === 'secondaryKeywords') {
                    // Handle secondary keywords specially
                    const keywordsText = Array.isArray(value) ? value.join(', ') : value;
                    previewSections += `
                        <div class="aysa-preview-section">
                            <h3>${displayName}</h3>
                            <div class="aysa-preview-suggested">
                                <strong>Generated:</strong> <span>${keywordsText}</span>
                            </div>
                        </div>
                    `;
                } else if (key === 'entityContent' || key === 'seoOptimizedContent' || key === 'content' || key === 'productDescription' || key === 'categoryDescription' || key === 'contentDescription' || key === 'metaDescription') {
                    // Handle long content fields with scroll
                    previewSections += `
                        <div class="aysa-preview-section">
                            <h3>${displayName}</h3>
                            <div class="aysa-preview-suggested">
                                <strong>Generated:</strong> <div class="aysa-content-preview">${value}</div>
                            </div>
                        </div>
                    `;
                } else {
                    // Handle regular text fields
                    previewSections += `
                        <div class="aysa-preview-section">
                            <h3>${displayName}</h3>
                            <div class="aysa-preview-suggested">
                                <strong>Generated:</strong> <span>${value}</span>
                            </div>
                        </div>
                    `;
                }
            }
        }
        
        const modalHtml = `
            <div id="aysa-seo-preview-modal" class="aysa-modal">
                <div class="aysa-modal-content">
                    <div class="aysa-modal-header">
                        <h2 class="aysa-modal-title">SEO Preview for "${keyword}" (${entityType})</h2>
                        <span class="aysa-modal-close">&times;</span>
                    </div>
                    <div class="aysa-modal-body">
                        ${previewSections || '<p>No SEO data available for preview.</p>'}
                    </div>
                    <div class="aysa-modal-footer">
                        <button class="aysa-btn aysa-btn-secondary" id="aysa-preview-close-btn">Close</button>
                    </div>
                </div>
            </div>
        `;
        
        // Remove any previous preview modal before rendering a fresh one.
        $('#aysa-seo-preview-modal').remove();

        // Add modal to page
        $('body').append(modalHtml);
        
        // Show modal
        $('#aysa-seo-preview-modal').show();
        
        // Close modal handlers
        $('#aysa-seo-preview-modal .aysa-modal-close, #aysa-preview-close-btn').on('click', function() {
            $('#aysa-seo-preview-modal').remove();
        });
        
        // Close on outside click
        $('#aysa-seo-preview-modal').on('click', function(e) {
            if (e.target === this) {
                $('#aysa-seo-preview-modal').remove();
            }
        });
    }
    
    // Function to apply SEO changes
    async function applySEOChanges(associationId, keyword, entityType, seoResult) {
        // Handle case where seoResult might be a JSON string
        if (typeof seoResult === 'string') {
            try {
                seoResult = JSON.parse(seoResult);
            } catch (e) {
                console.error('❌ [ERROR] Failed to parse seoResult JSON:', e);
                seoResult = {};
            }
        }
        
        try {
            onPageDebugLog('🔧 [DEBUG] applySEOChanges called with:', {
                associationId: associationId,
                keyword: keyword,
                entityType: entityType,
                seoResult: seoResult
            });
            
            // Get the association data to find the WordPress entity ID
            
            
            
            
            if (!window.aysaAssociations) {
                console.warn('Associations not loaded yet. Loading them now...');
                // Try to load associations if they're not available
                await new Promise((resolve) => {
                    fetchAssociations();
                    // Wait a bit for the AJAX call to complete
                    setTimeout(resolve, 2000);
                });
                
                
            }
            
            
            
            
            const association = (window.aysaAssociations || []).find((a) => parseInt(a.id, 10) === parseInt(associationId, 10));
            if (!association) {
                console.error('Could not find association for ID:', associationId);
                onPageDebugLog('Available associations:', window.aysaAssociations);
                throw new Error('Association not found');
            }
            
            
            
            // Get the WordPress entity ID from the association
            const entityId = association.associatedValue || association.idIntern;
            
            
            
            
            
            if (!entityId) {
                console.error('Could not determine WordPress entity ID for association:', association);
                throw new Error('WordPress entity ID not found');
            }
            
            
            
            // Apply SEO changes to the WordPress entity
            await applySEOToEntity(entityId, entityType, seoResult);
            
            // Update the association with new SEO data
            await updateAssociationWithSEO(associationId, seoResult);
            
            // Update association status to "processed" after successful SEO application
            try {
                await updateAssociationStatusAndRefresh(associationId, 'processed');
                onPageDebugLog('✅ [SUCCESS] Association status updated to processed and table refreshed for ID:', associationId);
                
                if (window.aysaAssociations) {
                    displayAssociations(window.aysaAssociations);
                }
            } catch (statusError) {
                console.warn('⚠️ [WARNING] Failed to update association status after SEO application:', statusError);
                // Don't fail the whole process if status update fails
            }
            
            // Update status in the schedule table
            const scheduleRow = $(`tr[data-association-id="${associationId}"]`);
            if (scheduleRow.length > 0) {
                scheduleRow.find('.aysa-status-badge')
                    .removeClass('aysa-status-pending')
                    .addClass('aysa-status-applied')
                    .text('Applied');
                
                // Remove the apply button since it's already applied
                scheduleRow.find('.aysa-btn-apply').remove();
            }
            
            // Show success message
            showNotificationModal(
                'SEO Applied Successfully!',
                `SEO changes for "${keyword}" have been applied to the entity.`,
                'success'
            );
            
            return { success: true };
        } catch (error) {
            console.error('applySEOChanges failed:', error);
            throw error;
        }
    }
    
    // Function to generate SEO content and create history entry
async function generateSEOContentWithHistory(keyword, entityType, associationId) {
    try {
        // Get the WordPress entity ID (idIntern) from the association
        const currentAssociation = (window.aysaAssociations || []).find((a) => parseInt(a.id, 10) === parseInt(associationId, 10));
        const wordPressEntityId = currentAssociation?.associatedValue || currentAssociation?.idIntern || currentAssociation?.IdIntern || 0;
        const entityName = currentAssociation?.entityName
            || currentAssociation?.name
            || currentAssociation?.title
            || currentAssociation?.entityTitle
            || '';
        const nonce = getAutomationNonce();
        
        onPageDebugLog('🔧 [DEBUG] Using WordPress entity ID:', wordPressEntityId, 'for association:', associationId);
        
        if (!wordPressEntityId) {
            console.error('Could not find WordPress entity ID for association:', associationId);
            throw new Error('Could not determine WordPress entity ID');
        }

        const generator = new AysaSeoGenerator({
            defaultTimeout: 30000,
            contentTimeout: 120000,
            totalTimeout: 180000,
            apiBaseUrl: window.aysa_params?.api_base_url || window.aysaParams?.api_base_url || 'https://api.aysa.ai/',
        });

        const generatedSeo = await generator.generateSuggestions({
            entityId: wordPressEntityId,
            entityType: entityType,
            keyword: keyword,
            entityName: entityName,
            websiteName: window.location.hostname,
            existingData: {},
        });

        if (!generatedSeo || typeof generatedSeo !== 'object') {
            throw new Error('SEO suggestions could not be generated.');
        }

        const response = await new Promise((resolve, reject) => {
            jQuery.ajax({
                url: ajaxurl,
                type: 'POST',
                data: {
                    action: 'aysa_store_manual_seo_review',
                    entity_id: wordPressEntityId,
                    entity_type: entityType,
                    keyword: keyword
                    ,
                    nonce: nonce,
                    seo_data: JSON.stringify(generatedSeo),
                },
                success: function(result) {
                    if (result && result.success) {
                        resolve({
                            ...(result.data || result),
                            seo_data: generatedSeo,
                        });
                        return;
                    }
                    reject(new Error(result?.data?.message || result?.data || 'SEO suggestions could not be saved.'));
                },
                error: function(xhr, status, error) {
                    reject(new Error(error || 'SEO review storage request failed.'));
                }
            });
        });

        onPageDebugLog('🔧 [DEBUG] Manual SEO generation result:', response);
        return response;
        
    } catch (error) {
        console.error('SEO generation with history failed:', error);
        throw error;
    }
}



// Function to apply SEO data to WordPress entity
async function applySEOToEntity(entityId, entityType, seoData) {
    try {
        onPageDebugLog('🔧 [DEBUG] applySEOToEntity called with:', {
            entityId: entityId,
            entityType: entityType,
            seoData: seoData
        });
        
        // Enhanced validation and logging
        if (!entityId) {
            console.error('Entity ID is missing or invalid:', entityId);
            throw new Error('Entity ID is required');
        }
        
        if (!entityType) {
            console.error('Entity type is missing:', entityType);
            throw new Error('Entity type is required');
        }
        
        // Check available nonces
        onPageDebugLog('🔑 [DEBUG] Available nonces:', {
            aysaSeoHistoryAjax: typeof aysaSeoHistoryAjax !== 'undefined' ? aysaSeoHistoryAjax.nonce : 'undefined',
            aysaParams: typeof aysaParams !== 'undefined' ? aysaParams.nonce : 'undefined'
        });
        
        let updateData = {
            nonce: (typeof aysaSeoHistoryAjax !== 'undefined' ? aysaSeoHistoryAjax.nonce : aysaParams.nonce),
            entity_id: entityId,
            entity_type: entityType,
            meta_title: seoData.metaTitle || '',
            meta_description: seoData.metaDescription || '',
            meta_keywords: seoData.metaKeywords || ''
        };
        
        // Add entity-specific fields based on type
        const entityTypeLower = entityType.toLowerCase();
        
        if (entityTypeLower === 'product') {
            // Product-specific fields - map from AysaSeoGenerator results
            // AysaSeoGenerator returns: entityTitle, entityContent, shortDescription, permalink
            updateData.product_title = seoData.entityTitle || seoData.productTitle || '';
            updateData.product_description = seoData.entityContent || seoData.productDescription || '';
            updateData.short_description = seoData.shortProductDescription || seoData.shortDescription || '';
            updateData.product_permalink = seoData.permalink || seoData.productPermalink || '';
            updateData.secondary_keywords = seoData.secondaryKeywords || seoData.secondKeyword || '';
            
            
            onPageDebugLog('🔍 [DEBUG] Product data mapping from AysaSeoGenerator:', {
                entityTitle: seoData.entityTitle,
                entityContent: seoData.entityContent,
                shortProductDescription: seoData.shortProductDescription,
                shortDescription: seoData.shortDescription,
                permalink: seoData.permalink,
                secondaryKeywords: seoData.secondaryKeywords,
                secondKeyword: seoData.secondKeyword
            });
            
        } else if (entityTypeLower === 'category' || entityTypeLower === 'product_cat' || entityTypeLower === 'product_category') {
            // Category-specific fields - map from AysaSeoGenerator results
            // AysaSeoGenerator returns: entityTitle, entityContent, permalink
            updateData.category_title = seoData.entityTitle || seoData.categoryTitle || seoData.productTitle || '';
            updateData.category_description = seoData.entityContent || seoData.categoryDescription || seoData.productDescription || seoData.content || '';
            updateData.category_permalink = seoData.permalink || seoData.categoryPermalink || seoData.productPermalink || '';
            updateData.secondary_keywords = seoData.secondaryKeywords || seoData.secondKeyword || '';
            
            
            onPageDebugLog('🔍 [DEBUG] Category data mapping from AysaSeoGenerator:', {
                entityTitle: seoData.entityTitle,
                entityContent: seoData.entityContent,
                permalink: seoData.permalink,
                secondaryKeywords: seoData.secondaryKeywords,
                secondKeyword: seoData.secondKeyword
            });
            
        } else if (entityTypeLower === 'post' || entityTypeLower === 'page') {
            // Post/Page-specific fields - map from AysaSeoGenerator results
            updateData.content_title = seoData.entityTitle || seoData.contentTitle || seoData.productTitle || '';
            updateData.content_description = seoData.entityContent || seoData.contentDescription || seoData.productDescription || seoData.content || '';
            updateData.secondary_keywords = seoData.secondaryKeywords || seoData.secondKeyword || '';
            
            
            onPageDebugLog('🔍 [DEBUG] Post/Page data mapping from AysaSeoGenerator:', {
                entityTitle: seoData.entityTitle,
                entityContent: seoData.entityContent,
                secondaryKeywords: seoData.secondaryKeywords,
                secondKeyword: seoData.secondKeyword
            });
        }
        
        
        
        
        // Different actions based on entity type
        
        switch (entityTypeLower) {
            case 'post':
                updateData.action = 'aysa_seo_update_post_meta';
                
                break;
            case 'page':
                updateData.action = 'aysa_seo_update_post_meta'; // Pages use same handler as posts
                
                break;
            case 'product':
                updateData.action = 'aysa_seo_update_product_meta';
                
                break;
            case 'category':
                updateData.action = 'aysa_seo_update_category_meta';
                
                break;
            case 'product_cat':
            case 'product_category':
                updateData.action = 'aysa_seo_update_product_category_meta';
                
                break;
            default:
                updateData.action = 'aysa_seo_update_generic_meta';
                
        }
        
        // Send AJAX request to update entity meta
        onPageDebugLog('🚀 [DEBUG] Sending AJAX request with data:', updateData);
        onPageDebugLog('🔍 [DEBUG] Short description value being sent:', updateData.short_description);
        onPageDebugLog('🔍 [DEBUG] Product title value being sent:', updateData.product_title);
        
        const updateResponse = await new Promise((resolve, reject) => {
            jQuery.ajax({
                url: (typeof aysaSeoHistoryAjax !== 'undefined' ? aysaSeoHistoryAjax.ajax_url : aysaParams.ajax_url),
                type: 'POST',
                data: updateData,
                beforeSend: function() {
                    onPageDebugLog('🚀 [DEBUG] AJAX request starting...');
                },
                success: function(response) {
                    onPageDebugLog('✅ [DEBUG] AJAX success response:', response);
                    
                    if (response.success) {
                        onPageDebugLog('✅ [DEBUG] Server returned success, data:', response.data);
                        resolve(response.data);
                    } else {
                        console.error('Server returned failure response:', response.data);
                        reject(new Error(response.data.message || 'Failed to update SEO meta'));
                    }
                },
                error: function(xhr, status, error) {
                    console.error('AJAX request failed:', error, xhr);
                    console.error('📊 [ERROR] Full XHR object:', xhr);
                    reject(new Error(`AJAX error: ${error}`));
                }
            });
        });
        
        // Update UI elements if they exist
        updateUIElements(seoData);
        
        // Test if the meta was actually saved
        
        
        return updateResponse;
        
    } catch (error) {
        console.error('Failed to apply SEO to entity:', error);
        throw error;
    }
}

// Function to update association with new SEO data
async function updateAssociationWithSEO(associationId, seoData) {
    try {
        
        
        // Get project ID from localStorage
        const projectId = localStorage.getItem('aysa_project_id');
        
        // First, get the current association data
        const currentAssociation = await requestAssociationThroughWordPress('get', associationId);
        
        // Update the association with new SEO data
        const updatedAssociation = {
            ...currentAssociation,
            metaTitle: seoData.metaTitle || currentAssociation.metaTitle,
            metaDescription: seoData.metaDescription || currentAssociation.metaDescription,
            metaKeywords: seoData.metaKeywords || currentAssociation.metaKeywords,
            titleSuggestion: seoData.metaTitle || '',
            descriptionSuggestion: seoData.metaDescription || '',
            lastUpdated: new Date().toISOString()
        };
        
        // Add projectId if available
        if (projectId && !updatedAssociation.projectId) {
            updatedAssociation.projectId = parseInt(projectId);
        }
        
        
        
        // Update via the upsert endpoint
        const updateResponse = await requestAssociationThroughWordPress('upsert', associationId, updatedAssociation);
        
        return updateResponse;
        
    } catch (error) {
        console.error('Failed to update association with SEO data:', error);
        throw error;
    }
}



// Function to update UI elements on the page
function updateUIElements(seoData) {
    // Update meta title field if it exists
    const titleField = document.querySelector('#aysa_seo_title, input[name="aysa_seo_title"], .seo-title-field');
    if (titleField) {
        titleField.value = seoData.metaTitle || '';
        titleField.dispatchEvent(new Event('change'));
    }
    
    // Update meta description field if it exists
    const descField = document.querySelector('#aysa_seo_description, textarea[name="aysa_seo_description"], .seo-description-field');
    if (descField) {
        descField.value = seoData.metaDescription || '';
        descField.dispatchEvent(new Event('change'));
    }
    
    // Update meta keywords field if it exists
    const keywordsField = document.querySelector('#aysa_seo_keywords, input[name="aysa_seo_keywords"], .seo-keywords-field');
    if (keywordsField) {
        keywordsField.value = seoData.metaKeywords || '';
        keywordsField.dispatchEvent(new Event('change'));
    }
    
    // Update product title field if it exists (for products)
    if (seoData.entityTitle || seoData.productTitle) {
        const productTitle = seoData.entityTitle || seoData.productTitle;
        
        // Try multiple possible product title fields
        const productTitleFields = [
            '#title',
            '#post_title',
            '#product_title',
            'input[name="title"]',
            'input[name="post_title"]',
            'input[name="product_title"]',
            '.editor-post-title__input'
        ];
        
        productTitleFields.forEach(selector => {
            const field = document.querySelector(selector);
            if (field) {
                field.value = productTitle;
                field.dispatchEvent(new Event('change'));
                field.dispatchEvent(new Event('input'));
                console.log(`✅ Updated product title in ${selector}`);
            }
        });
    }
    
    // Update short description field if it exists (for products)
    if (seoData.shortProductDescription) {
        // Try multiple possible short description fields
        const shortDescFields = [
            '#excerpt',
            '#product_short_description',
            'textarea[name="excerpt"]',
            'textarea[name="product_short_description"]',
            '.woocommerce-product-short-description'
        ];
        
        shortDescFields.forEach(selector => {
            const field = document.querySelector(selector);
            if (field) {
                field.value = seoData.shortProductDescription;
                field.dispatchEvent(new Event('change'));
                field.dispatchEvent(new Event('input'));
                console.log(`✅ Updated short description in ${selector}`);
            }
        });
        
        // Update TinyMCE editor for excerpt if it exists
        if (typeof tinyMCE !== 'undefined' && tinyMCE.get('excerpt')) {
            tinyMCE.get('excerpt').setContent(seoData.shortProductDescription, { format: 'raw' });
            console.log('✅ Updated short description in TinyMCE excerpt editor');
        }
    }
    
    // Update product description field if it exists (for products)
    if (seoData.entityContent || seoData.productDescription) {
        const productDesc = seoData.entityContent || seoData.productDescription;
        
        // Try multiple possible product description fields
        const productDescFields = [
            '#content',
            '#post_content',
            'textarea[name="content"]',
            'textarea[name="post_content"]',
            '.wp-editor-area'
        ];
        
        productDescFields.forEach(selector => {
            const field = document.querySelector(selector);
            if (field) {
                field.value = productDesc;
                field.dispatchEvent(new Event('change'));
                field.dispatchEvent(new Event('input'));
                console.log(`✅ Updated product description in ${selector}`);
            }
        });
        
        // Update TinyMCE editor for content if it exists
        if (typeof tinyMCE !== 'undefined' && tinyMCE.get('content')) {
            tinyMCE.get('content').setContent(productDesc, { format: 'raw' });
            console.log('✅ Updated product description in TinyMCE content editor');
        }
    }
    
    // Update product permalink field if it exists (for products)
    if (seoData.permalink || seoData.productPermalink) {
        const productPermalink = seoData.permalink || seoData.productPermalink;
        
        // Try multiple possible permalink fields
        const permalinkFields = [
            '#post_name',
            '#slug',
            '#editable-post-name',
            'input[name="post_name"]',
            'input[name="slug"]',
            '.editor-post-slug__input'
        ];
        
        permalinkFields.forEach(selector => {
            const field = document.querySelector(selector);
            if (field) {
                field.value = productPermalink;
                field.dispatchEvent(new Event('change'));
                field.dispatchEvent(new Event('input'));
                console.log(`✅ Updated product permalink in ${selector}`);
            }
        });
    }
    
    // Update preview elements if they exist
    const titlePreview = document.querySelector('.seo-title-preview');
    if (titlePreview) {
        titlePreview.textContent = seoData.metaTitle || '';
    }
    
    const descPreview = document.querySelector('.seo-description-preview');
    if (descPreview) {
        descPreview.textContent = seoData.metaDescription || '';
    }
}

// Optional: Function to handle different SEO plugins (Yoast, RankMath, etc.)
function updateThirdPartySEOFields(seoData) {
    // Yoast SEO fields
    const yoastTitle = document.querySelector('#yoast_wpseo_title');
    const yoastDesc = document.querySelector('#yoast_wpseo_metadesc');
    
    if (yoastTitle) {
        yoastTitle.value = seoData.metaTitle || '';
        yoastTitle.dispatchEvent(new Event('input'));
    }
    
    if (yoastDesc) {
        yoastDesc.value = seoData.metaDescription || '';
        yoastDesc.dispatchEvent(new Event('input'));
    }
    
    // RankMath SEO fields
    const rankMathTitle = document.querySelector('#rank_math_title');
    const rankMathDesc = document.querySelector('#rank_math_description');
    
    if (rankMathTitle) {
        rankMathTitle.value = seoData.metaTitle || '';
        rankMathTitle.dispatchEvent(new Event('input'));
    }
    
    if (rankMathDesc) {
        rankMathDesc.value = seoData.metaDescription || '';
        rankMathDesc.dispatchEvent(new Event('input'));
    }
}

    // Initialize when document is ready
    $(document).ready(function() {
        
        
        // Check if we're on the on-page optimization page
        if ($('.aysa-dashboard-wrapper').length && $('.aysa-dashboard-title h1').text().indexOf('On-Page Optimization') !== -1) {
            
            initializeAssociationsTable();
            
            // Load automated pending reviews
            loadAutomatedPendingReviews();
        }
        
        // Event handlers for History and Generate SEO buttons
        $(document).on('click', '.aysa-btn-history', function() {
            const $btn = $(this);
            const $row = $btn.closest('tr');
            const associationId = parseInt($btn.data('association-id'), 10) || 0;
            let entityId = parseInt($btn.data('entity-id'), 10) || 0;
            let entityType = String($btn.data('entity-type') || '').trim();

            if (!entityId && $row.length) {
                const rowEntityId = parseInt($row.find('.aysa-entity-cell').data('entity-id'), 10) || 0;
                if (rowEntityId) {
                    entityId = rowEntityId;
                }
            }
            if (!entityType && $row.length) {
                const rowEntityType = String($row.find('.aysa-entity-cell').data('entity-type') || $row.attr('data-entity-type') || '').trim();
                if (rowEntityType) {
                    entityType = rowEntityType;
                }
            }
            // Pending review rows may expose only data-id (entity id).
            if (!entityId) {
                entityId = parseInt($btn.data('id'), 10) || 0;
            }

            const params = new URLSearchParams();
            params.set('page', 'aysa-seo-history');
            if (entityId > 0) params.set('entity_id', String(entityId));
            if (entityType) params.set('entity_type', entityType);
            if (associationId > 0) params.set('association_id', String(associationId));
            const historyUrl = `${window.location.origin}${window.location.pathname}?${params.toString()}`;
            window.open(historyUrl, '_blank');
        });
        
        $(document).on('click', '.aysa-btn-generate', function() {
            const associationId = $(this).data('id');
            const $row = $(this).closest('tr');
            const keyword = String($row.attr('data-main-keyword') || '').trim();
            const entityType = String($row.attr('data-entity-type') || $row.find('.aysa-entity-cell').data('entity-type') || '').trim();
            
            
            
            // Show confirmation modal with editable keyword
            showSEOGenerationModal(associationId, keyword, entityType);
        });
        
        // Event handlers for pending review actions
        $(document).on('click', '.aysa-btn-preview', function() {
            const $row = $(this).closest('tr');
            const seoResult = $row.data('seo-result');
            const keyword = $row.data('keyword');
            const associationId = $row.data('association-id');
            const entityType = $row.data('entity-type');

            
            showSEOPreviewModal(associationId, keyword, seoResult, entityType);
        });
        
        $(document).on('click', '.aysa-btn-approve', function() {
            const $row = $(this).closest('tr');
            const seoResult = $row.data('seo-result');
            const keyword = $row.data('keyword');
            const entityType = $row.data('entity-type');
            const associationId = $row.data('association-id');
            
            
            
            // Apply the SEO changes
            applySEOChanges(associationId, keyword, entityType, seoResult)
                .then(function() {
                    // Move to optimization schedule
                    addToOptimizationSchedule(keyword, entityType, associationId);
                    
                    // Remove from pending reviews
                    $row.remove();
                    
                    // Show success message
                    showNotificationModal('Success', `SEO changes applied for keyword: "${keyword}"`, 'success');
                })
                .catch(function(error) {
                    console.error('Failed to apply SEO changes:', error);
                    showNotificationModal('Error', 'Failed to apply SEO changes. Please try again.', 'error');
                });
        });
        
        $(document).on('click', '.aysa-btn-reject', function() {
            const $row = $(this).closest('tr');
            const keyword = $row.data('keyword');
            
            
            
            // Remove from pending reviews
            $row.remove();
            
            // Show message
            showNotificationModal('Info', `SEO suggestions rejected for keyword: "${keyword}"`, 'info');
        });
        
        // Event handlers for automated review actions
        $(document).on('click', '.aysa-btn-approve-automated', function() {
            const $row = $(this).closest('tr');
            const reviewId = $row.data('review-id');
            const postTitle = $row.find('td:eq(0)').text().trim();
            
            
            if (window.AYSA_DEBUG === true) {
                console.log('📋 [DEBUG] Row data:', {
                    reviewId: reviewId,
                    postTitle: postTitle,
                    rowData: $row.data(),
                    allRowData: {
                        'review-id': $row.data('review-id'),
                        'entity-id': $row.data('entity-id'),
                        'entity-type': $row.data('entity-type'),
                        'association-id': $row.data('association-id'),
                        'keyword': $row.data('keyword'),
                        'seo-result': $row.data('seo-result')
                    }
                });
            }
            
            // Validate required data
            if (!reviewId) {
                console.error('❌ [ERROR] Review ID is missing from row data');
                showNotificationModal('Error', 'Review ID is missing. Cannot approve review.', 'error');
                return;
            }
            
            // Check available nonces
            console.log('🔑 [DEBUG] Available nonces for approval:', {
                aysaParams: typeof aysaParams !== 'undefined' ? aysaParams.nonce : 'undefined',
                aysaSeoHistoryAjax: typeof aysaSeoHistoryAjax !== 'undefined' ? aysaSeoHistoryAjax.nonce : 'undefined'
            });
            
            // Show loading state
            $(this).prop('disabled', true).text('Approving...');
            
            const requestData = {
                action: 'aysa_approve_automated_review',
                review_id: reviewId,
                nonce: getAutomationNonce()
            };
            
            console.log('📤 [DEBUG] Sending approval request:', {
                url: ajaxurl,
                data: requestData
            });
            
            // Make AJAX call to approve
            $.ajax({
                url: ajaxurl,
                type: 'POST',
                data: requestData,
                beforeSend: function() {
                    
                },
                success: function(response) {
                    
                    
                    
                    
                    if (response.success) {
                        
                        
                        
                        // Remove from pending reviews table
                        $row.remove();
                        
                        // Show success message
                        showNotificationModal('Success', `SEO changes applied for "${postTitle}"`, 'success');
                    } else {
                        console.error('❌ [ERROR] Server returned failure for approval:', response.data);
                        showNotificationModal('Error', 'Failed to approve review: ' + (response.data || 'Unknown error'), 'error');
                    }
                },
                error: function(xhr, status, error) {
                    console.error('💥 [FATAL ERROR] Approval AJAX request failed:');
                    console.error('📊 [ERROR] XHR status:', xhr.status);
                    console.error('📊 [ERROR] Status text:', status);
                    console.error('📊 [ERROR] Error message:', error);
                    console.error('📊 [ERROR] Response text:', xhr.responseText);
                    console.error('📊 [ERROR] Full XHR object:', xhr);
                    showNotificationModal('Error', 'Failed to approve review. Please try again.', 'error');
                },
                complete: function() {
                    
                    // Reset button state
                    $row.find('.aysa-btn-approve-automated').prop('disabled', false).text('Apply');
                }
            });
        });
        
        $(document).on('click', '.aysa-btn-reject-automated', function() {
            const $row = $(this).closest('tr');
            const reviewId = $row.data('review-id');
            const postTitle = $row.find('td:eq(0)').text().trim();
            
            
            
            // Show loading state
            $(this).prop('disabled', true).text('Rejecting...');
            
            // Make AJAX call to reject
            $.ajax({
                url: ajaxurl,
                type: 'POST',
                data: {
                     action: 'aysa_reject_automated_review',
                     review_id: reviewId,
                     nonce: getAutomationNonce()
                 },
                success: function(response) {
                    if (response.success) {
                        // Remove from pending reviews table
                        $row.remove();
                        
                        // Show success message
                        showNotificationModal('Info', `SEO suggestions rejected for "${postTitle}"`, 'info');
                    } else {
                        console.error('Failed to reject automated review:', response.data);
                        showNotificationModal('Error', 'Failed to reject review: ' + (response.data || 'Unknown error'), 'error');
                    }
                },
                error: function(xhr, status, error) {
                    console.error('AJAX error rejecting automated review:', error);
                    showNotificationModal('Error', 'Failed to reject review. Please try again.', 'error');
                },
                complete: function() {
                    // Reset button state
                    $row.find('.aysa-btn-reject-automated').prop('disabled', false).text('Reject');
                }
            });
        });
        
        // Bulk Actions for Pending Reviews
        $('#approve-all-pending-reviews').on('click', function() {
            const $btn = $(this);
            const $status = $('#bulk-action-status');
            
            // Check if there are any pending reviews
            const pendingRows = $('#aysa-pending-reviews-table tbody tr:not(.aysa-loading-row)');
            if (pendingRows.length === 0) {
                showNotificationModal('Info', 'No pending reviews to approve.', 'info');
                return;
            }
            
            // Confirm action
            if (!confirm('Are you sure you want to approve all pending reviews? This action cannot be undone.')) {
                return;
            }
            
            // Show loading state
            $btn.prop('disabled', true).text('Approving All...');
            $status.text('Processing...').show();
            
            let processedCount = 0;
            let successCount = 0;
            let errorCount = 0;
            const totalCount = pendingRows.length;
            
            // Process each pending review
            pendingRows.each(function(index) {
                const $row = $(this);
                const reviewId = $row.data('review-id');
                const keyword = $row.data('keyword');
                const entityType = $row.data('entity-type');
                const associationId = $row.data('association-id');
                const seoResult = $row.data('seo-result');
                
                // Update status
                $status.text(`Processing ${processedCount + 1} of ${totalCount}...`);
                
                // If it's an automated review (has review-id)
                if (reviewId) {
                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'aysa_approve_automated_review',
                            review_id: reviewId,
                            nonce: getAutomationNonce()
                        },
                        success: function(response) {
                            processedCount++;
                            if (response.success) {
                                successCount++;
                                $row.remove();
                            } else {
                                errorCount++;
                                console.error('Failed to approve review:', response.data);
                            }
                            
                            // Check if all are processed
                            if (processedCount === totalCount) {
                                completeBulkAction($btn, $status, successCount, errorCount, totalCount, 'approved');
                            }
                        },
                        error: function(xhr, status, error) {
                            processedCount++;
                            errorCount++;
                            console.error('AJAX error approving review:', error);
                            
                            if (processedCount === totalCount) {
                                completeBulkAction($btn, $status, successCount, errorCount, totalCount, 'approved');
                            }
                        }
                    });
                } else {
                    // If it's a manual review (no review-id), apply directly
                    applySEOChanges(associationId, keyword, entityType, seoResult)
                        .then(function() {
                            processedCount++;
                            successCount++;
                            $row.remove();
                            
                            if (processedCount === totalCount) {
                                completeBulkAction($btn, $status, successCount, errorCount, totalCount, 'approved');
                            }
                        })
                        .catch(function(error) {
                            processedCount++;
                            errorCount++;
                            console.error('Failed to apply SEO changes:', error);
                            
                            if (processedCount === totalCount) {
                                completeBulkAction($btn, $status, successCount, errorCount, totalCount, 'approved');
                            }
                        });
                }
            });
        });
        
        $('#reject-all-pending-reviews').on('click', function() {
            const $btn = $(this);
            const $status = $('#bulk-action-status');
            
            // Check if there are any pending reviews
            const pendingRows = $('#aysa-pending-reviews-table tbody tr:not(.aysa-loading-row)');
            if (pendingRows.length === 0) {
                showNotificationModal('Info', 'No pending reviews to reject.', 'info');
                return;
            }
            
            // Confirm action
            if (!confirm('Are you sure you want to reject all pending reviews? This action cannot be undone.')) {
                return;
            }
            
            // Show loading state
            $btn.prop('disabled', true).text('Rejecting All...');
            $status.text('Processing...').show();
            
            let processedCount = 0;
            let successCount = 0;
            let errorCount = 0;
            const totalCount = pendingRows.length;
            
            // Process each pending review
            pendingRows.each(function(index) {
                const $row = $(this);
                const reviewId = $row.data('review-id');
                
                // Update status
                $status.text(`Processing ${processedCount + 1} of ${totalCount}...`);
                
                // If it's an automated review (has review-id)
                if (reviewId) {
                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'aysa_reject_automated_review',
                            review_id: reviewId,
                            nonce: getAutomationNonce()
                        },
                        success: function(response) {
                            processedCount++;
                            if (response.success) {
                                successCount++;
                                $row.remove();
                            } else {
                                errorCount++;
                                console.error('Failed to reject review:', response.data);
                            }
                            
                            // Check if all are processed
                            if (processedCount === totalCount) {
                                completeBulkAction($btn, $status, successCount, errorCount, totalCount, 'rejected');
                            }
                        },
                        error: function(xhr, status, error) {
                            processedCount++;
                            errorCount++;
                            console.error('AJAX error rejecting review:', error);
                            
                            if (processedCount === totalCount) {
                                completeBulkAction($btn, $status, successCount, errorCount, totalCount, 'rejected');
                            }
                        }
                    });
                } else {
                    // If it's a manual review, just remove from table
                    processedCount++;
                    successCount++;
                    $row.remove();
                    
                    if (processedCount === totalCount) {
                        completeBulkAction($btn, $status, successCount, errorCount, totalCount, 'rejected');
                    }
                }
            });
        });
        
        // Helper function to complete bulk action
        function completeBulkAction($btn, $status, successCount, errorCount, totalCount, action) {
            // Reset button state
            $btn.prop('disabled', false).text(action === 'approved' ? 'Approve All Pending Reviews' : 'Reject All Pending Reviews');
            
            // Show final status
            let statusText = '';
            let statusClass = '';
            
            if (errorCount === 0) {
                statusText = `All ${totalCount} reviews ${action} successfully!`;
                statusClass = 'success';
            } else if (successCount === 0) {
                statusText = `Failed to ${action} any reviews. Please try again.`;
                statusClass = 'error';
            } else {
                statusText = `${successCount} reviews ${action}, ${errorCount} failed.`;
                statusClass = 'warning';
            }
            
            $status.text(statusText).removeClass('success error warning').addClass(statusClass);
            
            // Hide status after 5 seconds
            setTimeout(function() {
                $status.hide();
            }, 5000);
            
            // Show notification
            if (errorCount === 0) {
                showNotificationModal('Success', `All ${totalCount} pending reviews ${action} successfully!`, 'success');
            } else if (successCount > 0) {
                showNotificationModal('Warning', `${successCount} reviews ${action}, ${errorCount} failed. Check console for details.`, 'warning');
            } else {
                showNotificationModal('Error', `Failed to ${action} any reviews. Please try again.`, 'error');
            }
        }
    });
    
    // Function to update association title in the table without page refresh
    function updateAssociationTitleInTable(associationId, newTitle, entityUrl = null, editUrl = null) {
        const $row = $(`tr[data-id="${associationId}"], #aysa-row-${associationId}`);
        if ($row.length > 0) {
            const $entityCell = $row.find('.aysa-entity-cell');
            if ($entityCell.length > 0) {
                let linkHtml = '';
                
                if (newTitle) {
                    if (entityUrl) {
                        linkHtml = `<a href="${entityUrl}" target="_blank" title="View ${newTitle}">${newTitle}</a>`;
                        
                        // Add edit link if available
                        if (editUrl) {
                            linkHtml += ` <a href="${editUrl}" target="_blank" class="aysa-edit-link" title="Edit ${newTitle}">
                                <span class="dashicons dashicons-edit"></span>
                            </a>`;
                        }
                    } else {
                        linkHtml = `<span title="${newTitle}">${newTitle}</span>`;
                    }
                } else {
                    linkHtml = `<span class="aysa-error">Title not available</span>`;
                }
                
                $entityCell.html(linkHtml);
                console.log(`✅ Updated title for association ${associationId}: ${newTitle}`);
                return true;
            }
        }
        return false;
    }

    // Enhanced function to update association status and refresh entity details
    async function updateAssociationStatusAndRefresh(associationId, status) {
        try {
            // Update status in local storage and database
            storeStatus(associationId, status);
            
            // Update the status in the table row
            const $row = $(`tr[data-id="${associationId}"], #aysa-row-${associationId}`);
            if ($row.length > 0) {
                const $statusSpan = $row.find('.aysa-optimized-status-cell .aysa-status').first();
                $statusSpan.removeClass('good warning bad')
                          .addClass(getStatusClass(status))
                          .text(getStatusDisplayText(status));
                $row.attr('data-status', status.toLowerCase().replace(' ', '-'));
            }
            
            // Update the association in window.aysaAssociations
            if (window.aysaAssociations) {
                const association = window.aysaAssociations.find((a) => parseInt(a.id, 10) === parseInt(associationId, 10));
                if (association) {
                    association.status = status;
                    // Invalidate cache so fresh data is fetched next time
                    invalidateAssociationsCache();
                    // Update cache with new data
                    saveAssociationsToCache(window.aysaAssociations);
                }
            }
            
            // First update the status via AJAX
            await updateAssociationStatus(associationId, status);
            
            // Then refresh the entity details for this specific row
            if ($row.length > 0) {
                const $entityCell = $row.find('.aysa-entity-cell');
                const entityType = $entityCell.data('entity-type');
                const entityId = $entityCell.data('entity-id');
                
                if (entityType && entityId) {
                    // Show loading state
                    $entityCell.html('<span class="aysa-loading">Refreshing...</span>');
                    
                    // Refresh entity details
                    await refreshSingleEntityDetails(entityType, entityId, $entityCell);
                }
            }
            
            return true;
        } catch (error) {
            console.error('Error updating association status and refreshing:', error);
            throw error;
        }
    }
    
    // Helper function to get CSS class for status
    function getStatusClass(status) {
        switch(status) {
            case 'processed':
                return 'good';
            case 'poor':
                return 'bad';
            default:
                return 'warning';
        }
    }
    
    // Helper function to get display text for status
    function getStatusDisplayText(status) {
        switch(status) {
            case 'processed':
                return 'Processed';
            case 'poor':
                return 'Poor';
            default:
                return 'Needs Work';
        }
    }

    // Function to refresh entity details for a single cell
    function refreshSingleEntityDetails(entityType, entityId, $cell) {
        return new Promise((resolve, reject) => {
            $.ajax({
                url: aysa_params.ajax_url,
                type: 'POST',
                data: {
                    action: 'aysa_get_entity_details',
                    nonce: getNonce(),
                    entity_type: entityType,
                    entity_id: entityId
                },
                success: function(response) {
                    if (response.success && response.data) {
                        const entityData = response.data;
                        const $row = $cell.closest('tr');
                        const associationId = parseInt($row.attr('data-id'), 10) || 0;
                        let linkHtml = '';
                        
                        if (entityData.name && entityData.url) {
                            linkHtml = `<a href="${entityData.url}" target="_blank" title="View ${entityData.name}">${entityData.name}</a>`;
                            
                            // Add edit link if available
                            if (entityData.edit_url) {
                                linkHtml += ` <a href="${entityData.edit_url}" target="_blank" class="aysa-edit-link" title="Edit ${entityData.name}">
                                    <span class="dashicons dashicons-edit"></span>
                                </a>`;
                            }
                        } else {
                            linkHtml = `<span class="aysa-error">Entity not found (ID: ${entityId})</span>`;
                        }
                        
                        $cell.html(linkHtml);
                        const $urlCell = $row.find('.aysa-entity-url-cell');
                        if ($urlCell.length) {
                            if (entityData.url) {
                                $urlCell.html(`<a href="${entityData.url}" target="_blank" title="${entityData.url}">${entityData.url}</a>`);
                            } else {
                                $urlCell.html('<span class="aysa-error">URL unavailable</span>');
                            }
                        }
                        const $goBtn = $row.find('.aysa-navigate-to-page').first();
                        if ($goBtn.length) {
                            if (entityData.edit_url) {
                                $goBtn.attr('data-edit-url', entityData.edit_url);
                            } else {
                                $goBtn.attr('data-edit-url', '');
                            }
                        }
                        if (entityData.name && entityData.url) {
                            const storedStatus = getStoredStatus(associationId);
                            applyEntitySeoSnapshotToRow($row, entityData, storedStatus);
                        }
                        console.log(`✅ Refreshed entity details for ${entityType} ID: ${entityId}`);
                        resolve(entityData);
                    } else {
                        $cell.html(`<span class="aysa-error">Error loading entity (ID: ${entityId})</span>`);
                        reject(new Error('Failed to load entity details'));
                    }
                },
                error: function(xhr, status, error) {
                    $cell.html(`<span class="aysa-error">Failed to load entity (ID: ${entityId})</span>`);
                    reject(new Error('AJAX error: ' + error));
                }
            });
        });
    }

    // Function to update association status via AJAX
    async function updateAssociationStatus(associationId, status) {
        return new Promise((resolve, reject) => {
            $.ajax({
                url: aysa_params.ajax_url,
                type: 'POST',
                data: {
                    action: 'aysa_update_status',
                    association_id: associationId,
                    status: status,
                    nonce: getNonce()
                },
                success: function(response) {
                    if (response.success) {
                        console.log('✅ [SUCCESS] Association status updated successfully:', response.data);
                        
                        // Update the UI if the association is visible in the table
                        const $row = $(`tr[data-id="${associationId}"], #aysa-row-${associationId}`);
                        if ($row.length > 0) {
                            // Update the status badge in the table
                            const $statusCell = $row.find('.aysa-optimized-status-cell .aysa-status').first();
                            $statusCell.removeClass('warning bad good').addClass('good')
                                      .attr('data-status', status)
                                      .text('Processed');
                            
                            // Update the row's data attribute for filtering
                            $row.attr('data-status', status);
                            
                            // Apply current filters to potentially hide this row
                            if (typeof applyFilters === 'function') {
                                applyFilters();
                            }
                        }
                        
                        // Store status locally
                        if (typeof storeStatus === 'function') {
                            storeStatus(associationId, status);
                        }
                        
                        resolve(response.data);
                    } else {
                        console.error('❌ [ERROR] Failed to update association status:', response.data);
                        reject(new Error(response.data || 'Failed to update status'));
                    }
                },
                error: function(xhr, status, error) {
                    console.error('❌ [ERROR] AJAX error updating association status:', error);
                    reject(new Error('AJAX error: ' + error));
                }
            });
        });
    }

})(jQuery);
