<?php
if (!defined('WPINC')) {
    die;
}
?>

<div class="aysa-admin aysa-monitoring-analytics">
    <div class="aysa-page-title">
        <h1>Monitoring & Analytics</h1>
        <div class="aysa-title-right">
            <?php
            if (class_exists('Aysa_Credits_Component')) {
                Aysa_Credits_Component::display();
            } else {
                echo '<div class="aysa-credits-component"><span class="aysa-credits-value">Credits: <span class="aysa-credits-amount"><span class="aysa-loading-spinner"></span></span></span></div>';
            }
            ?>
        </div>
    </div>

    <div class="aysa-monitoring-tabs" role="tablist" aria-label="Monitoring tabs">
        <button type="button" class="aysa-monitoring-tab is-active" data-tab="rank">Rank Monitoring</button>
        <button type="button" class="aysa-monitoring-tab" data-tab="ai">How We Stand in AI</button>
    </div>

    <div class="aysa-content">
        <div class="aysa-monitoring-tab-panel is-active" data-panel="rank">
            <div class="aysa-panel aysa-mon-toolbar-panel">
                <p class="aysa-panel-subtitle">Tracked keywords overview.</p>
                <div id="aysa-mon-inline-alert" class="aysa-mon-inline-alert" hidden></div>
                <div class="aysa-mon-toolbar">
                    <button type="button" class="button button-primary" id="aysa-mon-refresh-data" disabled aria-disabled="true" title="Monitoring refresh requires explicit cost-cap approval.">Refresh requires approval</button>
                    <button type="button" class="button" id="aysa-mon-credits-refresh">Refresh Credits</button>
                </div>
                <p id="aysa-mon-status">Loading tracked keyword data...</p>
                <p id="aysa-mon-credits-detail">Spendable now: -</p>
                <p class="aysa-monitoring-legend">
                    <span class="aysa-monitoring-legend-dot aysa-monitoring-legend-dot--refreshed"></span> Refreshed
                    <span class="aysa-monitoring-legend-dot aysa-monitoring-legend-dot--stale"></span> Not refreshed
                </p>
            </div>

            <div class="aysa-panel aysa-mon-conversation-panel" id="aysa-mon-conversation-panel">
                <div class="aysa-mon-conversation-head">
                    <div>
                        <h2 class="aysa-panel-title">AI Monitoring Assistant</h2>
                        <p class="aysa-panel-subtitle">Use conversation to launch refreshes, inspect stale keywords and check job status.</p>
                    </div>
                    <button type="button" class="button" id="aysa-mon-conversation-reset">Start New Conversation</button>
                </div>
                <div id="aysa-mon-conversation-status" class="aysa-mon-conversation-status">Preparing the monitoring assistant...</div>
                <div id="aysa-mon-conversation-messages" class="aysa-mon-conversation-messages"></div>
                <div id="aysa-mon-conversation-suggested" class="aysa-mon-conversation-suggested"></div>
                <div class="aysa-mon-conversation-form">
                    <textarea id="aysa-mon-conversation-input" rows="3" placeholder="Ask about tracked keywords, stale rows, refresh status or credits..."></textarea>
                    <button type="button" class="button button-primary" id="aysa-mon-conversation-send">Send</button>
                </div>
            </div>

            <div class="aysa-panel" id="aysa-mon-rows-panel">
                <h2 class="aysa-panel-title">Rows</h2>
                <div class="aysa-table-wrap">
                    <table class="widefat striped aysa-monitoring-table">
                        <thead>
                        <tr>
                            <th>Keyword</th>
                            <th>Intent</th>
                            <th>Position</th>
                            <th>SERP Features</th>
                            <th>Traffic</th>
                            <th>Traffic %</th>
                            <th>Volume</th>
                            <th>KD %</th>
                            <th>URL</th>
                            <th>Updated</th>
                        </tr>
                        </thead>
                        <tbody id="aysa-mon-tbody">
                        <tr><td colspan="10">No tracked keywords available yet.</td></tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>

        <div class="aysa-monitoring-tab-panel" data-panel="ai">
            <div class="aysa-panel">
                <div class="aysa-mon-ai-head">
                    <div>
                        <h2 class="aysa-panel-title">How We Stand in AI</h2>
                        <p class="aysa-panel-subtitle">Manual refresh only. Results are cached and stored with history, not fetched live on every page load.</p>
                    </div>
                    <div class="aysa-mon-toolbar">
                        <button type="button" class="button button-primary" id="aysa-mon-ai-refresh" disabled aria-disabled="true" title="AI Monitoring refresh requires explicit cost-cap approval.">Refresh requires approval</button>
                        <button type="button" class="button" id="aysa-mon-ai-history-refresh">Reload History</button>
                    </div>
                </div>
                <div id="aysa-mon-ai-status" class="aysa-mon-ai-status">Loading AI monitoring overview...</div>
                <div id="aysa-mon-ai-inline-alert" class="aysa-mon-inline-alert" hidden></div>
                <div class="aysa-mon-ai-summary-grid">
                    <div class="aysa-mon-ai-summary-card">
                        <span class="aysa-mon-ai-summary-label">Domain</span>
                        <strong id="aysa-mon-ai-domain">-</strong>
                    </div>
                    <div class="aysa-mon-ai-summary-card">
                        <span class="aysa-mon-ai-summary-label">Brand</span>
                        <strong id="aysa-mon-ai-brand">-</strong>
                    </div>
                    <div class="aysa-mon-ai-summary-card">
                        <span class="aysa-mon-ai-summary-label">Visible Signals</span>
                        <strong id="aysa-mon-ai-visible">-</strong>
                    </div>
                    <div class="aysa-mon-ai-summary-card">
                        <span class="aysa-mon-ai-summary-label">Last Refresh</span>
                        <strong id="aysa-mon-ai-updated">-</strong>
                    </div>
                </div>
                <div class="aysa-mon-ai-querypack">
                    <div class="aysa-mon-ai-section-head">
                        <h3 class="aysa-mon-ai-section-title">User Question Pack</h3>
                        <div class="aysa-mon-toolbar">
                            <button type="button" class="button" id="aysa-mon-ai-add-question">Add question</button>
                            <button type="button" class="button" id="aysa-mon-ai-save-pack">Save questions</button>
                        </div>
                    </div>
                    <div id="aysa-mon-ai-query-pack" class="aysa-mon-ai-query-pack-editor"></div>
                </div>
            </div>

            <div class="aysa-panel">
                <div class="aysa-mon-ai-section-head">
                    <h2 class="aysa-panel-title">Question Visibility Overview</h2>
                    <p class="aysa-panel-subtitle">Each row shows what a real user might ask, where the brand ranks across AI sources, and which domains dominate the answer set.</p>
                </div>
                <div class="aysa-table-wrap">
                    <table class="widefat striped aysa-monitoring-table aysa-mon-ai-table">
                        <thead>
                        <tr>
                            <th>Question</th>
                            <th>Type</th>
                            <th>Best Rank</th>
                            <th>Best LLM</th>
                            <th>Visibility</th>
                            <th>LLM Breakdown</th>
                            <th>Context</th>
                            <th>Top Results</th>
                            <th>Inspect</th>
                        </tr>
                        </thead>
                        <tbody id="aysa-mon-ai-items">
                        <tr><td colspan="9">No AI snapshot loaded yet.</td></tr>
                        </tbody>
                    </table>
                </div>
            </div>

            <div class="aysa-panel">
                <div class="aysa-mon-ai-section-head">
                    <h2 class="aysa-panel-title">Snapshot History</h2>
                    <p class="aysa-panel-subtitle">Stored snapshots are used to track how visibility changes between manual refreshes.</p>
                </div>
                <div class="aysa-table-wrap">
                    <table class="widefat striped aysa-monitoring-table aysa-mon-ai-history-table">
                        <thead>
                        <tr>
                            <th>Snapshot</th>
                            <th>Status</th>
                            <th>Visible</th>
                            <th>Errors</th>
                            <th>Cost USD</th>
                            <th>Inspect</th>
                        </tr>
                        </thead>
                        <tbody id="aysa-mon-ai-history">
                        <tr><td colspan="6">No AI history available yet.</td></tr>
                        </tbody>
                    </table>
                </div>
            </div>

            <div class="aysa-panel">
                <div class="aysa-mon-ai-section-head">
                    <h2 class="aysa-panel-title">Detail</h2>
                    <p class="aysa-panel-subtitle">Raw payloads stay cached in the broker and are loaded on demand for drilldown.</p>
                </div>
                <div id="aysa-mon-ai-detail" class="aysa-mon-ai-detail-empty">Select a source row or snapshot to inspect details.</div>
            </div>
        </div>
    </div>
</div>

<script>
(function() {
    const AJAX_URL = <?php echo json_encode(admin_url('admin-ajax.php')); ?>;
    const NONCE = <?php echo json_encode(wp_create_nonce('aysa_nonce')); ?>;
    const CREDITS_NONCE = <?php echo json_encode(wp_create_nonce('aysa_credits_nonce')); ?>;

    const $ = (id) => document.getElementById(id);
    const state = {
        currentTab: 'rank',
        projectId: 0,
        target: '',
        trackedAssociations: [],
        trackedAssociationsRawCount: 0,
        latestTrackedRows: [],
        latestRefreshSnapshot: null,
        latestCreditsSnapshot: null,
        statusPollTimer: null,
        statusPollBusy: false,
        lastJobStatus: 'idle',
        conversationState: null,
        conversationRunId: '',
        conversationPollTimer: null,
        aiOverview: null,
        aiOverviewQuestions: [],
        aiHistory: [],
        aiDetail: null,
        aiRefreshPollTimer: null,
        aiQueryPack: [],
        aiQueryPackDirty: false,
        aiQueryPackSignature: '',
        aiQueryPackPinned: false,
        aiSaveInFlight: null,
        autoRankBootstrapStarted: false
    };
    const REFRESH_BATCH_SIZE = 5;
    const AI_ACTIVE_QUESTIONS_SOFT_LIMIT = 6;

    function escHtml(v) {
        return String(v || '')
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#039;');
    }

    function num(v) {
        if (v === null || v === undefined || v === '') return null;
        if (typeof v === 'number') return Number.isFinite(v) ? v : null;
        const n = Number(String(v).replace(',', '.').replace(/[^0-9.\-]/g, ''));
        return Number.isFinite(n) ? n : null;
    }

    function setActiveTab(tab) {
        state.currentTab = tab === 'ai' ? 'ai' : 'rank';
        document.querySelectorAll('.aysa-monitoring-tab').forEach((btn) => {
            btn.classList.toggle('is-active', btn.dataset.tab === state.currentTab);
        });
        document.querySelectorAll('.aysa-monitoring-tab-panel').forEach((panel) => {
            panel.classList.toggle('is-active', panel.dataset.panel === state.currentTab);
        });
    }

    function setStatus(text, isError) {
        const el = $('aysa-mon-status');
        if (!el) return;
        el.textContent = text;
        el.style.color = isError ? '#b42318' : '#1f2937';
    }

    function setConversationStatus(text, isError) {
        const el = $('aysa-mon-conversation-status');
        if (!el) return;
        el.textContent = text;
        el.style.color = isError ? '#b42318' : '#475467';
    }

    function setAiStatus(text, isError) {
        const el = $('aysa-mon-ai-status');
        if (!el) return;
        el.textContent = text;
        el.style.color = isError ? '#b42318' : '#475467';
    }

    function showInlineAlert(message, type, targetId = 'aysa-mon-inline-alert') {
        const el = $(targetId);
        if (!el) return;
        const alertType = (type === 'error') ? 'error' : (type === 'warning' ? 'warning' : 'success');
        el.textContent = String(message || '');
        el.className = 'aysa-mon-inline-alert aysa-mon-inline-alert--' + alertType;
        el.hidden = false;
    }

    function clearInlineAlert(targetId = 'aysa-mon-inline-alert') {
        const el = $(targetId);
        if (!el) return;
        el.hidden = true;
        el.textContent = '';
        el.className = 'aysa-mon-inline-alert';
    }

    function updateTopRightCreditsLabel(actual) {
        const valueEl = document.querySelector('.aysa-title-right .aysa-credits-value');
        const amountEl = document.querySelector('.aysa-title-right .aysa-credits-amount');
        if (valueEl) {
            const spinner = valueEl.querySelector('.aysa-loading-spinner');
            valueEl.textContent = 'Wallet Credits: ';
            if (amountEl) {
                amountEl.textContent = String(actual);
                valueEl.appendChild(amountEl);
            } else if (spinner) {
                valueEl.appendChild(spinner);
            } else {
                valueEl.appendChild(document.createTextNode(String(actual)));
            }
        } else if (amountEl) {
            amountEl.textContent = String(actual);
        }
    }

    function itemTypes(value) {
        if (Array.isArray(value)) return value;
        if (typeof value === 'string') {
            const s = value.trim();
            if (!s) return [];
            try {
                const parsed = JSON.parse(s);
                if (Array.isArray(parsed)) return parsed;
            } catch (_) {}
            return s.split(',').map(v => v.trim()).filter(Boolean);
        }
        return [];
    }

    function renderItemTypesIcons(value) {
        const types = itemTypes(value);
        if (!types.length) return 'N/A';
        const map = {
            organic: ['dashicons-admin-site-alt3', 'Organic'],
            paid: ['dashicons-money-alt', 'Paid'],
            paid_ad: ['dashicons-money-alt', 'Paid ad'],
            featured_snippet: ['dashicons-editor-quote', 'Featured snippet'],
            local_pack: ['dashicons-location-alt', 'Local pack'],
            local_finder: ['dashicons-location', 'Local finder'],
            knowledge_graph: ['dashicons-welcome-learn-more', 'Knowledge graph'],
            people_also_ask: ['dashicons-editor-help', 'People also ask'],
            related_searches: ['dashicons-search', 'Related searches'],
            images: ['dashicons-format-image', 'Images'],
            image_pack: ['dashicons-format-gallery', 'Image pack'],
            video: ['dashicons-video-alt3', 'Video'],
            videos: ['dashicons-video-alt3', 'Videos'],
            news: ['dashicons-media-document', 'News'],
            shopping: ['dashicons-cart', 'Shopping'],
            map: ['dashicons-location', 'Map'],
            sitelinks: ['dashicons-networking', 'Sitelinks']
        };
        return types.map((rawType) => {
            const key = String(rawType || '').trim().toLowerCase();
            const cfg = map[key] || ['dashicons-marker', rawType];
            const label = escHtml(cfg[1]);
            return '<span class="aysa-itemtype-pill" title="' + label + '">' +
                '<span class="dashicons ' + escHtml(cfg[0]) + ' aysa-itemtype-icon"></span>' +
                '<span class="aysa-itemtype-label">' + label + '</span>' +
            '</span>';
        }).join('');
    }

    function renderItemTypesCompact(value) {
        const types = itemTypes(value);
        if (!types.length) return 'N/A';
        const limited = types.slice(0, 2);
        const full = renderItemTypesIcons(limited);
        const remaining = types.length - limited.length;
        return remaining > 0
            ? full + '<span class="aysa-itemtype-pill aysa-itemtype-pill--more" title="' + escHtml(types.join(', ')) + '">+' + remaining + '</span>'
            : full;
    }

    async function ajax(action, payload) {
        const requestData = { action, nonce: NONCE, ...payload };
        const body = new URLSearchParams(requestData);
        const response = await fetch(AJAX_URL, {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
            body: body.toString()
        });
        const raw = await response.text();
        let json = null;
        try {
            json = JSON.parse(raw);
        } catch (_) {
            throw new Error('HTTP ' + response.status + ' non-JSON response for action ' + action);
        }
        if (!response.ok || !json || !json.success) {
            throw new Error((json && json.data) ? String(json.data) : ('ajax_failed_' + action + ' [http ' + response.status + ']'));
        }
        return json.data;
    }

    function renderRows(rows) {
        const tbody = $('aysa-mon-tbody');
        if (!tbody) return;
        if (!Array.isArray(rows) || !rows.length) {
            tbody.innerHTML = '<tr><td colspan="10">No tracked keywords available yet.</td></tr>';
            return;
        }

        const totalTraffic = rows.reduce((sum, row) => {
            const t = num(row.traffic ?? row.gsc_clicks ?? row.clicks ?? row.sessions);
            return sum + (t && t > 0 ? t : 0);
        }, 0);

        tbody.innerHTML = rows.map((row) => {
            const kw = String(row.keyword || '').trim() || 'N/A';
            const organic = num(row.rank_group ?? row.position ?? row.last_position);
            const traffic = num(row.traffic ?? row.gsc_clicks ?? row.clicks ?? row.sessions);
            const explicitTrafficPct = num(row.traffic_pct ?? row.trafficPercent);
            const trafficPct = explicitTrafficPct !== null
                ? explicitTrafficPct
                : ((totalTraffic > 0 && traffic !== null && traffic > 0) ? (traffic / totalTraffic * 100) : null);
            const volume = num(row.search_volume ?? row.volume);
            const kd = num(row.kd ?? row.keyword_difficulty ?? row.competition);
            const types = renderItemTypesCompact(row.item_types);
            const intent = String(row.intent || '').trim();
            const link = String(row.link || row.found_url || row.url || '').trim();
            const updated = String(row.last_checked_at || row.checked_at || row.tracked_at || '').trim() || (row && row._isRefreshed ? 'N/A' : 'Pending refresh');
            const organicLabel = (organic !== null && organic > 0) ? ('#' + Math.round(organic)) : (row && row._isRefreshed ? 'N/A' : 'Pending');
            const rowClass = row._isRefreshed ? 'aysa-monitoring-row-refreshed' : 'aysa-monitoring-row-stale';

            return '<tr class="' + rowClass + '">' +
                '<td>' + escHtml(kw) + '</td>' +
                '<td>' + (intent ? escHtml(intent) : 'N/A') + '</td>' +
                '<td>' + organicLabel + '</td>' +
                '<td>' + types + '</td>' +
                '<td>' + (traffic !== null ? Math.round(traffic) : 'N/A') + '</td>' +
                '<td>' + (trafficPct !== null ? trafficPct.toFixed(2) + '%' : 'N/A') + '</td>' +
                '<td>' + (volume !== null ? Math.round(volume) : 'N/A') + '</td>' +
                '<td>' + (kd !== null ? kd.toFixed(2) : 'N/A') + '</td>' +
                '<td>' + (link ? ('<a href="' + escHtml(link) + '" target="_blank" rel="noopener">' + escHtml(link) + '</a>') : 'N/A') + '</td>' +
                '<td>' + escHtml(updated) + '</td>' +
                '</tr>';
        }).join('');
    }

    function renderConversationMessages(messages) {
        const wrap = $('aysa-mon-conversation-messages');
        if (!wrap) return;
        wrap.innerHTML = '';
        (Array.isArray(messages) ? messages : []).forEach(function(msg) {
            const item = document.createElement('div');
            item.className = 'aysa-mon-conversation-message is-' + String(msg.role || 'assistant');
            item.textContent = String(msg.message || '');
            wrap.appendChild(item);
        });
        wrap.scrollTop = wrap.scrollHeight;
    }

    function appendConversationMessage(role, text) {
        const wrap = $('aysa-mon-conversation-messages');
        if (!wrap) return;
        const item = document.createElement('div');
        item.className = 'aysa-mon-conversation-message is-' + role;
        item.textContent = String(text || '');
        wrap.appendChild(item);
        wrap.scrollTop = wrap.scrollHeight;
    }

    function renderConversationSuggested(actions) {
        const wrap = $('aysa-mon-conversation-suggested');
        if (!wrap) return;
        wrap.innerHTML = '';
        (Array.isArray(actions) ? actions : []).slice(0, 3).forEach(function(action, index) {
            if (!action || !action.label) return;
            const btn = document.createElement('button');
            btn.type = 'button';
            btn.className = 'button' + (index === 0 ? ' button-primary' : '');
            btn.textContent = String(action.label || '');
            btn.addEventListener('click', function() {
                handleConversationAction(action);
            });
            wrap.appendChild(btn);
        });
    }

    function buildConversationClientState() {
        const staleRows = (Array.isArray(state.latestTrackedRows) ? state.latestTrackedRows : []).filter((row) => !row || !row._isRefreshed).slice(0, 3).map((row) => ({ keyword: String(row.keyword || '') }));
        const snapshot = state.latestRefreshSnapshot || {};
        return {
            tracked_count: Number(state.latestTrackedRows.length || 0),
            refreshed_count: Number((state.latestTrackedRows || []).filter((row) => !!row._isRefreshed).length || 0),
            stale_count: Number((state.latestTrackedRows || []).filter((row) => !row._isRefreshed).length || 0),
            stale_preview: staleRows,
            job_status: String(snapshot.status || state.lastJobStatus || 'idle'),
            job_processed: Number(snapshot.processed || 0),
            job_total: Number(snapshot.total || state.latestTrackedRows.length || 0),
            wallet_balance: Number((state.latestCreditsSnapshot && state.latestCreditsSnapshot.balance_cents) || 0),
            wallet_spendable: Number((state.latestCreditsSnapshot && state.latestCreditsSnapshot.spendable_cents) || 0),
            wallet_reserved_links: Number((state.latestCreditsSnapshot && state.latestCreditsSnapshot.reserved_links_cents) || 0)
        };
    }

    async function monitoringConversationRequest(mode, message, resetSession) {
        await ensureContext();
        const payload = {
            action: 'aysa_conversation_async_start',
            task: 'monitoring',
            nonce: NONCE,
            mode: mode,
            project_id: String(state.projectId),
            conversation_language: 'en',
            client_state: JSON.stringify(buildConversationClientState())
        };
        if (message) payload.user_message = message;
        if (resetSession) payload.reset_session = '1';
        const response = await fetch(AJAX_URL, {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
            body: new URLSearchParams(payload).toString()
        });
        const raw = await response.text();
        let json = null;
        try {
            json = JSON.parse(raw);
        } catch (_) {
            throw new Error('Monitoring conversation returned non-JSON response.');
        }
        if (!response.ok || !json || !json.success) {
            throw new Error((json && json.data && json.data.message) ? String(json.data.message) : 'Monitoring conversation failed.');
        }
        return json.data || {};
    }

    function stopConversationStatusPoll() {
        if (state.conversationPollTimer) {
            clearTimeout(state.conversationPollTimer);
            state.conversationPollTimer = null;
        }
    }

    async function pollMonitoringConversationStatus(mode, message) {
        stopConversationStatusPoll();
        return new Promise((resolve, reject) => {
            const tick = async () => {
                try {
                    const data = await ajax('aysa_conversation_async_status', {
                        task: 'monitoring',
                        project_id: String(state.projectId),
                        conversation_language: 'en',
                        mode: mode,
                        user_message: message || '',
                        client_state: JSON.stringify(buildConversationClientState()),
                        run_id: state.conversationRunId || ''
                    });
                    const status = String((data && data.status) || 'failed');
                    if (status === 'queued' || status === 'running') {
                        setConversationStatus(status === 'queued' ? 'Queued...' : 'Running...');
                        state.conversationPollTimer = setTimeout(tick, 1200);
                        return;
                    }
                    state.conversationRunId = '';
                    if (status === 'completed' && data.result && data.result.result) {
                        resolve(data.result.result);
                        return;
                    }
                    reject(new Error(String((data && data.error) || 'Monitoring conversation failed.')));
                } catch (error) {
                    state.conversationRunId = '';
                    reject(error);
                }
            };
            tick();
        });
    }

    function trackedKey(kw, associatedOn, idIntern) {
        return String(kw || '').trim().toLowerCase() + '|' + String(associatedOn || 'other').trim().toLowerCase() + '|' + String(parseInt(idIntern || 0, 10));
    }

    function hasAssocUrl(assoc) {
        if (!assoc || typeof assoc !== 'object') return false;
        const link = String(assoc.link || assoc.url || '').trim();
        return !!link;
    }

    function buildTrackedIndex(associations) {
        const exact = new Set();
        const byKeyword = new Set();
        (Array.isArray(associations) ? associations : []).forEach((a) => {
            const keyword = String(a && a.keyword ? a.keyword : '').trim();
            if (!keyword) return;
            exact.add(trackedKey(keyword, a.associated_on, a.id_intern));
            byKeyword.add(keyword.toLowerCase());
        });
        return { exact, byKeyword };
    }

    function hasRefreshData(row) {
        if (!row || typeof row !== 'object') return false;
        const updated = String(row.last_checked_at || row.checked_at || row.tracked_at || '').trim();
        if (updated && updated.toUpperCase() !== 'N/A') return true;
        const rank = num(row.rank_group ?? row.position ?? row.last_position);
        const rankAbs = num(row.rank_absolute ?? row.position_absolute);
        const gscPos = num(row.gsc_position ?? row.position_gsc);
        const clicks = num(row.gsc_clicks ?? row.clicks);
        const impressions = num(row.gsc_impressions ?? row.impressions);
        return (rank !== null && rank > 0) || (rankAbs !== null && rankAbs > 0) || (gscPos !== null && gscPos > 0) || (clicks !== null && clicks > 0) || (impressions !== null && impressions > 0);
    }

    function mergeTrackedRows(rows, trackedAssociations) {
        const dbRows = Array.isArray(rows) ? rows : [];
        const tracked = Array.isArray(trackedAssociations) ? trackedAssociations : [];
        const index = buildTrackedIndex(tracked);
        const byExact = new Map();
        const byKeyword = new Map();

        dbRows.forEach((row) => {
            if (!row || typeof row !== 'object') return;
            const exactKey = trackedKey(row.keyword, row.associated_on, row.id_intern);
            const keywordKey = String(row.keyword || '').trim().toLowerCase();
            if (index.exact.has(exactKey)) byExact.set(exactKey, row);
            if (keywordKey && index.byKeyword.has(keywordKey) && !byKeyword.has(keywordKey)) byKeyword.set(keywordKey, row);
        });

        return tracked.map((assoc) => {
            const keyword = String(assoc && assoc.keyword ? assoc.keyword : '').trim();
            const assocOn = String((assoc && assoc.associated_on) || 'other');
            const entityId = Number((assoc && assoc.id_intern) || 0);
            const exactKey = trackedKey(keyword, assocOn, entityId);
            const keywordKey = keyword.toLowerCase();
            const matched = byExact.get(exactKey) || byKeyword.get(keywordKey) || null;
            if (matched) {
                const matchedLink = String(matched.link || matched.found_url || matched.url || '').trim();
                const assocLink = String((assoc && assoc.link) || '').trim();
                return Object.assign({}, matched, {
                    keyword: keyword || matched.keyword || '',
                    associated_on: assocOn,
                    id_intern: entityId,
                    link: matchedLink || assocLink,
                    _isRefreshed: hasRefreshData(matched)
                });
            }

            return {
                keyword: keyword,
                associated_on: assocOn,
                id_intern: entityId,
                rank_group: null,
                position: null,
                last_checked_at: 'Pending refresh',
                _isRefreshed: false,
                link: String((assoc && assoc.link) || '')
            };
        });
    }

    async function ensureContext() {
        if (state.projectId > 0 && state.target && Array.isArray(state.trackedAssociations)) return;
        const projectData = await ajax('aysa_monitoring_get_project_id', {});
        const projectId = Number(projectData && projectData.project_id ? projectData.project_id : 0);
        if (projectId <= 0) throw new Error('Project ID not found');

        const [context, associationsData] = await Promise.all([
            ajax('aysa_monitoring_get_context', {}),
            ajax('aysa_monitoring_get_associations', { project_id: String(projectId) })
        ]);

        state.projectId = projectId;
        state.target = String(context && context.target ? context.target : '');
        const rawAssociations = Array.isArray(associationsData && associationsData.associations) ? associationsData.associations : [];
        state.trackedAssociationsRawCount = rawAssociations.length;
        state.trackedAssociations = rawAssociations.filter(hasAssocUrl);
    }

    async function loadTrackedRowsFromDb(opts) {
        const options = opts || {};
        await ensureContext();
        if (!state.trackedAssociations.length) {
            state.latestTrackedRows = [];
            renderRows([]);
            if (!options.suppressStatus) setStatus('No tracked keywords are currently configured.');
            return { total: 0, refreshed: 0, notRefreshed: 0, rows: [] };
        }

        const dbData = await ajax('aysa_monitoring_rank_latest_from_db', {
            project_id: String(state.projectId),
            target: state.target,
            associations: JSON.stringify(state.trackedAssociations)
        });
        const rows = Array.isArray(dbData && dbData.rows) ? dbData.rows : [];
        const trackedRows = mergeTrackedRows(rows, state.trackedAssociations);
        state.latestTrackedRows = trackedRows;
        const refreshedCount = trackedRows.filter((r) => !!r._isRefreshed).length;
        const excludedWithoutUrl = Math.max(0, Number(state.trackedAssociationsRawCount || 0) - trackedRows.length);
        renderRows(trackedRows);
        if (!options.suppressStatus) {
            const excludedText = excludedWithoutUrl > 0 ? (', ' + excludedWithoutUrl + ' excluded without URL') : '';
            if (trackedRows.length > 0 && refreshedCount === 0) {
                setStatus('Loaded ' + trackedRows.length + ' tracked keywords, but no cached monitoring data is available yet' + excludedText + '. A refresh is needed.');
            } else {
                setStatus('Loaded ' + trackedRows.length + ' tracked keywords (' + refreshedCount + ' refreshed, ' + (trackedRows.length - refreshedCount) + ' not refreshed' + excludedText + ').');
            }
        }
        return { total: trackedRows.length, refreshed: refreshedCount, notRefreshed: trackedRows.length - refreshedCount, rows: trackedRows };
    }

    function setRefreshButtonState(inProgress) {
        const btn = $('aysa-mon-refresh-data');
        if (!btn) return;
        btn.disabled = true;
        btn.textContent = 'Refresh requires approval';
    }

    function setAiRefreshButtonState(inProgress) {
        const btn = $('aysa-mon-ai-refresh');
        if (!btn) return;
        btn.disabled = true;
        btn.textContent = 'Refresh requires approval';
    }

    function setAiQueryPackButtonsState(isBusy) {
        const addBtn = $('aysa-mon-ai-add-question');
        const saveBtn = $('aysa-mon-ai-save-pack');
        if (addBtn) addBtn.disabled = !!isBusy;
        if (saveBtn) saveBtn.disabled = !!isBusy;
    }

    function clearAiRefreshPolling() {
        if (!state.aiRefreshPollTimer) return;
        clearTimeout(state.aiRefreshPollTimer);
        state.aiRefreshPollTimer = null;
    }

    function startAiAsync(task, payload) {
        return ajax('aysa_monitoring_ai_async_start', Object.assign({ task: task }, payload || {}));
    }

    function pollAiAsyncStatus(task, runId, attemptsLeft) {
        const tries = typeof attemptsLeft === 'number' ? attemptsLeft : 40;
        return ajax('aysa_monitoring_ai_async_status', {
            task: task,
            run_id: runId || ''
        }).then(function (data) {
            const status = String((data && data.status) || 'idle');
            if (status === 'completed' && data && data.result) {
                return data.result;
            }
            if (status === 'failed') {
                throw new Error(String((data && data.error) || 'AI monitoring worker failed.'));
            }
            if (tries <= 0) {
                throw new Error('AI monitoring worker timed out.');
            }
            return new Promise(function (resolve, reject) {
                window.setTimeout(function () {
                    pollAiAsyncStatus(task, runId, tries - 1).then(resolve).catch(reject);
                }, 3000);
            });
        });
    }

    async function refreshCredits() {
        if (!state.projectId || state.projectId <= 0) return;
        try {
            const data = await ajax('aysa_wallet_get_snapshot', {
                project_id: String(state.projectId),
                nonce: CREDITS_NONCE
            });
            state.latestCreditsSnapshot = data || {};
            const available = Number(data && data.spendable_cents ? data.spendable_cents : 0);
            const actual = Number(data && data.balance_cents ? data.balance_cents : available);
            const reserved = Number(data && data.reserved_links_cents ? data.reserved_links_cents : 0);
            updateTopRightCreditsLabel(actual);
            const detailEl = $('aysa-mon-credits-detail');
            if (detailEl) {
                detailEl.textContent = 'Spendable now: ' + available + ' (wallet ' + actual + ', reserved for links ' + reserved + ')';
            }
        } catch (_) {
            const detailEl = $('aysa-mon-credits-detail');
            if (detailEl) detailEl.textContent = 'Spendable now: N/A';
        }
    }

    function applyConversationUiAction(action) {
        if (action === 'open_monitoring_rows') {
            setActiveTab('rank');
            var rowsPanel = $('aysa-mon-rows-panel'); if (rowsPanel) rowsPanel.scrollIntoView({ behavior: 'smooth', block: 'start' });
            return;
        }
        if (action === 'open_monitoring_toolbar') {
            setActiveTab('rank');
            var conversationPanel = $('aysa-mon-conversation-panel'); if (conversationPanel) conversationPanel.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
    }

    async function getBackgroundRefreshStatus() {
        await ensureContext();
        return ajax('aysa_monitoring_rank_refresh_status', { project_id: String(state.projectId) });
    }

    function notifyRefreshComplete(message) {
        if (!('Notification' in window)) return;
        if (Notification.permission === 'granted') {
            try { new Notification('Monitoring Refresh Complete', { body: message }); } catch (_) {}
            return;
        }
        if (Notification.permission === 'default') {
            Notification.requestPermission().then((permission) => {
                if (permission === 'granted') {
                    try { new Notification('Monitoring Refresh Complete', { body: message }); } catch (_) {}
                }
            }).catch(() => {});
        }
    }

    function clearStatusPolling() {
        if (!state.statusPollTimer) return;
        clearInterval(state.statusPollTimer);
        state.statusPollTimer = null;
    }

    function renderJobStatus(statusData, snapshot) {
        state.latestRefreshSnapshot = statusData || {};
        const status = String(statusData && statusData.status ? statusData.status : 'idle');
        const processed = Number(statusData && statusData.processed ? statusData.processed : 0);
        const total = Number(statusData && statusData.total ? statusData.total : (snapshot ? snapshot.total : 0));
        const failed = Number(statusData && statusData.failed ? statusData.failed : 0);
        const deferred = Number(statusData && statusData.deferred ? statusData.deferred : 0);
        const runRefreshed = Number(statusData && statusData.refreshed ? statusData.refreshed : 0);
        const dbRefreshed = snapshot ? snapshot.refreshed : runRefreshed;

        if (status === 'running') {
            setRefreshButtonState(true);
            const deferredText = deferred > 0 ? (', ' + deferred + ' deferred') : '';
            setStatus('Background refresh running: ' + processed + '/' + total + ' processed, ' + runRefreshed + ' refreshed in this run (' + dbRefreshed + ' currently available in DB)' + deferredText + '.');
            showInlineAlert('Refresh in progress in background. You can leave this page; status is saved.', 'warning');
            return;
        }

        setRefreshButtonState(false);
        if (status === 'completed') {
            const tailError = (failed > 0 && statusData && statusData.last_error) ? (' Last error: ' + String(statusData.last_error)) : '';
            const deferredText = deferred > 0 ? (', ' + deferred + ' deferred') : '';
            setStatus('Background refresh completed: ' + runRefreshed + '/' + total + ' refreshed in this run (' + dbRefreshed + ' currently available in DB)' + (failed > 0 ? (', ' + failed + ' failed') : '') + deferredText + '.' + tailError, failed > 0);
            showInlineAlert('Background refresh completed: ' + runRefreshed + '/' + total + ' refreshed' + (failed > 0 ? (', ' + failed + ' failed') : '') + deferredText, failed > 0 ? 'warning' : 'success');
            refreshCredits();
            return;
        }
        if (status === 'failed') {
            const err = String(statusData && statusData.last_error ? statusData.last_error : 'Unknown error');
            setStatus('Background refresh failed: ' + err, true);
            showInlineAlert('Background refresh failed: ' + err, 'error');
            return;
        }
        clearInlineAlert();
        setStatus('Loaded ' + (snapshot ? snapshot.total : 0) + ' tracked keywords (' + dbRefreshed + ' refreshed, ' + ((snapshot ? snapshot.total : 0) - dbRefreshed) + ' not refreshed).');
    }

    async function pollBackgroundStatus() {
        if (state.statusPollBusy) return;
        state.statusPollBusy = true;
        try {
            const statusData = await getBackgroundRefreshStatus();
            const snapshot = await loadTrackedRowsFromDb({ suppressStatus: true });
            renderJobStatus(statusData, snapshot);
            const newStatus = String(statusData && statusData.status ? statusData.status : 'idle');
            if (state.lastJobStatus === 'running' && newStatus === 'completed') notifyRefreshComplete('Tracked keywords refresh completed.');
            state.lastJobStatus = newStatus;
            if (newStatus !== 'running') clearStatusPolling();
        } catch (e) {
            setStatus('Could not read background refresh status: ' + String(e && e.message ? e.message : e), true);
        } finally {
            state.statusPollBusy = false;
        }
    }

    function startStatusPolling() {
        if (!state.statusPollTimer) state.statusPollTimer = setInterval(pollBackgroundStatus, 5000);
    }

    async function startBackgroundRefresh() {
        setStatus('Refresh requires explicit cost-cap approval before provider work, scheduler, persistence or billing.', true);
        return;
        await ensureContext();
        if (!state.trackedAssociations.length) {
            renderRows([]);
            setStatus('No tracked keywords are currently configured.');
            return;
        }

        setRefreshButtonState(true);
        setStatus('Starting background refresh...');
        const payload = {
            project_id: String(state.projectId),
            target: state.target,
            batch_size: String(REFRESH_BATCH_SIZE),
            associations: JSON.stringify(state.trackedAssociations.map((a) => ({
                keyword: String(a.keyword || ''),
                associated_on: String(a.associated_on || 'other'),
                id_intern: Number(a.id_intern || 0),
                link: String(a.link || '')
            })))
        };

        const startData = await ajax('aysa_monitoring_rank_refresh_start_background', payload);
        state.lastJobStatus = String(startData && startData.status && startData.status.status ? startData.status.status : 'running');
        startStatusPolling();
        await pollBackgroundStatus();
    }

    async function bootstrapMonitoringConversation(resetSession) {
        setConversationStatus('Preparing the monitoring assistant...');
        const start = await monitoringConversationRequest('bootstrap', '', !!resetSession);
        state.conversationRunId = String(start.run_id || '');
        const result = (String(start.status || '') === 'completed' && start.result && start.result.result)
            ? start.result.result
            : await pollMonitoringConversationStatus('bootstrap', '');
        state.conversationState = result || {};
        renderConversationMessages(result.messages || []);
        renderConversationSuggested(result.suggested_actions || []);
        setConversationStatus('Monitoring assistant ready.');
    }

    async function applyConversationExecutionAction(action) {
        if (action === 'start_refresh' || action === 'refresh_requires_approval') {
            appendConversationMessage('assistant', 'Refresh requires explicit cost-cap approval before provider work, scheduler, persistence or billing.');
            return;
        }
        if (action === 'check_refresh_status') {
            const statusData = await getBackgroundRefreshStatus();
            state.latestRefreshSnapshot = statusData || {};
            appendConversationMessage('assistant', 'Status refresh: ' + String(statusData.status || 'idle') + ' (' + Number(statusData.processed || 0) + '/' + Number(statusData.total || 0) + ').');
            return;
        }
        if (action === 'refresh_credits') {
            await refreshCredits();
            appendConversationMessage('assistant', String(($('aysa-mon-credits-detail') || {}).textContent || 'Credits refreshed.'));
        }
    }

    async function handleConversationAction(action) {
        if (!action || typeof action !== 'object') return;
        if (action.message) {
            const input = $('aysa-mon-conversation-input');
            if (input) input.value = String(action.message || '');
            await sendConversationMessage();
            return;
        }
        if (action.ui_action) applyConversationUiAction(String(action.ui_action || ''));
        if (action.execution_action) await applyConversationExecutionAction(String(action.execution_action || ''));
    }

    async function sendConversationMessage() {
        const input = $('aysa-mon-conversation-input');
        if (!input) return;
        const message = String(input.value || '').trim();
        if (!message) return;
        appendConversationMessage('user', message);
        input.value = '';
        setConversationStatus('AYSA is preparing the next step...');
        const start = await monitoringConversationRequest('message', message, false);
        state.conversationRunId = String(start.run_id || '');
        const result = (String(start.status || '') === 'completed' && start.result && start.result.result)
            ? start.result.result
            : await pollMonitoringConversationStatus('message', message);
        state.conversationState = result || {};
        if (result.assistant_message) appendConversationMessage('assistant', String(result.assistant_message || ''));
        renderConversationSuggested(result.suggested_actions || []);
        if (result.ui_action) applyConversationUiAction(String(result.ui_action || ''));
        if (result.execution_action) await applyConversationExecutionAction(String(result.execution_action || ''));
        setConversationStatus('Monitoring assistant ready.');
    }

    function normalizeAiQuestion(question, allowEmpty = false) {
        if (!question || typeof question !== 'object') return null;
        const text = String(question.text || '').trim();
        if (!text && !allowEmpty) return null;
        return {
            id: String(question.id || Math.random().toString(36).slice(2, 10)),
            text,
            active: question.active !== false,
            type: String(question.type || 'custom'),
            origin: String(question.origin || 'custom')
        };
    }

    function signatureAiQueryPack(queryPack) {
        const normalized = (Array.isArray(queryPack) ? queryPack : [])
            .map((question) => normalizeAiQuestion(question, true))
            .filter(Boolean)
            .map((question) => ({
                text: String(question.text || '').trim(),
                active: question.active !== false,
                type: String(question.type || 'custom'),
                origin: String(question.origin || 'custom')
            }));
        normalized.sort((a, b) => {
            const left = (a.text + '|' + a.type + '|' + a.origin);
            const right = (b.text + '|' + b.type + '|' + b.origin);
            return left.localeCompare(right);
        });
        return JSON.stringify(normalized);
    }

    function setAiQueryPackState(queryPack, options = {}) {
        const items = Array.isArray(queryPack) ? queryPack.map((question) => normalizeAiQuestion(question, true)).filter(Boolean) : [];
        state.aiQueryPack = items;
        state.aiQueryPackSignature = signatureAiQueryPack(items);
        if (Object.prototype.hasOwnProperty.call(options, 'pinned')) {
            state.aiQueryPackPinned = !!options.pinned;
        }
        return items;
    }

    function tryApplyServerAiQueryPack(queryPack, options = {}) {
        const items = Array.isArray(queryPack) ? queryPack.map((question) => normalizeAiQuestion(question, true)).filter(Boolean) : [];
        const incomingSignature = signatureAiQueryPack(items);
        const force = !!options.force;
        if (!force && state.aiQueryPackPinned && state.aiQueryPackSignature && incomingSignature !== state.aiQueryPackSignature) {
            return false;
        }
        setAiQueryPackState(items, { pinned: false });
        return true;
    }

    function renderAiOverviewQuestions(overviewQuestions, queryPack) {
        const activePack = Array.isArray(queryPack) ? queryPack : state.aiQueryPack;
        renderAiItems(buildPendingOverviewQuestions(activePack, overviewQuestions));
    }

    function countActiveAiQuestions(queryPack) {
        return (Array.isArray(queryPack) ? queryPack : []).filter((question) => {
            return question && question.active !== false && String(question.text || '').trim();
        }).length;
    }

    function validateAiQueryPackLimit(queryPack, mode = 'save') {
        const activeCount = countActiveAiQuestions(queryPack);
        if (activeCount <= AI_ACTIVE_QUESTIONS_SOFT_LIMIT) {
            return { ok: true, activeCount };
        }
        const actionLabel = mode === 'refresh' ? 'refresh' : 'save';
        return {
            ok: false,
            activeCount,
            message: 'Too many active AI questions (' + activeCount + '). Keep at most ' + AI_ACTIVE_QUESTIONS_SOFT_LIMIT + ' active questions before ' + actionLabel + '.'
        };
    }

    function markAiQueryPackDirty(isDirty = true) {
        state.aiQueryPackDirty = !!isDirty;
        if (isDirty) {
            state.aiQueryPackPinned = true;
        }
    }

    function collectAiQueryPackFromEditor() {
        const rows = Array.from(document.querySelectorAll('.aysa-mon-ai-query-row'));
        return rows.map((row) => {
            const input = row.querySelector('.aysa-mon-ai-query-text');
            const active = row.querySelector('.aysa-mon-ai-query-active');
            const type = row.querySelector('.aysa-mon-ai-query-type');
            return normalizeAiQuestion({
                id: row.getAttribute('data-question-id') || '',
                text: input ? input.value : '',
                active: !!(active && active.checked),
                type: type ? type.value : 'custom',
                origin: row.getAttribute('data-origin') || 'custom'
            });
        }).filter(Boolean);
    }

    function splitAiQueryPack(items) {
        const auto = [];
        const custom = [];
        const agent = [];
        (Array.isArray(items) ? items : []).forEach((question) => {
            const normalized = normalizeAiQuestion(question, true);
            if (!normalized) return;
            if (normalized.origin === 'agent') {
                agent.push(normalized);
                return;
            }
            if (normalized.origin === 'auto') {
                auto.push(normalized);
                return;
            }
            custom.push(normalized);
        });
        return { auto, custom, agent };
    }

    function renderAiQuestionSection(title, description, questions, originLabel) {
        if (!questions.length) {
            return '' +
                '<div class="aysa-mon-ai-query-group">' +
                    '<div class="aysa-mon-ai-query-group-head">' +
                        '<div>' +
                            '<h4 class="aysa-mon-ai-query-group-title">' + escHtml(title) + '</h4>' +
                            '<p class="aysa-mon-ai-query-group-subtitle">' + escHtml(description) + '</p>' +
                        '</div>' +
                    '</div>' +
                    '<div class="aysa-mon-ai-muted">No questions in this section yet.</div>' +
                '</div>';
        }

        return '' +
            '<div class="aysa-mon-ai-query-group">' +
                '<div class="aysa-mon-ai-query-group-head">' +
                    '<div>' +
                        '<h4 class="aysa-mon-ai-query-group-title">' + escHtml(title) + '</h4>' +
                        '<p class="aysa-mon-ai-query-group-subtitle">' + escHtml(description) + '</p>' +
                    '</div>' +
                '</div>' +
                '<div class="aysa-mon-ai-query-group-body">' +
                    questions.map((question, index) => '' +
                        '<div class="aysa-mon-ai-query-row" data-question-id="' + escHtml(question.id) + '" data-origin="' + escHtml(question.origin) + '">' +
                            '<label class="aysa-mon-ai-query-toggle"><input type="checkbox" class="aysa-mon-ai-query-active" ' + (question.active ? 'checked' : '') + '> active</label>' +
                            '<input type="text" class="regular-text aysa-mon-ai-query-text" value="' + escHtml(question.text) + '">' +
                            '<select class="aysa-mon-ai-query-type">' +
                                ['brand','service','comparison','local','transactional','custom'].map((type) => '<option value="' + escHtml(type) + '" ' + (question.type === type ? 'selected' : '') + '>' + escHtml(type) + '</option>').join('') +
                            '</select>' +
                            '<span class="aysa-mon-ai-query-origin aysa-mon-ai-query-origin--' + escHtml(question.origin) + '">' + escHtml(originLabel) + '</span>' +
                            '<button type="button" class="button-link-delete aysa-mon-ai-query-remove" data-index="' + index + '">Remove</button>' +
                        '</div>').join('') +
                '</div>' +
            '</div>';
    }

    function renderAiQueryPack(queryPack) {
        const wrap = $('aysa-mon-ai-query-pack');
        if (!wrap) return;
        const items = Array.isArray(queryPack) ? queryPack.map((question) => normalizeAiQuestion(question, true)).filter(Boolean) : [];
        state.aiQueryPack = items;
        if (!items.length) {
            wrap.innerHTML = '<span class="aysa-mon-ai-muted">No question pack generated yet.</span>';
            return;
        }
        const grouped = splitAiQueryPack(items);
        wrap.innerHTML =
            renderAiQuestionSection('Suggested questions', 'Auto-generated from the business profile. These can be edited, disabled, or removed.', grouped.auto, 'Suggested') +
            renderAiQuestionSection('Custom questions', 'Questions added manually by the user. These stay saved in the list and are not replaced by auto regeneration.', grouped.custom, 'Custom') +
            (grouped.agent.length ? renderAiQuestionSection('Agent questions', 'Questions added by the AI agent through conversation and saved into the same monitoring list.', grouped.agent, 'Agent') : '');
        wrap.querySelectorAll('.aysa-mon-ai-query-remove').forEach((btn) => {
            btn.addEventListener('click', function() {
                const row = btn.closest('.aysa-mon-ai-query-row');
                if (row) {
                    row.remove();
                    markAiQueryPackDirty(true);
                }
            });
        });
        wrap.querySelectorAll('.aysa-mon-ai-query-text, .aysa-mon-ai-query-type, .aysa-mon-ai-query-active').forEach((field) => {
            const eventName = field.classList.contains('aysa-mon-ai-query-active') ? 'change' : 'input';
            field.addEventListener(eventName, function() {
                markAiQueryPackDirty(true);
            });
            if (eventName !== 'change') {
                field.addEventListener('change', function() {
                    markAiQueryPackDirty(true);
                });
            }
        });
    }

    function renderAiSummary(snapshot, queryPackOverride) {
        const summary = snapshot && snapshot.summary ? snapshot.summary : {};
        const activeCount = Array.isArray(queryPackOverride)
            ? queryPackOverride.filter((question) => question && question.active !== false && String(question.text || '').trim()).length
            : ((snapshot && snapshot.questions && snapshot.questions.length) || 0);
        $('aysa-mon-ai-domain').textContent = String((snapshot && snapshot.domain) || state.target || '-');
        $('aysa-mon-ai-brand').textContent = String((snapshot && snapshot.brand) || '-');
        $('aysa-mon-ai-visible').textContent = String(summary.visible_count ?? 0) + ' / ' + String(activeCount || summary.total_questions || summary.total_items || 0);
        $('aysa-mon-ai-updated').textContent = String((snapshot && snapshot.snapshot_at) || '-');
        if (!state.aiQueryPackDirty) {
            renderAiQueryPack(Array.isArray(queryPackOverride) ? queryPackOverride : ((snapshot && snapshot.query_pack) || []));
        }
    }

    function formatAiVisibilityStatus(status) {
        const value = String(status || 'unknown');
        if (value === 'pending_refresh') return 'Pending refresh';
        if (value === 'refresh_in_progress') return 'Refresh in progress';
        if (value === 'partial_error') return 'Partial error';
        if (value === 'mentioned_indirectly') return 'Mentioned indirectly';
        if (value === 'competing_results_only') return 'Competing results only';
        return value.replace(/_/g, ' ');
    }

    function buildPendingOverviewQuestions(queryPack, existingQuestions) {
        const currentMap = new Map();
        (Array.isArray(existingQuestions) ? existingQuestions : []).forEach((question) => {
            if (!question) return;
            const text = String(question.text || question.query || '').trim();
            if (!text) return;
            currentMap.set(text, question);
        });
        return (Array.isArray(queryPack) ? queryPack : [])
            .filter((question) => question && question.active !== false && String(question.text || '').trim())
            .map((question) => {
                const text = String(question.text || '').trim();
                const existing = currentMap.get(text);
                if (existing) {
                    return Object.assign({}, existing, {
                        id: question.id || existing.id || '',
                        text,
                        type: question.type || existing.type || 'custom',
                        origin: question.origin || existing.origin || 'custom',
                        active: question.active !== false
                    });
                }
                return {
                    id: question.id || Math.random().toString(36).slice(2, 10),
                    text,
                    type: question.type || 'custom',
                    origin: question.origin || 'custom',
                    active: question.active !== false,
                    visibility_status: 'pending_refresh',
                    our_position: null,
                    best_position_source: null,
                    top_results: [],
                    context_summary: 'Not refreshed yet. This saved question is waiting for the next AI refresh.',
                    pending_refresh: true,
                    provider_status: {},
                    responses: [],
                    top_domains: [],
                    sources_hit: [],
                    brand_mentioned: false,
                    brand_domain_cited: false,
                    error_by_source: {},
                    html_available: false
                };
            });
    }

    function resolveAiBestProvider(item) {
        const explicit = String(item && item.best_position_source || '').trim();
        if (explicit) return explicit;
        const rows = item && item.provider_status && typeof item.provider_status === 'object'
            ? Object.entries(item.provider_status)
            : [];
        let bestProvider = '';
        let bestPosition = 0;
        rows.forEach(([provider, row]) => {
            const position = Number(row && row.our_position || 0);
            if (position > 0 && (!bestPosition || position < bestPosition)) {
                bestPosition = position;
                bestProvider = provider;
            }
        });
        return bestProvider;
    }

    function renderAiProviderBreakdownHtml(providerStatus) {
        const rows = providerStatus && typeof providerStatus === 'object' ? Object.entries(providerStatus) : [];
        if (!rows.length) {
            return '<span class="aysa-mon-ai-muted">No provider data yet.</span>';
        }
        return '<div class="aysa-mon-ai-inline-provider-list">' + rows.map(([provider, row]) => {
            const position = Number(row && row.our_position || 0);
            const visibility = String((row && row.visibility_status) || 'unknown');
            const label = position > 0
                ? ('#' + String(position))
                : formatAiVisibilityStatus(visibility);
            return '<span class="aysa-mon-ai-inline-provider-pill">' +
                '<strong>' + escHtml(formatProviderLabel(provider)) + '</strong> ' +
                escHtml(label) +
                '</span>';
        }).join('') + '</div>';
    }

    function renderAiTopResultsHtml(item) {
        const topResults = Array.isArray(item && item.top_results) ? item.top_results.slice(0, 3).map((row) => {
            const domain = row.domain || '';
            const rank = Number(row.position || 0) > 0 ? '#' + String(row.position) : 'N/A';
            return domain ? (rank + ' ' + domain) : '';
        }).filter(Boolean) : [];
        if (!topResults.length) {
            return 'N/A';
        }
        const bestProvider = resolveAiBestProvider(item);
        const prefix = bestProvider ? ('From ' + formatProviderLabel(bestProvider) + ': ') : '';
        return escHtml(prefix + topResults.join(', '));
    }

    function buildAiOverviewContext(item) {
        const bestProvider = resolveAiBestProvider(item);
        const position = Number(item && item.our_position || 0);
        const base = String(item && item.context_summary || '').trim();
        if (item && item.pending_refresh) {
            return base || 'Pending refresh.';
        }
        if (position > 0 && bestProvider) {
            return 'Best rank is #' + String(position) + ' in ' + formatProviderLabel(bestProvider) + '. Open Inspect for provider-by-provider answers.';
        }
        return base || 'Open Inspect for provider-by-provider context.';
    }

    function renderAiItems(items) {
        const tbody = $('aysa-mon-ai-items');
        if (!tbody) return;
        state.aiOverviewQuestions = Array.isArray(items) ? items : [];
        if (!items.length) {
            tbody.innerHTML = '<tr><td colspan="9">No AI questions available yet.</td></tr>';
            return;
        }
        tbody.innerHTML = items.map((item, index) => {
            const position = item.pending_refresh
                ? 'Pending'
                : (Number(item.our_position || 0) > 0 ? '#' + String(item.our_position) : 'Not ranked');
            const bestProvider = resolveAiBestProvider(item);
            const visibilityClass = String(item.visibility_status || 'unknown').toLowerCase().replace(/[^a-z0-9]+/g, '-');
            return '<tr>' +
                '<td>' + escHtml(item.text || item.query || '') + '</td>' +
                '<td>' + escHtml(item.type || 'custom') + '</td>' +
                '<td>' + escHtml(position) + '</td>' +
                '<td>' + escHtml(bestProvider ? formatProviderLabel(bestProvider) : (item.pending_refresh ? 'Pending' : 'Mixed / none')) + '</td>' +
                '<td><span class="aysa-mon-ai-badge aysa-mon-ai-badge--' + escHtml(visibilityClass) + '">' + escHtml(formatAiVisibilityStatus(item.visibility_status || 'unknown')) + '</span></td>' +
                '<td>' + renderAiProviderBreakdownHtml(item.provider_status) + '</td>' +
                '<td>' + escHtml(buildAiOverviewContext(item)) + '</td>' +
                '<td>' + renderAiTopResultsHtml(item) + '</td>' +
                '<td><button type="button" class="button button-small aysa-mon-ai-inspect" data-question-index="' + index + '">Inspect</button></td>' +
                '</tr>';
        }).join('');

        tbody.querySelectorAll('.aysa-mon-ai-inspect').forEach((btn) => {
            btn.addEventListener('click', async function() {
                const questionIndex = Number(btn.getAttribute('data-question-index') || '-1');
                const questions = Array.isArray(items) ? items : [];
                if (questionIndex >= 0 && questions[questionIndex]) {
                    state.aiDetail = questions[questionIndex];
                    renderAiDetail(questions[questionIndex]);
                }
            });
        });
    }

    function renderAiHistory(history) {
        const tbody = $('aysa-mon-ai-history');
        if (!tbody) return;
        const rows = Array.isArray(history) ? history : [];
        if (!rows.length) {
            tbody.innerHTML = '<tr><td colspan="6">No AI history available yet.</td></tr>';
            return;
        }
        tbody.innerHTML = rows.map((row) => {
            const summary = row.summary || {};
            return '<tr>' +
                '<td>' + escHtml(row.snapshot_at || '') + '</td>' +
                '<td>' + escHtml(row.status || '') + '</td>' +
                '<td>' + escHtml(String(summary.visible_count ?? 0)) + ' / ' + escHtml(String(summary.total_items ?? 0)) + '</td>' +
                '<td>' + escHtml(String(summary.error_count ?? 0)) + '</td>' +
                '<td>' + escHtml(String(summary.provider_cost_usd ?? 0)) + '</td>' +
                '<td><button type="button" class="button button-small aysa-mon-ai-history-inspect" data-snapshot-id="' + escHtml(row.snapshot_id) + '">Inspect</button></td>' +
                '</tr>';
        }).join('');

        tbody.querySelectorAll('.aysa-mon-ai-history-inspect').forEach((btn) => {
            btn.addEventListener('click', async function() {
                const snapshotId = Number(btn.getAttribute('data-snapshot-id') || '0');
                if (snapshotId > 0) await loadAiDetail({ snapshot_id: snapshotId });
            });
        });
    }

    function formatProviderLabel(provider) {
        const value = String(provider || '').trim();
        if (value === 'ai_mode') return 'AI_MODE';
        if (value === 'chatgpt_scraper') return 'CHATGPT_SCRAPER';
        if (value === 'llm_responses') return 'LLM_RESPONSES';
        if (value === 'llm_mentions') return 'LLM_MENTIONS';
        return value || 'UNKNOWN';
    }

    function renderAiProviderStatus(providerStatus) {
        const rows = providerStatus && typeof providerStatus === 'object' ? Object.entries(providerStatus) : [];
        if (!rows.length) {
            return '<div class="aysa-mon-ai-muted">No provider breakdown available.</div>';
        }
        return '<div class="aysa-mon-ai-provider-list">' + rows.map(([provider, row]) => {
            const visibility = String((row && row.visibility_status) || 'unknown');
            const error = String((row && row.error) || '');
            const position = Number(row && row.our_position || 0) > 0 ? ('#' + String(row.our_position)) : 'Not ranked';
            const topResults = Array.isArray(row && row.top_results) ? row.top_results.slice(0, 3).map((item) => {
                const domain = item && item.domain ? String(item.domain) : '';
                const rank = Number(item && item.position || 0) > 0 ? ('#' + String(item.position)) : 'N/A';
                return domain ? (rank + ' ' + domain) : '';
            }).filter(Boolean) : [];
            return '' +
                '<div class="aysa-mon-ai-provider-card">' +
                    '<div class="aysa-mon-ai-provider-head">' +
                        '<strong>' + escHtml(formatProviderLabel(provider)) + '</strong>' +
                        '<span class="aysa-mon-ai-badge aysa-mon-ai-badge--' + escHtml(visibility.toLowerCase()) + '">' + escHtml(visibility) + '</span>' +
                    '</div>' +
                    '<div class="aysa-mon-ai-provider-meta"><strong>Our position:</strong> ' + escHtml(position) + '</div>' +
                    (topResults.length ? '<div class="aysa-mon-ai-provider-meta"><strong>Top results:</strong> ' + escHtml(topResults.join(', ')) + '</div>' : '') +
                    (error ? '<div class="aysa-mon-ai-provider-error">' + escHtml(error) + '</div>' : '') +
                '</div>';
        }).join('') + '</div>';
    }

    function renderAiResponseList(responses) {
        const rows = Array.isArray(responses) ? responses.filter(Boolean) : [];
        if (!rows.length) {
            return '<div class="aysa-mon-ai-muted">No answer snippets available.</div>';
        }
        return '<div class="aysa-mon-ai-response-list">' + rows.slice(0, 3).map((row) => {
            const answer = String((row && row.answer) || '').trim();
            const sources = Array.isArray(row && row.sources) ? row.sources.slice(0, 3) : [];
            return '' +
                '<div class="aysa-mon-ai-response-card">' +
                    (answer ? '<p>' + escHtml(answer) + '</p>' : '<p class="aysa-mon-ai-muted">No answer text returned.</p>') +
                    (sources.length ? '<div class="aysa-mon-ai-response-sources">Sources: ' + escHtml(sources.map((s) => s.source_name || s.url || '').filter(Boolean).join(', ')) + '</div>' : '') +
                '</div>';
        }).join('') + '</div>';
    }

    function renderAiDetail(detailResult) {
        const el = $('aysa-mon-ai-detail');
        if (!el) return;
        if (!detailResult) {
            el.className = 'aysa-mon-ai-detail-empty';
            el.textContent = 'Select a source row or snapshot to inspect details.';
            return;
        }
        el.className = 'aysa-mon-ai-detail-ready';
        if (Array.isArray(detailResult.questions)) {
            const summary = detailResult.summary || {};
            el.innerHTML = '' +
                '<div class="aysa-mon-ai-detail-grid">' +
                    '<div class="aysa-mon-ai-detail-card"><span>Snapshot</span><strong>#' + escHtml(detailResult.snapshot_id || '') + '</strong></div>' +
                    '<div class="aysa-mon-ai-detail-card"><span>Status</span><strong>' + escHtml(detailResult.status || '') + '</strong></div>' +
                    '<div class="aysa-mon-ai-detail-card"><span>Visible questions</span><strong>' + escHtml(String(summary.visible_count ?? 0)) + '</strong></div>' +
                    '<div class="aysa-mon-ai-detail-card"><span>Errors</span><strong>' + escHtml(String(summary.error_count ?? 0)) + '</strong></div>' +
                '</div>' +
                '<div class="aysa-mon-ai-detail-section">' +
                    '<h4>Questions in this snapshot</h4>' +
                    '<div class="aysa-mon-ai-detail-question-list">' +
                        detailResult.questions.map((question) => '' +
                            '<div class="aysa-mon-ai-detail-question-item">' +
                                '<div class="aysa-mon-ai-detail-question-title">' + escHtml(question.text || '') + '</div>' +
                                '<div class="aysa-mon-ai-detail-question-meta">' +
                                    '<span class="aysa-mon-ai-badge aysa-mon-ai-badge--' + escHtml(String(question.visibility_status || 'unknown').toLowerCase()) + '">' + escHtml(question.visibility_status || 'unknown') + '</span>' +
                                    '<span>Brand: ' + escHtml(question.brand_mentioned ? 'Mentioned' : 'No') + '</span>' +
                                    '<span>Domain: ' + escHtml(question.brand_domain_cited ? 'Cited' : 'No') + '</span>' +
                                '</div>' +
                                renderAiProviderStatus(question.provider_status) +
                            '</div>').join('') +
                    '</div>' +
                '</div>' +
                '<details class="aysa-mon-ai-debug"><summary>Raw debug</summary><pre>' + escHtml(JSON.stringify(detailResult, null, 2)) + '</pre></details>';
            return;
        }

        const topDomains = Array.isArray(detailResult.top_domains) ? detailResult.top_domains.slice(0, 6).map((row) => row.domain || row.source_name || '').filter(Boolean) : [];
        const topResults = Array.isArray(detailResult.top_results) ? detailResult.top_results.slice(0, 6).map((row) => {
            const domain = row.domain || '';
            const rank = Number(row.position || 0) > 0 ? '#' + String(row.position) : 'N/A';
            return domain ? (rank + ' ' + domain) : '';
        }).filter(Boolean) : [];
        const bestProvider = resolveAiBestProvider(detailResult);
        const sourcesHit = Array.isArray(detailResult.sources_hit) ? detailResult.sources_hit : [];
        const errorBySource = detailResult.error_by_source && typeof detailResult.error_by_source === 'object' ? Object.entries(detailResult.error_by_source) : [];
        el.innerHTML = '' +
            '<div class="aysa-mon-ai-detail-grid">' +
                '<div class="aysa-mon-ai-detail-card aysa-mon-ai-detail-card--wide"><span>Question</span><strong>' + escHtml(detailResult.text || detailResult.query || '') + '</strong></div>' +
                '<div class="aysa-mon-ai-detail-card"><span>Type</span><strong>' + escHtml(detailResult.type || 'custom') + '</strong></div>' +
                '<div class="aysa-mon-ai-detail-card"><span>Our position</span><strong>' + escHtml(Number(detailResult.our_position || 0) > 0 ? ('#' + String(detailResult.our_position)) : 'Not ranked') + '</strong></div>' +
                '<div class="aysa-mon-ai-detail-card"><span>Best LLM</span><strong>' + escHtml(bestProvider ? formatProviderLabel(bestProvider) : 'Mixed / none') + '</strong></div>' +
                '<div class="aysa-mon-ai-detail-card"><span>Visibility</span><strong>' + escHtml(detailResult.visibility_status || 'unknown') + '</strong></div>' +
                '<div class="aysa-mon-ai-detail-card"><span>Brand</span><strong>' + escHtml(detailResult.brand_mentioned ? 'Mentioned' : 'Not mentioned') + '</strong></div>' +
                '<div class="aysa-mon-ai-detail-card"><span>Domain</span><strong>' + escHtml(detailResult.brand_domain_cited ? 'Cited' : 'Not cited') + '</strong></div>' +
            '</div>' +
            '<div class="aysa-mon-ai-detail-section">' +
                '<h4>Top results</h4>' +
                '<p>' + (topResults.length ? escHtml((bestProvider ? ('From ' + formatProviderLabel(bestProvider) + ': ') : '') + topResults.join(', ')) : '<span class="aysa-mon-ai-muted">No ordered results extracted.</span>') + '</p>' +
            '</div>' +
            '<div class="aysa-mon-ai-detail-section">' +
                '<h4>Where we appear</h4>' +
                '<p>' + (sourcesHit.length ? escHtml(sourcesHit.join(', ')) : '<span class="aysa-mon-ai-muted">No direct brand-domain citations in this question.</span>') + '</p>' +
            '</div>' +
            '<div class="aysa-mon-ai-detail-section">' +
                '<h4>Top competing domains</h4>' +
                '<p>' + (topDomains.length ? escHtml(topDomains.join(', ')) : '<span class="aysa-mon-ai-muted">No competing domains extracted.</span>') + '</p>' +
            '</div>' +
            '<div class="aysa-mon-ai-detail-section">' +
                '<h4>Provider breakdown</h4>' +
                renderAiProviderStatus(detailResult.provider_status) +
            '</div>' +
            '<div class="aysa-mon-ai-detail-section">' +
                '<h4>Answer excerpts</h4>' +
                renderAiResponseList(detailResult.responses) +
            '</div>' +
            (errorBySource.length ? '<div class="aysa-mon-ai-detail-section"><h4>Errors</h4><ul class="aysa-mon-ai-error-list">' + errorBySource.map(([source, error]) => '<li><strong>' + escHtml(formatProviderLabel(source)) + ':</strong> ' + escHtml(String(error || '')) + '</li>').join('') + '</ul></div>' : '') +
            '<details class="aysa-mon-ai-debug"><summary>Raw debug</summary><pre>' + escHtml(JSON.stringify(detailResult, null, 2)) + '</pre></details>';
    }

    async function loadAiOverview() {
        setAiStatus('Loading AI monitoring overview...');
        clearInlineAlert('aysa-mon-ai-inline-alert');
        const start = await startAiAsync('overview', {});
        const data = (start && start.status === 'completed' && start.result)
            ? start.result
            : await pollAiAsyncStatus('overview', start && start.run_id ? start.run_id : '', 40);
        state.aiOverview = data || null;
        const serverQueryPack = Array.isArray(data && data.query_pack) ? data.query_pack : [];
        if (!state.aiQueryPackDirty) {
            tryApplyServerAiQueryPack(serverQueryPack, { force: !state.aiQueryPackPinned || !state.aiQueryPackSignature });
        }
        const overviewQuestions = Array.isArray(data && data.overview_questions) ? data.overview_questions : [];
        state.aiOverviewQuestions = overviewQuestions;
        if (data && data.snapshot) {
            renderAiSummary(data.snapshot, state.aiQueryPack);
            renderAiOverviewQuestions(overviewQuestions, state.aiQueryPack);
            state.aiHistory = Array.isArray(data.history) ? data.history : [];
            renderAiHistory(state.aiHistory);
            setAiStatus('AI monitoring snapshot loaded.');
        } else {
            if (!state.aiQueryPackDirty) {
                renderAiSummary(null, state.aiQueryPack);
            } else {
                $('aysa-mon-ai-domain').textContent = String((data && data.domain) || state.target || '-');
                $('aysa-mon-ai-brand').textContent = String((data && data.brand) || '-');
                const activeCount = Array.isArray(state.aiQueryPack) ? state.aiQueryPack.filter((question) => question && question.active !== false && String(question.text || '').trim()).length : 0;
                $('aysa-mon-ai-visible').textContent = '0 / ' + String(activeCount);
                $('aysa-mon-ai-updated').textContent = '-';
            }
            renderAiOverviewQuestions(overviewQuestions, state.aiQueryPack);
            if (!state.aiQueryPackDirty) {
                renderAiQueryPack(state.aiQueryPack);
            }
            renderAiHistory(Array.isArray(data && data.history) ? data.history : []);
            setAiStatus(overviewQuestions.length ? 'Questions saved. Run a manual refresh to populate their AI rankings.' : 'No AI snapshot stored yet. Run a manual refresh to create the first cached snapshot.');
        }
    }

    async function pollAiRefreshStatus() {
        try {
            const data = await ajax('aysa_monitoring_get_ai_refresh_status', {});
            const status = String((data && data.status) || 'idle');

            if (status === 'queued' || status === 'running') {
                const processedSources = Number((data && data.processed_sources) || 0);
                const totalSources = Number((data && data.total_sources) || 0);
                const rawMessage = String((data && data.message) || 'Refreshing AI monitoring snapshot...');
                const progressToken = totalSources > 0 ? (processedSources + '/' + totalSources + ' sources processed.') : '';
                const messageAlreadyHasProgress = progressToken && rawMessage.indexOf(progressToken) !== -1;
                const progressLabel = progressToken && !messageAlreadyHasProgress ? (' ' + progressToken) : '';
                setAiStatus(rawMessage + progressLabel);
                const serverQueryPack = Array.isArray(data && data.query_pack) ? data.query_pack : [];
                if (!state.aiQueryPackDirty && serverQueryPack.length && tryApplyServerAiQueryPack(serverQueryPack)) {
                    renderAiQueryPack(state.aiQueryPack);
                }
                if (Array.isArray(data && data.overview_questions)) {
                    renderAiOverviewQuestions(data.overview_questions, state.aiQueryPack);
                }
                state.aiRefreshPollTimer = window.setTimeout(pollAiRefreshStatus, 5000);
                return;
            }

            if (data && data.snapshot) {
                if (!state.aiQueryPackDirty) {
                    renderAiSummary(data.snapshot, state.aiQueryPack);
                } else {
                    $('aysa-mon-ai-domain').textContent = String((data.snapshot.domain) || state.target || '-');
                    $('aysa-mon-ai-brand').textContent = String((data.snapshot.brand) || '-');
                    $('aysa-mon-ai-visible').textContent = String((data.snapshot.summary && data.snapshot.summary.visible_count) ?? 0) + ' / ' + String((data.snapshot.questions && data.snapshot.questions.length) || (data.snapshot.summary && data.snapshot.summary.total_items) || 0);
                    $('aysa-mon-ai-updated').textContent = String((data.snapshot.snapshot_at) || '-');
                }
            }
            if (Array.isArray(data && data.overview_questions)) {
                renderAiOverviewQuestions(data.overview_questions, state.aiQueryPack);
            }
            if (Array.isArray(data && data.history)) {
                state.aiHistory = data.history;
                renderAiHistory(state.aiHistory);
            }

            clearAiRefreshPolling();
            setAiRefreshButtonState(false);

            if (status === 'completed') {
                showInlineAlert('AI monitoring snapshot refreshed and cached successfully.', 'success', 'aysa-mon-ai-inline-alert');
                setAiStatus('AI monitoring snapshot refreshed.');
                await loadAiOverview();
                return;
            }

            if (status === 'failed') {
                showInlineAlert(String((data && (data.error || data.message)) || 'AI refresh failed.'), 'error', 'aysa-mon-ai-inline-alert');
                setAiStatus('AI refresh failed.', true);
                return;
            }

            setAiStatus(String((data && data.message) || 'AI refresh idle.'));
        } catch (e) {
            clearAiRefreshPolling();
            setAiRefreshButtonState(false);
            showInlineAlert(String(e && e.message ? e.message : e), 'error', 'aysa-mon-ai-inline-alert');
            setAiStatus('AI refresh failed.', true);
        }
    }

    async function refreshAiSnapshot() {
        showInlineAlert('AI Monitoring refresh requires explicit cost-cap approval before provider work, scheduler, persistence or billing.', 'warning', 'aysa-mon-ai-inline-alert');
        setAiStatus('Refresh requires explicit cost-cap approval.', true);
        setAiRefreshButtonState(false);
        return;
        setAiRefreshButtonState(true);
        clearAiRefreshPolling();
        setAiStatus('Queueing AI monitoring refresh...');
        try {
            if (state.aiSaveInFlight) {
                await state.aiSaveInFlight;
            }
            if (state.aiQueryPackDirty) {
                await saveAiQueryPack();
            }
            const outgoingQueryPack = Array.isArray(state.aiQueryPack) ? state.aiQueryPack : [];
            const validation = validateAiQueryPackLimit(outgoingQueryPack, 'refresh');
            if (!validation.ok) {
                showInlineAlert(validation.message, 'warning', 'aysa-mon-ai-inline-alert');
                setAiStatus(validation.message, true);
                setAiRefreshButtonState(false);
                return;
            }
            setAiQueryPackState(outgoingQueryPack, { pinned: true });
            renderAiQueryPack(state.aiQueryPack);
            renderAiOverviewQuestions(state.aiOverviewQuestions, state.aiQueryPack);
            const data = await ajax('aysa_monitoring_start_ai_refresh', { query_pack: JSON.stringify(outgoingQueryPack) });
            showInlineAlert(String((data && data.message) || 'AI refresh queued.'), 'success', 'aysa-mon-ai-inline-alert');
            setAiStatus(String((data && data.message) || 'AI refresh queued.'));
            if (Array.isArray(data && data.query_pack) && tryApplyServerAiQueryPack(data.query_pack)) {
                renderAiQueryPack(state.aiQueryPack);
            }
            if (Array.isArray(data && data.overview_questions)) {
                renderAiOverviewQuestions(data.overview_questions, state.aiQueryPack);
            }
            state.aiRefreshPollTimer = window.setTimeout(pollAiRefreshStatus, 2500);
        } catch (e) {
            showInlineAlert(String(e && e.message ? e.message : e), 'error', 'aysa-mon-ai-inline-alert');
            setAiStatus('AI refresh failed.', true);
            setAiRefreshButtonState(false);
        }
    }

    async function loadAiHistory() {
        const start = await startAiAsync('history', { limit: '12' });
        const data = (start && start.status === 'completed' && start.result)
            ? start.result
            : await pollAiAsyncStatus('history', start && start.run_id ? start.run_id : '', 30);
        state.aiHistory = Array.isArray(data && data.history) ? data.history : [];
        renderAiHistory(state.aiHistory);
    }

    async function loadAiDetail(params) {
        const payload = {};
        if (params.item_id) payload.item_id = String(params.item_id);
        if (params.snapshot_id) payload.snapshot_id = String(params.snapshot_id);
        const start = await startAiAsync('detail', payload);
        const data = (start && start.status === 'completed' && start.result)
            ? start.result
            : await pollAiAsyncStatus('detail', start && start.run_id ? start.run_id : '', 40);
        state.aiDetail = data || null;
        renderAiDetail(data.detail || data.snapshot || data);
    }

    async function saveAiQueryPack() {
        if (state.aiSaveInFlight) {
            return state.aiSaveInFlight;
        }
        state.aiSaveInFlight = (async () => {
            setAiQueryPackButtonsState(true);
            try {
                const queryPack = collectAiQueryPackFromEditor();
                const validation = validateAiQueryPackLimit(queryPack, 'save');
                if (!validation.ok) {
                    showInlineAlert(validation.message, 'warning', 'aysa-mon-ai-inline-alert');
                    setAiStatus(validation.message, true);
                    return;
                }
                setAiQueryPackState(queryPack, { pinned: true });
                markAiQueryPackDirty(false);
                renderAiQueryPack(state.aiQueryPack);
                renderAiOverviewQuestions(state.aiOverviewQuestions, state.aiQueryPack);
                setAiStatus('Saving AI question pack...');
                const data = await ajax('aysa_monitoring_save_ai_query_pack', { query_pack: JSON.stringify(queryPack) });
                if (Array.isArray(data && data.query_pack)) {
                    tryApplyServerAiQueryPack(data.query_pack, { force: true });
                } else {
                    setAiQueryPackState(queryPack, { pinned: true });
                }
                renderAiQueryPack(state.aiQueryPack);
                if (Array.isArray(data && data.overview_questions)) {
                    renderAiOverviewQuestions(data.overview_questions, state.aiQueryPack);
                }
                if (data && data.snapshot) {
                    renderAiSummary(data.snapshot, state.aiQueryPack);
                    state.aiHistory = Array.isArray(data.history) ? data.history : state.aiHistory;
                    renderAiHistory(state.aiHistory);
                } else {
                    $('aysa-mon-ai-domain').textContent = String((data && data.domain) || state.target || '-');
                    $('aysa-mon-ai-brand').textContent = String((data && data.brand) || '-');
                    $('aysa-mon-ai-visible').textContent = '0 / ' + String(state.aiQueryPack.filter((question) => question && question.active !== false && String(question.text || '').trim()).length);
                    $('aysa-mon-ai-updated').textContent = '-';
                }
                showInlineAlert('AI question pack saved.', 'success', 'aysa-mon-ai-inline-alert');
                setAiStatus('AI question pack saved. New or changed questions are pending until the next AI refresh.');
            } finally {
                setAiQueryPackButtonsState(false);
                state.aiSaveInFlight = null;
            }
        })();
        return state.aiSaveInFlight;
    }

    function addAiQuestionRow() {
        const queryPack = collectAiQueryPackFromEditor();
        queryPack.push({
            id: Math.random().toString(36).slice(2, 10),
            text: '',
            active: true,
            type: 'custom',
            origin: 'custom'
        });
        markAiQueryPackDirty(true);
        renderAiQueryPack(queryPack);
        const customInputs = Array.from(document.querySelectorAll('.aysa-mon-ai-query-group .aysa-mon-ai-query-text'));
        if (customInputs.length) {
            const input = customInputs[customInputs.length - 1];
            if (input && typeof input.focus === 'function') {
                input.focus();
                if (typeof input.select === 'function') input.select();
            }
        }
    }

    document.addEventListener('DOMContentLoaded', function() {
        document.querySelectorAll('.aysa-monitoring-tab').forEach((btn) => {
            btn.addEventListener('click', function() {
                setActiveTab(btn.dataset.tab || 'rank');
            });
        });

        if ($('aysa-mon-refresh-data')) $('aysa-mon-refresh-data').addEventListener('click', async function() {
            try {
                await startBackgroundRefresh();
            } catch (e) {
                setRefreshButtonState(false);
                setStatus('Error starting background refresh: ' + String(e && e.message ? e.message : e), true);
            }
        });
        if ($('aysa-mon-credits-refresh')) $('aysa-mon-credits-refresh').addEventListener('click', async function() {
            await ensureContext();
            await refreshCredits();
        });
        if ($('aysa-mon-conversation-send')) $('aysa-mon-conversation-send').addEventListener('click', async function() {
            try {
                await sendConversationMessage();
            } catch (e) {
                setConversationStatus('Error: ' + String(e && e.message ? e.message : e), true);
            }
        });
        if ($('aysa-mon-conversation-input')) $('aysa-mon-conversation-input').addEventListener('keydown', async function(e) {
            if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
                e.preventDefault();
                try {
                    await sendConversationMessage();
                } catch (err) {
                    setConversationStatus('Error: ' + String(err && err.message ? err.message : err), true);
                }
            }
        });
        if ($('aysa-mon-conversation-reset')) $('aysa-mon-conversation-reset').addEventListener('click', async function() {
            try {
                await bootstrapMonitoringConversation(true);
            } catch (e) {
                setConversationStatus('Error: ' + String(e && e.message ? e.message : e), true);
            }
        });
        if ($('aysa-mon-ai-refresh')) $('aysa-mon-ai-refresh').addEventListener('click', refreshAiSnapshot);
        if ($('aysa-mon-ai-save-pack')) $('aysa-mon-ai-save-pack').addEventListener('click', async function() {
            try {
                await saveAiQueryPack();
            } catch (e) {
                showInlineAlert(String(e && e.message ? e.message : e), 'error', 'aysa-mon-ai-inline-alert');
                setAiStatus('Could not save AI question pack.', true);
            }
        });
        if ($('aysa-mon-ai-add-question')) $('aysa-mon-ai-add-question').addEventListener('click', addAiQuestionRow);
        if ($('aysa-mon-ai-history-refresh')) $('aysa-mon-ai-history-refresh').addEventListener('click', async function() {
            try {
                await loadAiHistory();
                setAiStatus('AI history reloaded.');
            } catch (e) {
                setAiStatus('Could not reload AI history: ' + String(e && e.message ? e.message : e), true);
            }
        });

        (async function() {
            try {
                const initialSnapshot = await loadTrackedRowsFromDb();
                await refreshCredits();
                const statusData = await getBackgroundRefreshStatus();
                state.lastJobStatus = String(statusData && statusData.status ? statusData.status : 'idle');
                state.latestRefreshSnapshot = statusData || {};
                if (state.lastJobStatus === 'running') {
                    startStatusPolling();
                    await pollBackgroundStatus();
                } else if (
                    !state.autoRankBootstrapStarted &&
                    initialSnapshot &&
                    Number(initialSnapshot.total || 0) > 0 &&
                    Number(initialSnapshot.refreshed || 0) === 0
                ) {
                    state.autoRankBootstrapStarted = true;
                    showInlineAlert('No cached monitoring data was found. Starting the first background refresh automatically.', 'warning');
                    await startBackgroundRefresh();
                }
                await bootstrapMonitoringConversation(false);
                await loadAiOverview();
            } catch (e) {
                setStatus('Error loading tracked keywords: ' + String(e && e.message ? e.message : e), true);
                setConversationStatus('Error: ' + String(e && e.message ? e.message : e), true);
                setAiStatus('Error loading AI monitoring: ' + String(e && e.message ? e.message : e), true);
            }
        })();
    });
})();
</script>

<style>
.aysa-monitoring-analytics .aysa-page-title {
    align-items: center;
    display: flex;
    justify-content: space-between;
    margin: 0 0 16px 0;
}
.aysa-monitoring-tabs {
    display: flex;
    gap: 10px;
    margin: 0 0 16px 0;
}
.aysa-monitoring-tab {
    background: #fff;
    border: 1px solid #d0d5dd;
    border-radius: 999px;
    color: #344054;
    cursor: pointer;
    font-weight: 600;
    padding: 10px 16px;
}
.aysa-monitoring-tab.is-active {
    background: #101828;
    border-color: #101828;
    color: #fff;
}
.aysa-monitoring-tab-panel {
    display: none;
}
.aysa-monitoring-tab-panel.is-active {
    display: grid;
    gap: 16px;
}
.aysa-monitoring-analytics .aysa-content {
    display: grid;
    gap: 16px;
}
.aysa-monitoring-analytics .aysa-panel {
    background: #fff;
    border: 1px solid #e4e7ec;
    border-radius: 10px;
    padding: 16px;
}
.aysa-monitoring-analytics .aysa-panel-title {
    color: #101828;
    font-size: 20px;
    margin: 0 0 12px 0;
}
.aysa-monitoring-analytics .aysa-panel-subtitle {
    color: #475467;
    margin: 0 0 12px 0;
}
.aysa-mon-toolbar {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: flex-end;
}
.aysa-mon-ai-head,
.aysa-mon-ai-section-head,
.aysa-mon-conversation-head {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: space-between;
}
.aysa-mon-ai-head,
.aysa-mon-ai-section-head,
.aysa-mon-conversation-head {
    align-items: flex-start;
}
.aysa-mon-ai-section-head {
    margin-bottom: 12px;
}
.aysa-mon-inline-alert {
    border-radius: 8px;
    font-size: 13px;
    margin: 0 0 10px 0;
    padding: 8px 10px;
}
.aysa-mon-inline-alert--success {
    background: #ecfdf3;
    border: 1px solid #abefc6;
    color: #067647;
}
.aysa-mon-inline-alert--warning {
    background: #fffaeb;
    border: 1px solid #fedf89;
    color: #b54708;
}
.aysa-mon-inline-alert--error {
    background: #fef3f2;
    border: 1px solid #fecdca;
    color: #b42318;
}
.aysa-mon-credits-detail,
.aysa-mon-ai-status {
    color: #475467;
    font-size: 13px;
    margin: 8px 0 0 0;
}
.aysa-monitoring-legend {
    color: #475467;
    font-size: 12px;
    margin: 10px 0 0 0;
}
.aysa-monitoring-legend-dot {
    border-radius: 50%;
    display: inline-block;
    height: 10px;
    margin: 0 4px 0 16px;
    width: 10px;
}
.aysa-monitoring-legend-dot--refreshed {
    background: #16a34a;
}
.aysa-monitoring-legend-dot--stale {
    background: #f59e0b;
}
.aysa-monitoring-analytics .aysa-table-wrap {
    overflow-x: auto;
}
.aysa-monitoring-analytics .aysa-monitoring-table {
    min-width: 980px;
}
.aysa-monitoring-analytics .aysa-monitoring-table thead th {
    background: #f8fafc;
    position: sticky;
    top: 0;
    z-index: 15;
}
.aysa-mon-conversation-panel {
    border: 1px solid #d0d5dd;
}
.aysa-mon-conversation-status {
    color: #475467;
    font-size: 13px;
    margin: 8px 0 12px 0;
}
.aysa-mon-conversation-messages {
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-height: 320px;
    overflow: auto;
    padding: 8px 0;
}
.aysa-mon-conversation-message {
    border-radius: 12px;
    line-height: 1.45;
    max-width: 80%;
    padding: 10px 12px;
}
.aysa-mon-conversation-message.is-user {
    align-self: flex-end;
    background: #1d4ed8;
    color: #fff;
}
.aysa-mon-conversation-message.is-assistant {
    align-self: flex-start;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    color: #111827;
}
.aysa-mon-conversation-suggested {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 10px 0 12px 0;
}
.aysa-mon-conversation-form {
    align-items: flex-end;
    display: flex;
    gap: 12px;
}
.aysa-mon-conversation-form textarea {
    min-height: 88px;
    width: 100%;
}
.aysa-mon-ai-summary-grid {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    margin-top: 14px;
}
.aysa-mon-ai-summary-card {
    background: #f8fafc;
    border: 1px solid #e4e7ec;
    border-radius: 10px;
    padding: 14px;
}
.aysa-mon-ai-summary-label {
    color: #475467;
    display: block;
    font-size: 12px;
    margin-bottom: 6px;
    text-transform: uppercase;
}
.aysa-mon-ai-querypack {
    margin-top: 14px;
}
.aysa-mon-ai-section-title {
    color: #101828;
    font-size: 14px;
    margin: 0 0 10px 0;
}
.aysa-mon-ai-query-pack-editor {
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.aysa-mon-ai-query-group {
    border: 1px solid #e4e7ec;
    border-radius: 12px;
    padding: 12px;
    background: #fff;
}
.aysa-mon-ai-query-group-head {
    margin-bottom: 10px;
}
.aysa-mon-ai-query-group-title {
    margin: 0 0 4px 0;
    font-size: 13px;
    color: #101828;
}
.aysa-mon-ai-query-group-subtitle {
    margin: 0;
    font-size: 12px;
    color: #667085;
}
.aysa-mon-ai-query-group-body {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.aysa-mon-ai-query-row {
    align-items: center;
    display: grid;
    gap: 10px;
    grid-template-columns: 84px minmax(0, 1fr) 120px 88px auto;
}
.aysa-mon-ai-query-row .regular-text {
    min-width: 0;
    width: 100%;
}
.aysa-mon-ai-query-toggle {
    color: #475467;
    font-size: 12px;
    white-space: nowrap;
}
.aysa-mon-ai-query-origin {
    align-items: center;
    border-radius: 999px;
    display: inline-flex;
    font-size: 11px;
    font-weight: 600;
    justify-content: center;
    min-height: 28px;
    padding: 0 10px;
}
.aysa-mon-ai-query-origin--auto {
    background: #eef4ff;
    color: #3538cd;
}
.aysa-mon-ai-query-origin--custom {
    background: #ecfdf3;
    color: #027a48;
}
.aysa-mon-ai-query-origin--agent {
    background: #fff7ed;
    color: #c4320a;
}
@media (max-width: 1100px) {
    .aysa-mon-ai-query-row {
        grid-template-columns: 1fr;
        align-items: stretch;
    }
    .aysa-mon-ai-query-row .button-link-delete,
    .aysa-mon-ai-query-row .aysa-mon-ai-query-origin,
    .aysa-mon-ai-query-row .aysa-mon-ai-query-type,
    .aysa-mon-ai-query-row .aysa-mon-ai-query-toggle {
        justify-self: start;
    }
}
.aysa-mon-ai-muted {
    color: #667085;
    font-size: 13px;
}
.aysa-mon-ai-badge {
    border-radius: 999px;
    display: inline-block;
    font-size: 12px;
    font-weight: 700;
    padding: 4px 10px;
    text-transform: capitalize;
}
.aysa-mon-ai-badge--visible,
.aysa-mon-ai-badge--mentioned_indirectly {
    background: #ecfdf3;
    color: #067647;
}
.aysa-mon-ai-badge--competing_results_only,
.aysa-mon-ai-badge--not_visible {
    background: #f2f4f7;
    color: #344054;
}
.aysa-mon-ai-badge--error {
    background: #fef3f2;
    color: #b42318;
}
.aysa-mon-ai-detail-empty,
.aysa-mon-ai-debug pre {
    background: #0f172a;
    border-radius: 10px;
    color: #e2e8f0;
    margin: 0;
    overflow: auto;
    padding: 16px;
}
.aysa-mon-ai-detail-empty {
    background: #f8fafc;
    color: #475467;
}
.aysa-mon-ai-detail-ready {
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.aysa-mon-ai-detail-grid {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.aysa-mon-ai-detail-card {
    background: #f8fafc;
    border: 1px solid #e4e7ec;
    border-radius: 10px;
    padding: 12px 14px;
}
.aysa-mon-ai-detail-card--wide {
    grid-column: span 2;
}
.aysa-mon-ai-detail-card span {
    color: #475467;
    display: block;
    font-size: 12px;
    margin-bottom: 6px;
    text-transform: uppercase;
}
.aysa-mon-ai-detail-section h4 {
    color: #101828;
    font-size: 14px;
    margin: 0 0 8px 0;
}
.aysa-mon-ai-detail-section p {
    margin: 0;
}
.aysa-mon-ai-provider-list,
.aysa-mon-ai-response-list,
.aysa-mon-ai-detail-question-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.aysa-mon-ai-inline-provider-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.aysa-mon-ai-inline-provider-pill {
    background: #f8fafc;
    border: 1px solid #d0d5dd;
    border-radius: 999px;
    color: #344054;
    display: inline-flex;
    font-size: 12px;
    gap: 4px;
    line-height: 1.2;
    padding: 4px 8px;
    white-space: nowrap;
}
.aysa-mon-ai-provider-card,
.aysa-mon-ai-response-card,
.aysa-mon-ai-detail-question-item {
    background: #f8fafc;
    border: 1px solid #e4e7ec;
    border-radius: 10px;
    padding: 12px 14px;
}
.aysa-mon-ai-provider-head,
.aysa-mon-ai-detail-question-meta {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: space-between;
}
.aysa-mon-ai-provider-error {
    color: #b42318;
    font-size: 12px;
    margin-top: 8px;
}
.aysa-mon-ai-response-card p {
    margin: 0 0 8px 0;
}
.aysa-mon-ai-response-sources,
.aysa-mon-ai-detail-question-title {
    color: #344054;
    font-size: 13px;
}
.aysa-mon-ai-error-list {
    margin: 0;
    padding-left: 18px;
}
.aysa-mon-ai-debug summary {
    color: #475467;
    cursor: pointer;
    font-size: 13px;
}
</style>
