/**
 * @file wally-scanner.js
 * @description Full site scan engine for Wally Monitor — runFullSiteScan.
 *              Uses iframes to load each page, delegates analysis to
 *              WallyMonitor.AnalysisModes for rule engine orchestration.
 *              Supports distinct modes: WCAG-only or NBR-only.
 *              Depends on: WallyMonitor.AnalysisModes, WallyMonitor.Stats,
 *              WallyMonitor.Report, WallyMonitor.UI, WallyMonitor.Utils.
 * @module WallyMonitor.Scanner
 * @since 1.3.0
 */
window.WallyMonitor = window.WallyMonitor || {};

(function($) {
    'use strict';

    WallyMonitor.Scanner = WallyMonitor.Scanner || {};

    /** @type {boolean} Whether a scan is currently in progress */
    WallyMonitor.Scanner.isScanning = false;

    /**
     * Roda a análise no site todo com processamento paralelo.
     * Respeita o modo de análise configurado via WallyMonitor.AnalysisModes.
     *
     * @param {string} [overrideMode] - Optional: force a specific analysis mode
     */
    WallyMonitor.Scanner.runFullSiteScan = async function(overrideMode) {
        var escapeHtml = WallyMonitor.Utils.escapeHtml;

        // Proteção contra cliques múltiplos
        if (WallyMonitor.Scanner.isScanning) {
            WallyMonitor.Utils.showNotification('Uma análise já está em andamento. Aguarde a conclusão.', 'warning');
            return;
        }
        
        WallyMonitor.Scanner.isScanning = true;

        // Resolve analysis mode
        var analysisMode = overrideMode || WallyMonitor.AnalysisModes.getCurrentMode();
        var modeConfig   = WallyMonitor.AnalysisModes.getConfig(analysisMode);
        var modeSummary  = WallyMonitor.AnalysisModes.getSummaryText(analysisMode);
        
        const $button = $('#wally-run-full-scan');
        const $progress = $('#wally-full-scan-progress');
        
        // Limpa iframes antigos que possam ter ficado
        $('iframe[src*="' + wallyData.siteUrl + '"]').remove();
        
        // Limpa containers de resultados anteriores
        $('#wally-summary').empty();
        $('#wally-results').empty();
        $('#wally-report-container').hide();
        
        $button.prop('disabled', true).html('<span class="wally-btn-icon">⏳</span> Analisando...');
        $progress.html(
            '<div class="wally-card" style="text-align:center; padding: 24px;">' +
            '<span style="font-size:24px;">🔎</span><br>' +
            'Buscando páginas do site...<br>' +
            '<small style="color: var(--wally-text-sec, #5f6b7a);">Modo: ' + escapeHtml(modeSummary) + '</small>' +
            '</div>'
        );

        /**
         * Descobre URLs do site via fetch client-side (fallback quando AJAX falha).
         * Busca a homepage, extrai links internos publicados.
         * @returns {Promise<string[]>} Lista de URLs encontradas
         */
        const discoverUrlsViaFetch = async () => {
            var siteOrigin = new URL(wallyData.siteUrl).origin;
            var resp = await fetch(wallyData.siteUrl, { credentials: 'same-origin' });
            if (!resp.ok) { throw new Error('HTTP ' + resp.status); }
            var html = await resp.text();
            var parser = new DOMParser();
            var doc = parser.parseFromString(html, 'text/html');
            var links = doc.querySelectorAll('a[href]');
            var found = new Set();
            // Inclui homepage
            found.add(wallyData.siteUrl.replace(/\/$/, '') + '/');
            for (var i = 0; i < links.length; i++) {
                try {
                    var href = new URL(links[i].getAttribute('href'), siteOrigin);
                    // Mesma origem, sem fragmentos, sem admin, sem login, sem feed
                    if (href.origin === siteOrigin &&
                        !href.pathname.match(/\/(wp-admin|wp-login|wp-content|wp-includes|feed|xmlrpc)/) &&
                        !href.pathname.match(/\.(css|js|png|jpg|jpeg|gif|svg|ico|xml|json|txt)$/i) &&
                        !href.search) {
                        found.add(href.origin + href.pathname);
                    }
                } catch (e) { /* ignorar URLs inválidas */ }
            }
            var result = Array.from(found);
            console.log('[Wally Scanner] URLs descobertas via fetch:', result.length, result);
            return result;
        };

        try {
            // ── Busca lista de URLs — AJAX com fallback client-side ──
            // jQuery Deferred não integra bem com async/await em todos os ambientes,
            // por isso envolvemos $.post num Promise nativo explícito.
            let urls;
            try {
                urls = await new Promise(function(resolve, reject) {
                    $.post(wallyData.ajax_url, {
                        action: 'wally_get_site_urls',
                        nonce: wallyData.nonce
                    }).done(function(response) {
                        if (response && response.success && response.data) {
                            resolve(response.data);
                        } else {
                            reject(new Error('Resposta inválida do servidor.'));
                        }
                    }).fail(function(jqXHR, textStatus) {
                        reject(new Error('AJAX error: ' + textStatus + ' (' + jqXHR.status + ')'));
                    });
                });
            } catch (ajaxUrlErr) {
                // Fallback: descobre URLs navegando no site via fetch
                console.warn('[Wally Scanner] AJAX wally_get_site_urls falhou, descobrindo URLs via fetch...', ajaxUrlErr.message);
                urls = await discoverUrlsViaFetch();
                if (!urls || urls.length === 0) {
                    throw new Error('Não foi possível buscar URLs do site (AJAX e fetch falharam).');
                }
            }
            
            // Remove URLs duplicadas
            const uniqueUrls = [...new Set(urls)];
            
            const startTime = Date.now();
            
            // Configurações de concorrência
            const CONCURRENT_LIMIT = 2; // Analisa 2 páginas em paralelo (axe-core em iframes separados)
            const TIMEOUT_MS = 15000; // Timeout de 15s por página
            
            let allViolations = [];
            let analyzedCount = 0;
            let successCount = 0;
            let errorCount = 0;
            let errorUrls = []; // Armazena URLs com erro

            // Atualiza progresso em tempo real
            const updateProgress = () => {
                const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
                const remaining = uniqueUrls.length - analyzedCount;
                const avgTimePerPage = analyzedCount > 0 ? elapsed / analyzedCount : 0;
                const estimatedRemaining = avgTimePerPage > 0 ? (remaining * avgTimePerPage).toFixed(0) : '?';
                
                $progress.html(`
                    <div class="wally-card wally-progress-stats">
                        <div class="progress-bar">
                            <div class="progress-fill" style="width: ${(analyzedCount / uniqueUrls.length * 100).toFixed(1)}%"></div>
                        </div>
                        <p><strong>${analyzedCount}</strong> de <strong>${uniqueUrls.length}</strong> páginas analisadas 
                        (<span style="color: var(--wally-pass, #22c55e);">${successCount} sucesso</span>, 
                        <span style="color: var(--wally-critical, #e5484d);">${errorCount} erro</span>)</p>
                        <p>Tempo decorrido: <strong>${elapsed}s</strong> | Estimativa restante: <strong>${estimatedRemaining}s</strong></p>
                        <p style="font-size: 12px; color: var(--wally-text-sec, #5f6b7a);">📋 ${escapeHtml(modeConfig.label)}</p>
                    </div>
                `);
            };

            /**
             * Analisa HTML injetando num div no documento principal.
             * axe.run funciona porque o div está no mesmo document (sem cross-origin).
             * @param {string} htmlContent - HTML completo da página
             * @param {string} url - URL original da página
             * @param {string} lang - Idioma da página
             * @returns {Object} Resultado da análise
             */
            const analyzeHtmlInDiv = async (htmlContent, url, lang) => {
                var analysisContainer = null;
                var styleContainer = null;
                // ID fixo para que selectors axe sejam iguais entre páginas (dedup funciona)
                var containerId = 'wally-scan-container';
                var savedBodyClasses = document.body.className; // salva para restaurar
                try {
                    analysisContainer = document.createElement('div');
                    analysisContainer.id = containerId;
                    analysisContainer.setAttribute('lang', lang);
                    analysisContainer.style.cssText = 'position:fixed;top:-9999px;left:-9999px;width:1024px;height:768px;overflow:auto;pointer-events:none;z-index:-1;opacity:0.01;';

                    // ── Extrai e injeta estilos da página ──
                    styleContainer = document.createDocumentFragment();
                    var styleElements = [];
                    var headMatch = htmlContent.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
                    if (headMatch) {
                        var headHtml = headMatch[1];
                        var styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
                        var styleMatch;
                        while ((styleMatch = styleRegex.exec(headHtml)) !== null) {
                            var styleEl = document.createElement('style');
                            styleEl.setAttribute('data-wally-scan', 'true');
                            styleEl.textContent = styleMatch[1];
                            styleContainer.appendChild(styleEl);
                            styleElements.push(styleEl);
                        }
                        var linkRegex = /<link[^>]*rel=["']stylesheet["'][^>]*href=["']([^"']+)["'][^>]*>/gi;
                        var linkMatch;
                        while ((linkMatch = linkRegex.exec(headHtml)) !== null) {
                            var linkEl = document.createElement('link');
                            linkEl.rel = 'stylesheet';
                            linkEl.setAttribute('data-wally-scan', 'true');
                            linkEl.href = linkMatch[1];
                            styleContainer.appendChild(linkEl);
                            styleElements.push(linkEl);
                        }
                    }
                    document.head.appendChild(styleContainer);

                    // ── Usa o body COMPLETO da página (sem admin chrome) ──
                    // axe-core precisa da página completa (header, nav, footer)
                    // para detectar problemas reais de acessibilidade.
                    // A deduplicação em wally-report.js lida com duplicatas
                    // entre páginas (mesmo selector = conta 1 vez).
                    var tempDiv = document.createElement('div');
                    var bodyMatch = htmlContent.match(/<body[^>]*>([\s\S]*)<\/body>/i);
                    tempDiv.innerHTML = bodyMatch ? bodyMatch[1] : htmlContent;

                    // Remove apenas admin chrome e scripts (não afetam conteúdo público)
                    ['script', 'noscript', '#wpadminbar', '.skip-link'].forEach(function(sel) {
                        try {
                            var els = tempDiv.querySelectorAll(sel);
                            for (var i = 0; i < els.length; i++) { els[i].remove(); }
                        } catch (e) { /* ignorar */ }
                    });

                    // ── Neutraliza landmarks para evitar falsos positivos ──
                    // A página admin (wp-admin) já possui <main>, <header>, <footer>.
                    // Injetar o conteúdo da página escaneada com esses mesmos elementos
                    // faz o axe-core reportar landmark-no-duplicate-main/banner/contentinfo.
                    // Convertemos para <div> preservando atributos e conteúdo.
                    ['main', 'header', 'footer'].forEach(function(tag) {
                        var landmarks = tempDiv.querySelectorAll(tag);
                        for (var i = 0; i < landmarks.length; i++) {
                            var el = landmarks[i];
                            var div = document.createElement('div');
                            div.setAttribute('data-wally-original-tag', tag);
                            for (var j = 0; j < el.attributes.length; j++) {
                                var attr = el.attributes[j];
                                div.setAttribute(attr.name, attr.value);
                            }
                            while (el.firstChild) {
                                div.appendChild(el.firstChild);
                            }
                            el.parentNode.replaceChild(div, el);
                        }
                    });

                    analysisContainer.innerHTML = tempDiv.innerHTML;

                    // ── Classes do body na <body> real para cascade CSS ──
                    // CSS selectors como "body.wp-embed-responsive .wp-block-quote"
                    // precisam que o <body> tenha as classes da página analisada.
                    // O container está dentro do <body>, então os seletores casam.
                    // Restauramos as classes originais no finally.
                    var bodyClassMatch = htmlContent.match(/<body[^>]*class=["']([^"']+)["']/i);
                    if (bodyClassMatch) {
                        bodyClassMatch[1].split(/\s+/).forEach(function(c) {
                            if (c) { document.body.classList.add(c); }
                        });
                    }

                    document.body.appendChild(analysisContainer);

                    // ── Detecta skip-link injetado via JS (ex.: Twenty Twenty-Five) ──
                    // O scanner remove <script>, então skip links criados via
                    // document.createElement('a') + classList.add('skip-link')
                    // nunca existem no DOM analisado. Marcamos uma flag para que
                    // checkSkipNavigation() não reporte falso positivo.
                    if (/skip[\-_]link|skip[\-_]to[\-_]content|Skip\s+to\s+content|Pular\s+para\s+o\s+conte[uú]do/i.test(htmlContent)) {
                        analysisContainer.setAttribute('data-wally-has-skip-link-script', 'true');
                    }

                    // Aguarda DOM renderizar e stylesheets carregarem
                    // Espera explícita por <link> stylesheets injetadas no head
                    var linkStylesheets = styleElements.filter(function(el) { return el.tagName === 'LINK'; });
                    if (linkStylesheets.length > 0) {
                        await Promise.race([
                            Promise.all(linkStylesheets.map(function(link) {
                                return new Promise(function(resolve) {
                                    if (link.sheet) { resolve(); return; }
                                    link.onload = resolve;
                                    link.onerror = resolve;
                                });
                            })),
                            new Promise(function(r) { setTimeout(r, 3000); }) // max 3s
                        ]);
                    }
                    // Delay adicional para reflow com estilos aplicados
                    await new Promise(function(r) { setTimeout(r, 200); });

                    var analysisResult = await WallyMonitor.AnalysisModes.runAnalysis(analysisContainer, analysisMode);
                    var violations = analysisResult.violations;

                    // Normaliza selectors removendo o prefixo do container,
                    // para que a deduplicação em wally-report.js funcione corretamente.
                    var containerPrefix = '#' + containerId + ' > ';
                    violations.forEach(function(v) {
                        if (v.nodes) {
                            v.nodes.forEach(function(n) {
                                n.pageUrl = url;
                                if (n.target && Array.isArray(n.target)) {
                                    n.target = n.target.map(function(t) {
                                        return typeof t === 'string' ? t.replace(containerPrefix, '') : t;
                                    });
                                }
                            });
                        }
                    });

                    // Coleta estatísticas do body COMPLETO (tempDiv já está limpo).
                    var pageStats = WallyMonitor.Stats.collectPageStats(tempDiv);

                    return { success: true, violations: violations, stats: pageStats, url: url, analysisResult: analysisResult };
                } finally {
                    // Restaura classes originais do <body> da página admin
                    document.body.className = savedBodyClasses;
                    if (analysisContainer && analysisContainer.parentNode) {
                        analysisContainer.parentNode.removeChild(analysisContainer);
                    }
                    // Remove estilos injetados no <head>
                    if (styleElements && styleElements.length) {
                        styleElements.forEach(function(el) {
                            if (el.parentNode) { el.parentNode.removeChild(el); }
                        });
                    }
                }
            };

            /**
             * Busca HTML da página via AJAX server-side (wp_remote_get).
             * Mais confiável que fetch() em ambientes sandboxed (WP Playground).
             * @param {string} url - URL da página a buscar
             * @returns {Promise<string>} HTML da página
             */
            const fetchPageViaAjax = (url) => {
                return new Promise(function(resolve, reject) {
                    $.post(wallyData.ajax_url, {
                        action: 'wally_analyze_url',
                        nonce: wallyData.nonce,
                        url: url
                    }).done(function(response) {
                        if (response.success && response.data && response.data.html) {
                            resolve(response.data.html);
                        } else {
                            reject(new Error(response.data || 'Falha ao buscar HTML'));
                        }
                    }).fail(function(jqXHR, textStatus) {
                        reject(new Error('AJAX error: ' + textStatus));
                    });
                });
            };

            // Helper: tenta análise via AJAX proxy + div (funciona em qualquer ambiente)
            const analyzeViaAjaxFallback = async (url) => {
                var fetchedHtml = await fetchPageViaAjax(url);
                var langMatch = fetchedHtml.match(/<html[^>]*lang=["']([^"']+)["']/i);
                var pageLang = langMatch ? langMatch[1] : 'pt-BR';
                return await analyzeHtmlInDiv(fetchedHtml, url, pageLang);
            };

            // Helper: busca HTML via fetch() do browser (client-side, mesma origem)
            const analyzeViaFetchFallback = async (url) => {
                var resp = await fetch(url, { credentials: 'same-origin' });
                if (!resp.ok) { throw new Error('HTTP ' + resp.status); }
                var html = await resp.text();
                var langMatch = html.match(/<html[^>]*lang=["']([^"']+)["']/i);
                var pageLang = langMatch ? langMatch[1] : 'pt-BR';
                return await analyzeHtmlInDiv(html, url, pageLang);
            };

            // Helper: cadeia de fallbacks — AJAX → fetch → erro
            const analyzeWithFallbacks = async (url, context) => {
                // 1) Tenta AJAX proxy (PHP wp_remote_get — funciona em WP normal)
                try {
                    return await analyzeViaAjaxFallback(url);
                } catch (ajaxErr) {
                    console.warn('[Wally Scanner] AJAX falhou para ' + url + ' (' + context + '):', ajaxErr.message, '→ tentando fetch...');
                }
                // 2) Tenta fetch do browser (mesma origem — funciona em Playground)
                try {
                    return await analyzeViaFetchFallback(url);
                } catch (fetchErr) {
                    console.error('[Wally Scanner] fetch também falhou para ' + url + ':', fetchErr.message);
                    throw fetchErr; // propaga para o chamador tratar
                }
            };

            // Função para analisar uma única URL com timeout
            const analyzeUrl = async (url) => {
                return Promise.race([
                    new Promise(async (resolve) => {
                        const iframe = document.createElement('iframe');
                        iframe.style.display = 'none';
                        iframe.style.position = 'absolute';
                        iframe.style.left = '-9999px';
                        document.body.appendChild(iframe);

                        const timeoutId = setTimeout(() => {
                            if (iframe.parentNode) {
                                document.body.removeChild(iframe);
                            }
                            // Timeout do iframe: tenta fallbacks (AJAX → fetch) antes de desistir
                            console.warn('[Wally Scanner] iframe timeout para ' + url + ', tentando fallbacks...');
                            analyzeWithFallbacks(url, 'timeout').then(function(result) {
                                resolve(result);
                            }).catch(function(err) {
                                resolve({ success: false, error: err.message || 'Timeout', url });
                            });
                        }, TIMEOUT_MS);

                        iframe.onload = async () => {
                            clearTimeout(timeoutId);
                            try {
                                // Verifica se temos acesso ao conteúdo (cross-origin safety)
                                let contentDoc;
                                try {
                                    contentDoc = iframe.contentDocument;
                                } catch (e) {
                                    throw new Error('Cross-origin access denied');
                                }

                                // Verifica se o iframe carregou conteúdo válido
                                if (!contentDoc || !contentDoc.body) {
                                    throw new Error('Documento não carregado');
                                }

                                // Aguarda um pouco para garantir que o DOM está pronto
                                await new Promise(resolve => setTimeout(resolve, 300));

                                // ── Roda análise via AnalysisModes (respeita modo selecionado) ──
                                var analysisResult = await WallyMonitor.AnalysisModes.runAnalysis(contentDoc, analysisMode);
                                var violations = analysisResult.violations;
                                
                                // Adiciona a URL da página aos resultados
                                violations.forEach(function(v) {
                                    if (v.nodes) {
                                        v.nodes.forEach(function(n) { n.pageUrl = url; });
                                    }
                                });
                                
                                // Coleta estatísticas do documento
                                const pageStats = WallyMonitor.Stats.collectPageStats(contentDoc);
                                
                                if (iframe.parentNode) {
                                    document.body.removeChild(iframe);
                                }
                                resolve({ success: true, violations: violations, stats: pageStats, url, analysisResult: analysisResult });
                            } catch (e) {
                                // ── Fallback: busca HTML via AJAX (PHP wp_remote_get) ──
                                // Usa proxy server-side para evitar cross-origin e sandbox issues.
                                // Analisa num div no documento principal (axe.run funciona no mesmo document).
                                console.warn('[Wally Scanner] iframe direto falhou para ' + url + ', tentando fallbacks...', e.message);
                                if (iframe.parentNode) {
                                    document.body.removeChild(iframe);
                                }
                                try {
                                    var result = await analyzeWithFallbacks(url, 'onload');
                                    resolve(result);
                                } catch (fallbackErr) {
                                    resolve({ success: false, error: fallbackErr.message, url });
                                }
                            }
                        };

                        iframe.onerror = () => {
                            clearTimeout(timeoutId);
                            if (iframe.parentNode) {
                                document.body.removeChild(iframe);
                            }
                            // Tenta fallbacks (AJAX → fetch) no onerror
                            analyzeWithFallbacks(url, 'onerror').then(function(result) {
                                resolve(result);
                            }).catch(function(err) {
                                resolve({ success: false, error: err.message || 'Failed to load', url });
                            });
                        };

                        // Tenta carregar a URL
                        try {
                            iframe.src = url;
                        } catch (e) {
                            clearTimeout(timeoutId);
                            if (iframe.parentNode) {
                                document.body.removeChild(iframe);
                            }
                            resolve({ success: false, error: 'Invalid URL', url });
                        }
                    }),
                    // Timeout de segurança adicional (iframe 15s + AJAX fallback 35s)
                    new Promise((resolve) => {
                        setTimeout(() => {
                            resolve({ success: false, error: 'Timeout', url });
                        }, TIMEOUT_MS + 35000);
                    })
                ]);
            };

            // Processa URLs em lotes paralelos
            const processBatch = async (batch) => {
                const results = await Promise.all(batch.map(url => analyzeUrl(url)));
                
                results.forEach(result => {
                    analyzedCount++;
                    if (result.success) {
                        successCount++;
                        Array.prototype.push.apply(allViolations, result.violations);
                        // Agrega estatísticas
                        if (result.stats) {
                            WallyMonitor.Stats.aggregateStats(allStats, result.stats);
                        }
                    } else {
                        errorCount++;
                        errorUrls.push({ url: result.url, error: result.error });
                        console.warn('Erro ao analisar ' + result.url + ': ' + result.error);
                    }
                });
                
                updateProgress();
            };

            // Objeto para agregar estatísticas de todas as páginas
            let allStats = {
                totalImages: 0, imagesWithAlt: 0,
                totalLinks: 0, linksWithText: 0,
                totalButtons: 0, buttonsWithLabel: 0,
                totalFormFields: 0, formFieldsWithLabel: 0,
                totalHeadings: 0, headingsCorrect: 0,
                totalTables: 0, tablesAccessible: 0,
                totalVideos: 0, videosWithCaptions: 0
            };

            // Divide URLs em lotes e processa
            for (let i = 0; i < uniqueUrls.length; i += CONCURRENT_LIMIT) {
                const batch = uniqueUrls.slice(i, i + CONCURRENT_LIMIT);
                await processBatch(batch);
            }

            const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
            
            // Processa violações sem passar documentContext (já coletamos stats no scan)
            const finalReport = WallyMonitor.Report.processAxeResults(allViolations, null);
            
            // Injeta estatísticas agregadas e metadados do modo de análise
            finalReport.summary.elementsStats = allStats;
            finalReport.summary.analysisMode  = analysisMode;
            finalReport.summary.modeLabel     = modeConfig.label;
            finalReport.summary.engines       = WallyMonitor.AnalysisModes.getActiveEngines(modeConfig);
            
            WallyMonitor.UI.displayReport(finalReport);
            WallyMonitor.UI.saveReport(finalReport);

            // Build engine badges
            var engineBadges = finalReport.summary.engines.map(function(e) {
                return '<span class="wally-engine-badge">' + escapeHtml(e) + '</span>';
            }).join(' ');

            $progress.html(`
                <div class="wally-card wally-complete-message">
                    <strong>✅ Análise concluída em ${escapeHtml(totalTime)}s!</strong>
                    <p>${escapeHtml(String(finalReport.summary.totalProblems))} problemas encontrados em ${escapeHtml(String(successCount))} páginas analisadas.</p>
                    <p style="font-size: 12px; color: var(--wally-text-sec, #5f6b7a);">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: -2px;"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1" ry="1"/></svg> Modo: <strong>${escapeHtml(modeConfig.label)}</strong> | 
                        Motores: ${engineBadges}
                    </p>
                    ${errorCount > 0 ? `<p style="color: var(--wally-serious, #e5793a);">⚠ ${escapeHtml(String(errorCount))} páginas com erro (timeout ou falha de carregamento)</p>
                    <details style="margin-top: 10px;">
                        <summary style="cursor: pointer; color: var(--wally-text-sec, #5f6b7a);">Ver páginas com erro</summary>
                        <ul style="margin-top: 10px; color: var(--wally-text-sec, #5f6b7a); font-size: 12px;">
                            ${errorUrls.map(item => `<li><strong>${escapeHtml(item.url)}</strong> - ${escapeHtml(item.error)}</li>`).join('')}
                        </ul>
                    </details>` : ''}
                </div>
            `);

        } catch (error) {
            console.error('Erro na análise completa:', error);
            var errMsg = (error && error.message) ? error.message : (typeof error === 'string' ? error : 'Falha inesperada na análise.');
            $progress.html(`<div class="wally-card" style="border-left: 4px solid var(--wally-critical, #e5484d); padding: 16px;"><strong style="color: var(--wally-critical, #e5484d);">❌ Erro:</strong> ${escapeHtml(errMsg)}</div>`);
        } finally {
            WallyMonitor.Scanner.isScanning = false; // Libera para nova análise
            $button.prop('disabled', false).html('<span class="wally-btn-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></span> Analisar Site Completo');
        }
    };

})(jQuery);
