Back to Repository

GA Patio

0 upvotes
By domanovsky1@gmail.com
Manual (context menu)

Match Pattern

<all_urls>

Script Code

// ============================================
// GA4/GTM ДИАГНОСТИКА v5.1 (FIXED)
// ТОЛЬКО НАБЛЮДЕНИЕ — НЕ ЛОМАЕТ ОТПРАВКУ
// ============================================

(function() {
    if (document.getElementById('ga4-diagnostic-panel')) {
        console.log('ℹ️ Панель диагностики уже активна');
        return;
    }
    
    const STORAGE_KEY = '__ga4_diagnostic_data';
    
    // ========== СПРАВОЧНИК ОБЯЗАТЕЛЬНЫХ ПОЛЕЙ ==========
    const REQUIRED_FIELDS = {
        'view_item': { fields: ['items'], itemFields: ['item_id', 'item_name'] },
        'view_item_list': { fields: ['items'], itemFields: ['item_id', 'item_name'] },
        'select_item': { fields: ['items'], itemFields: ['item_id', 'item_name'] },
        'add_to_cart': { fields: ['items', 'currency', 'value'], itemFields: ['item_id', 'item_name', 'price', 'quantity'] },
        'remove_from_cart': { fields: ['items', 'currency', 'value'], itemFields: ['item_id', 'item_name', 'price', 'quantity'] },
        'add_to_wishlist': { fields: ['items', 'currency', 'value'], itemFields: ['item_id', 'item_name', 'price'] },
        'view_cart': { fields: ['items', 'currency', 'value'], itemFields: ['item_id', 'item_name', 'price', 'quantity'] },
        'begin_checkout': { fields: ['items', 'currency', 'value'], itemFields: ['item_id', 'item_name', 'price', 'quantity'] },
        'add_shipping_info': { fields: ['items', 'currency', 'value'], itemFields: ['item_id', 'item_name'] },
        'add_payment_info': { fields: ['items', 'currency', 'value'], itemFields: ['item_id', 'item_name'] },
        'purchase': { fields: ['transaction_id', 'value', 'currency', 'items'], itemFields: ['item_id', 'item_name', 'price', 'quantity'] },
        'view_promotion': { fields: ['items'], itemFields: ['item_id', 'item_name'] },
        'select_promotion': { fields: ['items'], itemFields: ['item_id', 'item_name'] }
    };
    
    // ========== СОЗДАЕМ ПАНЕЛЬ ==========
    const panel = document.createElement('div');
    panel.id = 'ga4-diagnostic-panel';
    panel.innerHTML = `
        <style>
            #ga4-diagnostic-panel {
                position: fixed;
                bottom: 20px;
                right: 20px;
                z-index: 999999;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace;
                font-size: 12px;
                background: #1e1e2f;
                color: #fff;
                border-radius: 12px;
                box-shadow: 0 8px 32px rgba(0,0,0,0.4);
                width: 450px;
                display: flex;
                flex-direction: column;
                overflow: hidden;
                transition: all 0.3s ease;
            }
            #ga4-diagnostic-panel.collapsed { width: auto; min-width: 200px; }
            #ga4-diagnostic-header {
                background: linear-gradient(135deg, #4a4e69, #2d2d3a);
                padding: 10px 14px;
                cursor: pointer;
                display: flex;
                justify-content: space-between;
                align-items: center;
                font-weight: bold;
                user-select: none;
            }
            #ga4-diagnostic-header:hover { background: linear-gradient(135deg, #6c6f8d, #3a3a48); }
            #ga4-diagnostic-title { font-size: 13px; display: flex; align-items: center; gap: 8px; }
            #ga4-diagnostic-status-light {
                width: 10px;
                height: 10px;
                border-radius: 50%;
                background: #4caf50;
                box-shadow: 0 0 5px #4caf50;
                animation: pulse 2s infinite;
            }
            @keyframes pulse { 0% { opacity: 0.5; } 50% { opacity: 1; } 100% { opacity: 0.5; } }
            #ga4-diagnostic-content {
                padding: 12px;
                overflow-y: auto;
                max-height: 600px;
                background: #2d2d3a;
                font-size: 11px;
            }
            .ga4-diagnostic-btn {
                background: #6c6f8d;
                border: none;
                color: white;
                padding: 6px 12px;
                margin: 2px;
                border-radius: 6px;
                cursor: pointer;
                font-size: 11px;
                font-family: inherit;
                transition: all 0.2s;
            }
            .ga4-diagnostic-btn:hover { background: #8b8eae; transform: scale(1.02); }
            .ga4-diagnostic-btn.primary { background: #4caf50; }
            .ga4-diagnostic-btn.danger { background: #c44536; }
            .ga4-diagnostic-stats {
                display: flex;
                gap: 8px;
                margin-bottom: 12px;
                flex-wrap: wrap;
            }
            .ga4-diagnostic-stat {
                background: #3a3a48;
                padding: 6px 10px;
                border-radius: 8px;
                text-align: center;
                flex: 1;
                min-width: 60px;
            }
            .ga4-diagnostic-stat-value { font-size: 18px; font-weight: bold; color: #4caf50; }
            .ga4-diagnostic-stat-label { font-size: 9px; opacity: 0.7; }
            .ga4-diagnostic-events-list { max-height: 300px; overflow-y: auto; margin: 8px 0; }
            .ga4-diagnostic-event {
                padding: 8px;
                margin: 6px 0;
                border-left: 3px solid #4caf50;
                background: #3a3a48;
                border-radius: 8px;
                font-size: 10px;
                cursor: pointer;
                transition: all 0.2s;
            }
            .ga4-diagnostic-event.valid { border-left-color: #4caf50; }
            .ga4-diagnostic-event.invalid { border-left-color: #f44336; background: #4a2a2a; }
            .ga4-diagnostic-event.warning { border-left-color: #ff9800; background: #4a3a2a; }
            .ga4-diagnostic-badge {
                border-radius: 10px;
                padding: 2px 6px;
                font-size: 9px;
                margin-left: 6px;
                display: inline-block;
            }
            .ga4-diagnostic-badge.error { background: #c44536; }
            .ga4-diagnostic-badge.warning { background: #ff9800; }
            .ga4-diagnostic-badge.success { background: #4caf50; }
            .ga4-diagnostic-error-list { color: #ff6b6b; font-size: 9px; margin-top: 4px; }
            .ga4-download-menu { position: relative; display: inline-block; }
            .ga4-download-content {
                display: none;
                position: absolute;
                bottom: 100%;
                left: 0;
                background: #3a3a48;
                min-width: 160px;
                border-radius: 8px;
                padding: 4px 0;
                margin-bottom: 4px;
                z-index: 1000;
            }
            .ga4-download-content button {
                display: block;
                width: 100%;
                text-align: left;
                background: none;
                border: none;
                color: white;
                padding: 6px 12px;
                cursor: pointer;
                font-size: 11px;
            }
            .ga4-download-content button:hover { background: #6c6f8d; }
            .ga4-download-menu:hover .ga4-download-content { display: block; }
            hr { border-color: #4a4e69; margin: 8px 0; }
            .ga4-section-title { font-size: 11px; font-weight: bold; margin: 8px 0 4px 0; color: #aaa; }
            .ga4-funnel { background: #1e1e2f; border-radius: 8px; padding: 8px; margin: 8px 0; }
            .ga4-funnel-step { display: flex; align-items: center; gap: 8px; margin: 4px 0; padding: 4px; border-radius: 4px; }
            .ga4-funnel-step.completed { color: #4caf50; }
            .ga4-funnel-step.missing { color: #c44536; opacity: 0.6; }
            .ga4-modal {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background: rgba(0,0,0,0.8);
                z-index: 1000000;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .ga4-modal-content {
		color: #f5f5f5;
                background: #2d2d3a;
                border-radius: 12px;
                max-width: 550px;
                max-height: 80vh;
                overflow: auto;
                padding: 20px;
                font-family: monospace;
                font-size: 11px;
            }
        </style>
        <div id="ga4-diagnostic-header">
            <div id="ga4-diagnostic-title">
                <span id="ga4-diagnostic-status-light"></span>
                <span>🔍 GA4 Диагностика v5.1</span>
            </div>
            <div style="display: flex; gap: 8px;">
                <span id="ga4-error-count" style="background:#c44536; border-radius:10px; padding:2px 6px; font-size:10px;">0</span>
                <span id="ga4-diagnostic-collapse-icon">▼</span>
            </div>
        </div>
        <div id="ga4-diagnostic-content">
            <div class="ga4-diagnostic-stats" id="ga4-diagnostic-stats">
                <div class="ga4-diagnostic-stat"><div class="ga4-diagnostic-stat-value" id="stat-pages">0</div><div class="ga4-diagnostic-stat-label">страниц</div></div>
                <div class="ga4-diagnostic-stat"><div class="ga4-diagnostic-stat-value" id="stat-events">0</div><div class="ga4-diagnostic-stat-label">событий</div></div>
                <div class="ga4-diagnostic-stat"><div class="ga4-diagnostic-stat-value" id="stat-errors">0</div><div class="ga4-diagnostic-stat-label">ошибок</div></div>
                <div class="ga4-diagnostic-stat"><div class="ga4-diagnostic-stat-value" id="stat-warnings">0</div><div class="ga4-diagnostic-stat-label">⚠️</div></div>
            </div>
            <div style="display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px;">
                <button class="ga4-diagnostic-btn primary" id="ga4-btn-report">📊 Отчет</button>
                <div class="ga4-download-menu">
                    <button class="ga4-diagnostic-btn" id="ga4-btn-download">💾 Скачать ▼</button>
                    <div class="ga4-download-content">
                        <button id="ga4-download-json">📄 JSON</button>
                        <button id="ga4-download-csv">📊 CSV</button>
                        <button id="ga4-download-txt">📝 TXT</button>
                        <button id="ga4-download-html">🌐 HTML</button>
                        <button id="ga4-download-validation">🔍 Ошибки</button>
                    </div>
                </div>
                <button class="ga4-diagnostic-btn danger" id="ga4-btn-reset">🗑️ Сброс</button>
            </div>
            <div class="ga4-section-title">🎯 Воронка покупки</div>
            <div id="ga4-funnel" class="ga4-funnel"></div>
            <div class="ga4-section-title">📡 События</div>
            <div id="ga4-diagnostic-events" class="ga4-diagnostic-events-list"></div>
            <hr>
            <div class="ga4-section-title">⚠️ Ошибки</div>
            <div id="ga4-diagnostic-log" class="ga4-diagnostic-log"></div>
        </div>
    `;
    document.body.appendChild(panel);
    
    // ========== СОСТОЯНИЕ ==========
    let diagnosticData = {
        sessionId: Date.now() + '_' + Math.random().toString(36).substr(2, 6),
        startTime: new Date().toISOString(),
        lastUpdate: new Date().toISOString(),
        pages: [],
        events: [],
        errors: [],
        warnings: [],
        gtmId: null,
        ga4Id: null,
        funnel: {
            view_item_list: false,
            select_item: false,
            view_item: false,
            add_to_cart: false,
            begin_checkout: false,
            purchase: false
        }
    };
    
    try {
        const saved = localStorage.getItem(STORAGE_KEY);
        if (saved) {
            const parsed = JSON.parse(saved);
            diagnosticData = parsed;
            console.log(`📂 Загружена сессия: ${diagnosticData.events.length} событий`);
        }
    } catch(e) {}
    
    const currentUrl = window.location.href;
    if (!diagnosticData.pages.some(p => p.url === currentUrl)) {
        diagnosticData.pages.push({ url: currentUrl, title: document.title, timestamp: new Date().toISOString() });
        saveData();
    }
    
    function saveData() {
        diagnosticData.lastUpdate = new Date().toISOString();
        try { localStorage.setItem(STORAGE_KEY, JSON.stringify(diagnosticData)); } catch(e) {}
        updateUI();
    }
    
    // ========== ВАЛИДАЦИЯ ==========
    function validateEvent(eventName, data) {
        const errors = [];
        const warnings = [];
        const eventData = data.ecommerce || data;
        const items = eventData.items || [];
        
        const required = REQUIRED_FIELDS[eventName];
        if (required) {
            required.fields.forEach(field => {
                if (eventData[field] === undefined || eventData[field] === null) {
                    errors.push(`Отсутствует обязательное поле: ${field}`);
                } else if (field === 'items' && (!Array.isArray(eventData[field]) || eventData[field].length === 0)) {
                    errors.push(`Поле items должно быть непустым массивом`);
                }
            });
            
            if (items.length > 0) {
                items.forEach((item, idx) => {
                    const hasId = item.item_id !== undefined && item.item_id !== '';
                    const hasName = item.item_name !== undefined && item.item_name !== '';
                    if (!hasId && !hasName) {
                        errors.push(`Товар #${idx+1}: нет ни item_id, ни item_name`);
                    }
                    if (required.itemFields) {
                        required.itemFields.forEach(field => {
                            if (field !== 'item_id' && field !== 'item_name' && item[field] === undefined) {
                                warnings.push(`Товар #${idx+1}: отсутствует поле ${field}`);
                            }
                        });
                    }
                });
            }
        }
        
        // Проверка суммы
        if (eventData.value !== undefined && items.length > 0) {
            const sum = items.reduce((acc, item) => acc + (item.price || 0) * (item.quantity || 1), 0);
            if (Math.abs(sum - eventData.value) > 0.01) {
                warnings.push(`value (${eventData.value}) != сумме товаров (${sum})`);
            }
        }
        
        return { errors, warnings };
    }
    
    function checkGlobalConsistency() {
        const globalWarnings = [];
        const currencies = new Set();
        diagnosticData.events.forEach(e => {
            const currency = e.data.currency || e.data.ecommerce?.currency;
            if (currency) currencies.add(currency);
        });
        if (currencies.size > 1) globalWarnings.push(`⚠️ Несколько валют: ${[...currencies].join(', ')}`);
        
        const hasPurchase = diagnosticData.events.some(e => e.name === 'purchase');
        const hasBeginCheckout = diagnosticData.events.some(e => e.name === 'begin_checkout');
        if (hasPurchase && !hasBeginCheckout) globalWarnings.push(`⚠️ purchase без begin_checkout`);
        
        return globalWarnings;
    }
    
    function updateFunnel() {
        const steps = ['view_item_list', 'select_item', 'view_item', 'add_to_cart', 'begin_checkout', 'purchase'];
        steps.forEach(step => { diagnosticData.funnel[step] = diagnosticData.events.some(e => e.name === step); });
        const funnelDiv = document.getElementById('ga4-funnel');
        if (funnelDiv) {
            funnelDiv.innerHTML = steps.map(step => {
                const completed = diagnosticData.funnel[step];
                return `<div class="ga4-funnel-step ${completed ? 'completed' : 'missing'}">${completed ? '✅' : '⬜'} ${step}</div>`;
            }).join('');
        }
    }
    
    // ========== ДОБАВЛЕНИЕ СОБЫТИЯ (БЕЗ ПЕРЕХВАТА) ==========
    function addEvent(eventName, eventData, source) {
        const { errors, warnings } = validateEvent(eventName, eventData);
        
        const event = {
            name: eventName,
            data: eventData,
            source: source,
            url: window.location.href,
            timestamp: new Date().toISOString(),
            validation: { isValid: errors.length === 0, errors, warnings }
        };
        
        diagnosticData.events.push(event);
        
        errors.forEach(err => {
            diagnosticData.errors.push({ type: 'error', event: eventName, message: err, timestamp: event.timestamp });
        });
        warnings.forEach(warn => {
            diagnosticData.warnings.push({ type: 'warning', event: eventName, message: warn, timestamp: event.timestamp });
        });
        
        checkGlobalConsistency().forEach(warn => {
            if (!diagnosticData.warnings.some(w => w.message === warn)) {
                diagnosticData.warnings.push({ type: 'global', event: null, message: warn, timestamp: new Date().toISOString() });
            }
        });
        
        updateFunnel();
        saveData();
        
        const color = !event.validation.isValid ? '#f44336' : (warnings.length > 0 ? '#ff9800' : '#4caf50');
        console.log(`%c📡 ${eventName} [${event.validation.isValid ? '✓' : '✗'}]`, `color: ${color}`, eventData);
        if (errors.length) console.groupCollapsed(`%c❌ Ошибки (${errors.length})`, 'color: #f44336');
        errors.forEach(e => console.log(`   ${e}`));
        if (errors.length) console.groupEnd();
        if (warnings.length) console.groupCollapsed(`%c⚠️ Предупреждения (${warnings.length})`, 'color: #ff9800');
        warnings.forEach(w => console.log(`   ${w}`));
        if (warnings.length) console.groupEnd();
    }
    
    function updateUI() {
        document.getElementById('stat-pages').textContent = diagnosticData.pages.length;
        document.getElementById('stat-events').textContent = diagnosticData.events.length;
        document.getElementById('stat-errors').textContent = diagnosticData.errors.length;
        document.getElementById('stat-warnings').textContent = diagnosticData.warnings.length;
        document.getElementById('ga4-error-count').textContent = diagnosticData.errors.length;
        
        const eventsDiv = document.getElementById('ga4-diagnostic-events');
        const recentEvents = [...diagnosticData.events].reverse().slice(0, 15);
        eventsDiv.innerHTML = recentEvents.map(e => {
            const isValid = e.validation.isValid;
            const hasWarnings = e.validation.warnings.length > 0;
            let statusClass = 'valid';
            if (!isValid) statusClass = 'invalid';
            else if (hasWarnings) statusClass = 'warning';
            
            const transId = e.data.transaction_id || e.data.ecommerce?.transaction_id;
            const value = e.data.value || e.data.ecommerce?.value;
            const itemName = e.data.items?.[0]?.item_name || '';
            const statusIcon = !isValid ? '🔴' : (hasWarnings ? '🟠' : '✅');
            
            return `
            <div class="ga4-diagnostic-event ${statusClass}" onclick="window.showEventDetails(${JSON.stringify(e).replace(/"/g, '&quot;')})">
                <div style="display: flex; justify-content: space-between;">
                    <strong>${statusIcon} ${e.name}</strong>
                    <span style="color:#8b8eae;">${new Date(e.timestamp).toLocaleTimeString()}</span>
                </div>
                ${transId ? `<span class="ga4-diagnostic-badge success">ID: ${transId}</span>` : ''}
                ${value ? `<span class="ga4-diagnostic-badge">💰 ${value}</span>` : ''}
                ${itemName ? `<div style="font-size:9px; color:#aaa;">${itemName.substring(0, 40)}</div>` : ''}
                ${!isValid ? `<div class="ga4-diagnostic-error-list">❌ ${e.validation.errors[0]}</div>` : ''}
            </div>`;
        }).join('');
        
        const logDiv = document.getElementById('ga4-diagnostic-log');
        const allIssues = [...diagnosticData.errors, ...diagnosticData.warnings].reverse().slice(0, 10);
        if (allIssues.length) {
            logDiv.innerHTML = allIssues.map(issue => `
                <div class="ga4-diagnostic-log-entry">
                    <span class="ga4-diagnostic-log-time">${new Date(issue.timestamp).toLocaleTimeString()}</span>
                    ${issue.type === 'error' ? '🔴' : '⚠️'} [${issue.event || 'global'}] ${issue.message}
                </div>
            `).join('');
        } else {
            logDiv.innerHTML = '<div style="color:#8b8eae; text-align:center;">✅ Нет ошибок</div>';
        }
        
        const light = document.getElementById('ga4-diagnostic-status-light');
        if (diagnosticData.errors.length > 0) {
            light.style.background = '#f44336';
            light.style.boxShadow = '0 0 5px #f44336';
        } else if (diagnosticData.warnings.length > 0) {
            light.style.background = '#ff9800';
            light.style.boxShadow = '0 0 5px #ff9800';
        } else {
            light.style.background = '#4caf50';
            light.style.boxShadow = '0 0 5px #4caf50';
        }
    }
    
    window.showEventDetails = function(event) {
        const modal = document.createElement('div');
        modal.className = 'ga4-modal';
        modal.innerHTML = `
            <div class="ga4-modal-content">
                <div style="display: flex; justify-content: space-between;">
                    <strong>🔍 ${event.name}</strong>
                    <button style="background:#c44536; border:none; color:white; padding:2px 8px; border-radius:4px; cursor:pointer;" onclick="this.closest('.ga4-modal').remove()">✕</button>
                </div>
                <div style="margin:12px 0;">🕐 ${new Date(event.timestamp).toLocaleString()}<br>🔗 ${event.url.split('/').pop() || '/'}<br>📡 ${event.source}</div>
                ${event.validation.errors.length ? `<div style="background:#4a2a2a; padding:8px; border-radius:6px; margin:12px 0;"><strong>❌ Ошибки:</strong><br>${event.validation.errors.join('<br>')}</div>` : ''}
                ${event.validation.warnings.length ? `<div style="background:#4a3a2a; padding:8px; border-radius:6px; margin:12px 0;"><strong>⚠️ Предупреждения:</strong><br>${event.validation.warnings.join('<br>')}</div>` : ''}
                <div style="background:#1e1e2f; padding:12px; border-radius:8px;"><strong>📦 Данные:</strong><pre style="white-space:pre-wrap; font-size:10px;">${JSON.stringify(event.data, null, 2)}</pre></div>
            </div>
        `;
        document.body.appendChild(modal);
        modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); });
    };
    
    // ========== ПАССИВНЫЙ МОНИТОРИНГ (НЕ ПЕРЕХВАТЫВАЕТ) ==========
    function setupPassiveMonitoring() {
        // 1. Подписываемся на события GTM через addEventListener
        if (window.dataLayer) {
            // Сохраняем оригинальный метод, но НЕ переопределяем
            // Просто следим через прокси без изменения функциональности
            const originalPush = window.dataLayer.push;
            window.dataLayer.push = function(...args) {
                const result = originalPush.apply(this, args);
                const event = args[0];
                if (event && typeof event === 'object' && event.event) {
                    // Используем setTimeout, чтобы не блокировать выполнение
                    setTimeout(() => addEvent(event.event, event, 'datalayer'), 0);
                }
                return result;
            };
            console.log('✅ dataLayer мониторинг (пассивный)');
        }
        
        // 2. Для gtag используем перехват с сохранением функциональности
        if (typeof window.gtag === 'function') {
            const originalGtag = window.gtag;
            window.gtag = function(...args) {
                const result = originalGtag.apply(this, args);
                if (args[0] === 'event') {
                    setTimeout(() => addEvent(args[1], args[2] || {}, 'direct_gtag'), 0);
                }
                return result;
            };
            console.log('✅ gtag мониторинг (пассивный)');
        }
        
        // 3. Поиск ID
        const scripts = Array.from(document.querySelectorAll('script[src*="googletagmanager"]'));
        const gtmScript = scripts.find(s => s.src && s.src.includes('gtm.js'));
        if (gtmScript && !diagnosticData.gtmId) {
            const match = gtmScript.src.match(/id=([^&]+)/);
            if (match) { diagnosticData.gtmId = match[1]; saveData(); console.log(`✅ GTM ID: ${diagnosticData.gtmId}`); }
        }
        
        // 4. SPA мониторинг
        let lastUrl = window.location.href;
        const originalPushState = history.pushState;
        history.pushState = function(...args) {
            const result = originalPushState.apply(this, args);
            setTimeout(checkUrlChange, 100);
            return result;
        };
        const originalReplaceState = history.replaceState;
        history.replaceState = function(...args) {
            const result = originalReplaceState.apply(this, args);
            setTimeout(checkUrlChange, 100);
            return result;
        };
        window.addEventListener('popstate', () => setTimeout(checkUrlChange, 100));
        
        function checkUrlChange() {
            const newUrl = window.location.href;
            if (newUrl !== lastUrl) {
                lastUrl = newUrl;
                setTimeout(() => {
                    if (!diagnosticData.pages.some(p => p.url === newUrl)) {
                        diagnosticData.pages.push({ url: newUrl, title: document.title, timestamp: new Date().toISOString() });
                        saveData();
                        console.log(`📄 SPA переход: ${newUrl.split('/').pop() || '/'}`);
                    }
                }, 200);
            }
        }
        
        console.log('✅ SPA мониторинг');
    }
    
    // ========== СКАЧИВАНИЕ ==========
    function downloadFile(content, filename, mimeType) {
        const blob = new Blob([content], { type: mimeType });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = filename;
        link.click();
        URL.revokeObjectURL(link.href);
    }
    
    function downloadJSON() {
        downloadFile(JSON.stringify(diagnosticData, null, 2), `ga4_report_${new Date().toISOString().slice(0,19).replace(/:/g, '-')}.json`, 'application/json');
    }
    
    function downloadCSV() {
        const headers = ['timestamp', 'event_name', 'source', 'url', 'valid', 'errors', 'warnings', 'transaction_id', 'value', 'currency'];
        const rows = diagnosticData.events.map(e => [
            e.timestamp, e.name, e.source, e.url, e.validation.isValid ? 'YES' : 'NO',
            `"${e.validation.errors.join('; ')}"`, `"${e.validation.warnings.join('; ')}"`,
            e.data.transaction_id || e.data.ecommerce?.transaction_id || '',
            e.data.value || e.data.ecommerce?.value || '',
            e.data.currency || e.data.ecommerce?.currency || ''
        ]);
        downloadFile([headers, ...rows].map(r => r.join(',')).join('\n'), `ga4_events_${new Date().toISOString().slice(0,19).replace(/:/g, '-')}.csv`, 'text/csv;charset=utf-8;');
    }
    
    function downloadTXT() {
        let content = `GA4 ДИАГНОСТИКА v5.1\n${'='.repeat(60)}\nДата: ${new Date().toISOString()}\nСтраниц: ${diagnosticData.pages.length}\nСобытий: ${diagnosticData.events.length}\nОшибок: ${diagnosticData.errors.length}\nПредупреждений: ${diagnosticData.warnings.length}\n\n${'='.repeat(60)}\nОШИБКИ:\n${diagnosticData.errors.map(e => `🔴 [${e.event}] ${e.message}`).join('\n')}\n\n${'='.repeat(60)}\nСОБЫТИЯ:\n${diagnosticData.events.map(e => `[${e.name}] ${e.validation.isValid ? '✓' : '✗'} ${e.validation.errors.join('; ')}`).join('\n')}`;
        downloadFile(content, `ga4_report_${new Date().toISOString().slice(0,19).replace(/:/g, '-')}.txt`, 'text/plain;charset=utf-8;');
    }
    
    // ========== ПОДРОБНЫЙ HTML ОТЧЕТ ==========
function downloadHTML() {
    const validEvents = diagnosticData.events.filter(e => e.validation.isValid);
    const invalidEvents = diagnosticData.events.filter(e => !e.validation.isValid);
    const purchaseEvents = diagnosticData.events.filter(e => e.name === 'purchase');
    const hasDuplicate = purchaseEvents.length > 1;
    
    const eventGroups = {};
    diagnosticData.events.forEach(e => {
        if (!eventGroups[e.name]) eventGroups[e.name] = [];
        eventGroups[e.name].push(e);
    });
    
    const html = `<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>GA4 Diagnostic Report</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
            background: #f0f2f5;
            padding: 40px 20px;
            line-height: 1.5;
        }
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 20px;
            box-shadow: 0 8px 30px rgba(0,0,0,0.1);
            overflow: hidden;
        }
        .header {
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
            color: white;
            padding: 30px 40px;
        }
        .header h1 {
            font-size: 28px;
            margin-bottom: 10px;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .header .meta {
            opacity: 0.8;
            font-size: 14px;
            margin-top: 15px;
        }
        .status-card {
            margin: 20px 40px;
            padding: 20px;
            border-radius: 16px;
            font-weight: 500;
        }
        .status-card.success {
            background: #e8f5e9;
            border-left: 4px solid #4caf50;
            color: #2e7d32;
        }
        .status-card.error {
            background: #ffebee;
            border-left: 4px solid #f44336;
            color: #c62828;
        }
        .status-card.warning {
            background: #fff3e0;
            border-left: 4px solid #ff9800;
            color: #ef6c00;
        }
        .stats-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
            gap: 20px;
            padding: 0 40px 30px;
        }
        .stat-card {
            background: #f8f9fa;
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            transition: transform 0.2s;
        }
        .stat-card:hover { transform: translateY(-2px); }
        .stat-value {
            font-size: 36px;
            font-weight: bold;
            color: #1a1a2e;
        }
        .stat-label {
            color: #666;
            font-size: 13px;
            margin-top: 8px;
        }
        .stat-value.valid { color: #4caf50; }
        .stat-value.invalid { color: #f44336; }
        .stat-value.warning { color: #ff9800; }
        
        .section {
            padding: 0 40px 30px;
            border-bottom: 1px solid #e0e0e0;
        }
        .section-title {
            font-size: 20px;
            font-weight: 600;
            margin-bottom: 20px;
            color: #1a1a2e;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .funnel {
            background: #f8f9fa;
            border-radius: 12px;
            padding: 20px;
        }
        .funnel-step {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 8px 0;
            border-bottom: 1px solid #e9ecef;
        }
        .funnel-step:last-child { border-bottom: none; }
        .funnel-step.completed { color: #4caf50; }
        .funnel-step.missing { color: #f44336; opacity: 0.7; }
        
        .pages-list {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }
        .page-item {
            background: #f8f9fa;
            padding: 8px 16px;
            border-radius: 20px;
            font-size: 13px;
            font-family: monospace;
        }
        
        table {
            width: 100%;
            border-collapse: collapse;
            font-size: 13px;
        }
        th, td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #e9ecef;
            vertical-align: top;
        }
        th {
            background: #f8f9fa;
            font-weight: 600;
            position: sticky;
            top: 0;
        }
        .event-valid { background: #e8f5e9; }
        .event-invalid { background: #ffebee; }
        .event-warning { background: #fff3e0; }
        .badge {
            display: inline-block;
            padding: 2px 8px;
            border-radius: 20px;
            font-size: 11px;
            font-weight: 500;
        }
        .badge-valid { background: #4caf50; color: white; }
        .badge-invalid { background: #f44336; color: white; }
        .badge-warning { background: #ff9800; color: white; }
        .items-list {
            font-size: 11px;
            background: #f8f9fa;
            padding: 8px;
            border-radius: 8px;
            margin-top: 4px;
        }
        .item {
            margin: 6px 0;
            padding: 6px;
            background: white;
            border-radius: 6px;
        }
        .param-key { color: #ff6b6b; font-weight: 500; }
        .param-value { color: #4caf50; }
        .error-text { color: #f44336; font-size: 11px; margin-top: 4px; }
        .warning-text { color: #ff9800; font-size: 11px; }
        
        @media print {
            body { background: white; padding: 0; }
            .container { box-shadow: none; }
            .status-card { break-inside: avoid; }
            .section { break-inside: avoid; }
        }
    </style>
</head>
<body>
<div class="container">
    <div class="header">
        <h1>🔍 GA4 Диагностический отчет</h1>
        <div class="meta">
            Session ID: ${diagnosticData.sessionId}<br>
            Начало сессии: ${new Date(diagnosticData.startTime).toLocaleString()}<br>
            Сформирован: ${new Date().toLocaleString()}<br>
            GTM ID: ${diagnosticData.gtmId || 'не найден'}
        </div>
    </div>
    
    <div class="status-card ${hasDuplicate ? 'error' : (diagnosticData.errors.length > 0 ? 'error' : (diagnosticData.warnings.length > 0 ? 'warning' : 'success'))}">
        <strong>${hasDuplicate ? '❌ ОБНАРУЖЕНО ДУБЛИРОВАНИЕ PURCHASE!' : (diagnosticData.errors.length > 0 ? '❌ НАЙДЕНЫ ОШИБКИ ВАЛИДАЦИИ' : (diagnosticData.warnings.length > 0 ? '⚠️ ЕСТЬ ПРЕДУПРЕЖДЕНИЯ' : '✅ ВСЕ ПРОВЕРКИ ПРОЙДЕНЫ'))}</strong>
        ${hasDuplicate ? `<br>Зафиксировано ${purchaseEvents.length} покупки(ок) — проверьте источники дублирования` : ''}
        ${diagnosticData.errors.length > 0 && !hasDuplicate ? `<br>Найдено ${diagnosticData.errors.length} критических ошибок в событиях` : ''}
    </div>
    
    <div class="stats-grid">
        <div class="stat-card"><div class="stat-value">${diagnosticData.pages.length}</div><div class="stat-label">страниц</div></div>
        <div class="stat-card"><div class="stat-value">${diagnosticData.events.length}</div><div class="stat-label">событий</div></div>
        <div class="stat-card"><div class="stat-value valid">${validEvents.length}</div><div class="stat-label">валидных</div></div>
        <div class="stat-card"><div class="stat-value invalid">${invalidEvents.length}</div><div class="stat-label">невалидных</div></div>
        <div class="stat-card"><div class="stat-value warning">${diagnosticData.warnings.length}</div><div class="stat-label">предупреждений</div></div>
        <div class="stat-card"><div class="stat-value invalid">${diagnosticData.errors.length}</div><div class="stat-label">ошибок</div></div>
    </div>
    
    <div class="section">
        <div class="section-title">🎯 Воронка покупки</div>
        <div class="funnel">
            ${[
                { step: 'view_item_list', desc: 'Просмотр списка товаров' },
                { step: 'select_item', desc: 'Выбор товара из списка' },
                { step: 'view_item', desc: 'Просмотр карточки товара' },
                { step: 'add_to_cart', desc: 'Добавление в корзину' },
                { step: 'begin_checkout', desc: 'Начало оформления' },
                { step: 'purchase', desc: 'Покупка' }
            ].map(({step, desc}) => `
                <div class="funnel-step ${diagnosticData.funnel[step] ? 'completed' : 'missing'}">
                    ${diagnosticData.funnel[step] ? '✅' : '⬜'} <strong>${step}</strong> — ${desc}
                </div>
            `).join('')}
        </div>
    </div>
    
    <div class="section">
        <div class="section-title">📄 Посещенные страницы</div>
        <div class="pages-list">
            ${diagnosticData.pages.map(p => `
                <div class="page-item" title="${p.url}">
                    ${p.url.split('/').pop() || '/'} <span style="color:#999; font-size:10px;">${new Date(p.timestamp).toLocaleTimeString()}</span>
                </div>
            `).join('')}
        </div>
    </div>
    
    <div class="section">
        <div class="section-title">📡 Все события с параметрами</div>
        <div style="overflow-x: auto;">
            <table>
                <thead>
                    <tr>
                        <th>Время</th>
                        <th>Событие</th>
                        <th>Статус</th>
                        <th>Transaction ID</th>
                        <th>Сумма</th>
                        <th>Параметры / Товары</th>
                        <th>Страница</th>
                    </tr>
                </thead>
                <tbody>
                    ${diagnosticData.events.map(e => {
                        const eventData = e.data.ecommerce || e.data;
                        const items = eventData.items || [];
                        const transId = eventData.transaction_id || '';
                        const value = eventData.value || '';
                        const currency = eventData.currency || 'USD';
                        const rowClass = !e.validation.isValid ? 'event-invalid' : (e.validation.warnings.length > 0 ? 'event-warning' : 'event-valid');
                        const statusBadge = !e.validation.isValid ? 'invalid' : (e.validation.warnings.length > 0 ? 'warning' : 'valid');
                        
                        let itemsHtml = '';
                        if (items.length > 0) {
                            itemsHtml = `<div class="items-list"><strong>📦 Товары (${items.length}):</strong>`;
                            items.forEach((item, idx) => {
                                itemsHtml += `
                                    <div class="item">
                                        <strong>#${idx+1}</strong><br>
                                        ${item.item_id ? `<span class="param-key">ID:</span> <span class="param-value">${item.item_id}</span><br>` : ''}
                                        ${item.item_name ? `<span class="param-key">Название:</span> <span class="param-value">${item.item_name.substring(0, 80)}</span><br>` : ''}
                                        ${item.item_brand ? `<span class="param-key">Бренд:</span> <span class="param-value">${item.item_brand}</span><br>` : ''}
                                        ${item.item_category ? `<span class="param-key">Категория:</span> <span class="param-value">${item.item_category}</span><br>` : ''}
                                        ${item.item_category2 ? `<span class="param-key">Категория 2:</span> <span class="param-value">${item.item_category2}</span><br>` : ''}
                                        ${item.price ? `<span class="param-key">Цена:</span> <span class="param-value">${item.price} ${currency}</span><br>` : ''}
                                        ${item.quantity ? `<span class="param-key">Кол-во:</span> <span class="param-value">${item.quantity}</span><br>` : ''}
                                        ${item.index !== undefined ? `<span class="param-key">Позиция:</span> <span class="param-value">${item.index}</span><br>` : ''}
                                    </div>
                                `;
                            });
                            itemsHtml += `</div>`;
                        }
                        
                        // Дополнительные параметры
                        let extraHtml = '';
                        if (eventData.creative_name) extraHtml += `<div><span class="param-key">🎨 Креатив:</span> ${eventData.creative_name}</div>`;
                        if (eventData.promotion_name) extraHtml += `<div><span class="param-key">🎁 Промо:</span> ${eventData.promotion_name}</div>`;
                        if (eventData.item_list_name && !items.some(i => i.item_list_name)) {
                            extraHtml += `<div><span class="param-key">📋 Список:</span> ${eventData.item_list_name}</div>`;
                        }
                        
                        return `
                        <tr class="${rowClass}">
                            <td style="white-space: nowrap;">${new Date(e.timestamp).toLocaleString()}</td>
                            <td><strong>${e.name}</strong><br><small>${e.source}</small></td>
                            <td><span class="badge badge-${statusBadge}">${!e.validation.isValid ? 'ОШИБКА' : (e.validation.warnings.length > 0 ? 'ПРЕДУПРЕЖДЕНИЕ' : 'ВАЛИДНО')}</span></td>
                            <td>${transId || '-'}</td>
                            <td>${value ? `${currency} ${value}` : '-'}</td>
                            <td>${itemsHtml || extraHtml || '-'}</td>
                            <td>${e.url.split('/').pop() || '/'}</td>
                        </tr>
                        `;
                    }).join('')}
                </tbody>
            </table>
        </div>
    </div>
    
    <div class="section">
        <div class="section-title">⚠️ Ошибки и предупреждения</div>
        ${diagnosticData.errors.length === 0 && diagnosticData.warnings.length === 0 ? 
            '<div style="padding: 20px; text-align: center; color: #4caf50;">✅ Нет ошибок и предупреждений</div>' : 
            `
                ${diagnosticData.errors.length > 0 ? `
                    <div style="margin-bottom: 20px;">
                        <strong style="color: #f44336;">🔴 ОШИБКИ (${diagnosticData.errors.length}):</strong>
                        <ul style="margin-top: 10px;">
                            ${diagnosticData.errors.map(err => `<li style="color: #f44336;">[${err.event || 'global'}] ${err.message}</li>`).join('')}
                        </ul>
                    </div>
                ` : ''}
                ${diagnosticData.warnings.length > 0 ? `
                    <div>
                        <strong style="color: #ff9800;">🟠 ПРЕДУПРЕЖДЕНИЯ (${diagnosticData.warnings.length}):</strong>
                        <ul style="margin-top: 10px;">
                            ${diagnosticData.warnings.map(warn => `<li style="color: #ff9800;">[${warn.event || 'global'}] ${warn.message}</li>`).join('')}
                        </ul>
                    </div>
                ` : ''}
            `
        }
    </div>
    
    <div class="section">
        <div class="section-title">📊 Сводка по типам событий</div>
        <table>
            <thead><tr><th>Событие</th><th>Количество</th><th>Валидных</th><th>Невалидных</th><th>Примеры параметров</th></tr></thead>
            <tbody>
                ${Object.entries(eventGroups).map(([name, events]) => {
                    const valid = events.filter(e => e.validation.isValid).length;
                    const invalid = events.length - valid;
                    const sample = events[0];
                    const eventData = sample.data.ecommerce || sample.data;
                    const items = eventData.items || [];
                    let example = '';
                    if (items.length > 0) {
                        example = `${items[0].item_name || items[0].item_id} — ${eventData.value || items[0].price} ${eventData.currency || 'USD'}`;
                    } else if (eventData.value) {
                        example = `value=${eventData.value} ${eventData.currency || 'USD'}`;
                    }
                    return `
                    <tr>
                        <td><strong>${name}</strong></td>
                        <td>${events.length}</td>
                        <td style="color:#4caf50;">${valid}</td>
                        <td style="color:#f44336;">${invalid}</td>
                        <td><small>${example || '-'}</small></td>
                    </tr>
                    `;
                }).join('')}
            </tbody>
        </table>
    </div>
    
    <div class="section">
        <div class="section-title">🎯 Вывод по purchase</div>
        ${purchaseEvents.length === 0 ? 
            '<div>ℹ️ За время сессии не было зафиксировано ни одной покупки. Совершите тестовую покупку для проверки.</div>' :
            (purchaseEvents.length === 1 ?
                `<div style="background:#e8f5e9; padding:16px; border-radius:12px;">
                    <strong>✅ ЗАФИКСИРОВАНА 1 ПОКУПКА — дублирования НЕТ!</strong><br>
                    Transaction ID: ${purchaseEvents[0].data.transaction_id || purchaseEvents[0].data.ecommerce?.transaction_id || 'не указан'}<br>
                    Сумма: ${purchaseEvents[0].data.value || purchaseEvents[0].data.ecommerce?.value || 'не указана'}<br>
                    Время: ${new Date(purchaseEvents[0].timestamp).toLocaleString()}<br>
                    Источник: ${purchaseEvents[0].source}<br>
                    Страница: ${purchaseEvents[0].url.split('/').pop() || '/'}
                </div>` :
                `<div style="background:#ffebee; padding:16px; border-radius:12px;">
                    <strong>❌ ЗАФИКСИРОВАНО ${purchaseEvents.length} ПОКУПКИ(ОК) — ОБНАРУЖЕНО ДУБЛИРОВАНИЕ!</strong><br>
                    ${purchaseEvents.map((p,i) => `${i+1}. ${new Date(p.timestamp).toLocaleTimeString()} — ID: ${p.data.transaction_id || p.data.ecommerce?.transaction_id || 'не указан'} — ${p.url.split('/').pop()}`).join('<br>')}
                    <br><br>
                    <strong>🔧 РЕКОМЕНДАЦИИ:</strong><br>
                    1. Проверьте триггер purchase в GTM — он должен срабатывать 1 раз<br>
                    2. Убедитесь, что нет двух источников (GTM + прямой gtag)<br>
                    3. Реализуйте защиту через sessionStorage/localStorage
                </div>`)
        }
    </div>
</div>
</body>
</html>`;
    
    downloadFile(html, `ga4_report_${new Date().toISOString().slice(0,19).replace(/:/g, '-')}.html`, 'text/html;charset=utf-8;');
}
    
    function downloadValidationCSV() {
        const headers = ['timestamp', 'event_name', 'type', 'message'];
        const rows = [
            ...diagnosticData.errors.map(e => [e.timestamp, e.event, 'error', e.message]),
            ...diagnosticData.warnings.map(w => [w.timestamp, w.event, 'warning', w.message])
        ];
        downloadFile([headers, ...rows].map(r => r.join(',')).join('\n'), `ga4_validation_${new Date().toISOString().slice(0,19).replace(/:/g, '-')}.csv`, 'text/csv;charset=utf-8;');
    }
    
    // ========== ПОДРОБНЫЙ ОТЧЕТ В КОНСОЛИ ==========
function showReport() {
    const validEvents = diagnosticData.events.filter(e => e.validation.isValid);
    const invalidEvents = diagnosticData.events.filter(e => !e.validation.isValid);
    const purchaseEvents = diagnosticData.events.filter(e => e.name === 'purchase');
    const hasDuplicate = purchaseEvents.length > 1;
    
    console.clear();
    console.log('\n' + '█'.repeat(80));
    console.log('📊 GA4 ДИАГНОСТИЧЕСКИЙ ОТЧЕТ v5.1');
    console.log('█'.repeat(80));
    console.log(`🕐 Сессия начата: ${new Date(diagnosticData.startTime).toLocaleString()}`);
    console.log(`🕐 Отчет сформирован: ${new Date().toLocaleString()}`);
    console.log(`🆔 Session ID: ${diagnosticData.sessionId}`);
    console.log(`🆔 GTM ID: ${diagnosticData.gtmId || 'не найден'}`);
    console.log(`🆔 GA4 ID: ${diagnosticData.ga4Id || 'не найден'}`);
    
    console.log('\n' + '─'.repeat(80));
    console.log('📊 СТАТИСТИКА СЕССИИ:');
    console.log('─'.repeat(80));
    console.log(`   📄 Посещено страниц: ${diagnosticData.pages.length}`);
    console.log(`   📡 Всего событий: ${diagnosticData.events.length}`);
    console.log(`      ✅ Валидных: ${validEvents.length}`);
    console.log(`      ❌ Невалидных: ${invalidEvents.length}`);
    console.log(`   🛍️ Покупок: ${purchaseEvents.length}`);
    console.log(`   ⚠️ Предупреждений: ${diagnosticData.warnings.length}`);
    console.log(`   🔴 Ошибок: ${diagnosticData.errors.length}`);
    
    console.log('\n' + '─'.repeat(80));
    console.log('🎯 ВОРОНКА ПОКУПКИ:');
    console.log('─'.repeat(80));
    const funnelSteps = [
        { step: 'view_item_list', desc: 'Просмотр списка товаров', required: false },
        { step: 'select_item', desc: 'Выбор товара из списка', required: false },
        { step: 'view_item', desc: 'Просмотр карточки товара', required: true },
        { step: 'add_to_cart', desc: 'Добавление в корзину', required: true },
        { step: 'begin_checkout', desc: 'Начало оформления', required: true },
        { step: 'purchase', desc: 'Покупка', required: true }
    ];
    
    funnelSteps.forEach(({ step, desc, required }) => {
        const completed = diagnosticData.funnel[step];
        const icon = completed ? '✅' : (required ? '❌' : '⬜');
        const color = completed ? '' : (required ? ' (ОБЯЗАТЕЛЬНО!)' : '');
        console.log(`   ${icon} ${step.padEnd(20)} → ${desc}${color}`);
    });
    
    console.log('\n' + '─'.repeat(80));
    console.log('📄 ПОСЕЩЕННЫЕ СТРАНИЦЫ:');
    console.log('─'.repeat(80));
    diagnosticData.pages.forEach((page, i) => {
        const shortUrl = page.url.replace(/^https?:\/\/[^\/]+/, '');
        console.log(`   ${(i+1).toString().padStart(2)}. ${shortUrl || '/'}`);
        console.log(`       🕐 ${new Date(page.timestamp).toLocaleString()}`);
    });
    
    console.log('\n' + '─'.repeat(80));
    console.log('📡 ВСЕ СОБЫТИЯ С ПОДРОБНЫМИ ПАРАМЕТРАМИ:');
    console.log('─'.repeat(80));
    
    diagnosticData.events.forEach((e, i) => {
        const isValid = e.validation.isValid;
        const statusIcon = !isValid ? '🔴' : (e.validation.warnings.length > 0 ? '🟠' : '✅');
        
        console.log(`\n[${i+1}] ${statusIcon} ${e.name} | ${new Date(e.timestamp).toLocaleString()}`);
        console.log(`   📡 Источник: ${e.source}`);
        console.log(`   🔗 Страница: ${e.url.split('/').pop() || '/'}`);
        
        // Основные параметры события
        const eventData = e.data.ecommerce || e.data;
        if (eventData.transaction_id) console.log(`   🆔 transaction_id: ${eventData.transaction_id}`);
        if (eventData.value) console.log(`   💰 value: ${eventData.value} ${eventData.currency || 'USD'}`);
        if (eventData.currency) console.log(`   💱 currency: ${eventData.currency}`);
        if (eventData.item_list_name) console.log(`   📋 item_list_name: ${eventData.item_list_name}`);
        
        // Параметры товаров
        const items = eventData.items || [];
        if (items.length > 0) {
            console.log(`   📦 ТОВАРЫ (${items.length}):`);
            items.forEach((item, idx) => {
                console.log(`      [${idx+1}] ─────────────────────────`);
                if (item.item_id) console.log(`          🆔 item_id: ${item.item_id}`);
                if (item.item_name) console.log(`          📝 item_name: ${item.item_name.substring(0, 80)}`);
                if (item.item_brand) console.log(`          🏷️ item_brand: ${item.item_brand}`);
                if (item.item_category) console.log(`          📁 item_category: ${item.item_category}`);
                if (item.item_category2) console.log(`          📁 item_category2: ${item.item_category2}`);
                if (item.item_category3) console.log(`          📁 item_category3: ${item.item_category3}`);
                if (item.price) console.log(`          💰 price: ${item.price}`);
                if (item.quantity) console.log(`          🔢 quantity: ${item.quantity}`);
                if (item.index !== undefined) console.log(`          🔢 index: ${item.index}`);
                if (item.item_list_name) console.log(`          📋 item_list_name: ${item.item_list_name}`);
            });
            
            // Проверка суммы
            const sum = items.reduce((acc, item) => acc + (item.price || 0) * (item.quantity || 1), 0);
            if (eventData.value && Math.abs(sum - eventData.value) > 0.01) {
                console.log(`      ⚠️ ВНИМАНИЕ: value (${eventData.value}) не совпадает с суммой товаров (${sum})`);
            }
        }
        
        // Параметры промо
        if (eventData.creative_name) console.log(`   🎨 creative_name: ${eventData.creative_name}`);
        if (eventData.creative_slot) console.log(`   📍 creative_slot: ${eventData.creative_slot}`);
        if (eventData.promotion_id) console.log(`   🎁 promotion_id: ${eventData.promotion_id}`);
        if (eventData.promotion_name) console.log(`   🎁 promotion_name: ${eventData.promotion_name}`);
        
        // Ошибки и предупреждения
        if (e.validation.errors.length > 0) {
            console.log(`   ❌ ОШИБКИ (${e.validation.errors.length}):`);
            e.validation.errors.forEach(err => console.log(`      🔴 ${err}`));
        }
        if (e.validation.warnings.length > 0) {
            console.log(`   ⚠️ ПРЕДУПРЕЖДЕНИЯ (${e.validation.warnings.length}):`);
            e.validation.warnings.forEach(warn => console.log(`      🟠 ${warn}`));
        }
    });
    
    console.log('\n' + '─'.repeat(80));
    console.log('⚠️ ВСЕ ОШИБКИ И ПРЕДУПРЕЖДЕНИЯ:');
    console.log('─'.repeat(80));
    
    if (diagnosticData.errors.length === 0 && diagnosticData.warnings.length === 0) {
        console.log('   ✅ Нет ошибок и предупреждений');
    } else {
        if (diagnosticData.errors.length > 0) {
            console.log(`   🔴 ОШИБКИ (${diagnosticData.errors.length}):`);
            diagnosticData.errors.forEach(err => {
                console.log(`      [${err.event || 'global'}] ${err.message}`);
            });
        }
        if (diagnosticData.warnings.length > 0) {
            console.log(`   🟠 ПРЕДУПРЕЖДЕНИЯ (${diagnosticData.warnings.length}):`);
            diagnosticData.warnings.forEach(warn => {
                console.log(`      [${warn.event || 'global'}] ${warn.message}`);
            });
        }
    }
    
    console.log('\n' + '─'.repeat(80));
    console.log('🎯 ВЫВОД ПО PURCHASE:');
    console.log('─'.repeat(80));
    
    if (purchaseEvents.length === 0) {
        console.log('   ℹ️ За время сессии не было зафиксировано ни одной покупки.');
        console.log('   💡 Совершите тестовую покупку для проверки.');
    } else if (purchaseEvents.length === 1) {
        console.log('   ✅ ЗАФИКСИРОВАНА 1 ПОКУПКА — дублирования НЕТ!');
        const p = purchaseEvents[0];
        const transId = p.data.transaction_id || p.data.ecommerce?.transaction_id;
        const value = p.data.value || p.data.ecommerce?.value;
        console.log(`      🆔 Transaction ID: ${transId || 'не указан'}`);
        console.log(`      💰 Сумма: ${value || 'не указана'}`);
        console.log(`      🕐 Время: ${new Date(p.timestamp).toLocaleString()}`);
        console.log(`      📡 Источник: ${p.source}`);
        console.log(`      📄 Страница: ${p.url.split('/').pop() || '/'}`);
        if (p.data.items && p.data.items.length > 0) {
            console.log(`      📦 Товары:`);
            p.data.items.forEach((item, idx) => {
                console.log(`         ${idx+1}. ${item.item_name || item.item_id} — ${item.price} x ${item.quantity} = ${item.price * item.quantity}`);
            });
        }
        if (!p.validation.isValid) {
            console.log(`      ❌ ОШИБКИ ВАЛИДАЦИИ:`);
            p.validation.errors.forEach(err => console.log(`         🔴 ${err}`));
        }
    } else {
        console.log(`   ❌ ЗАФИКСИРОВАНО ${purchaseEvents.length} ПОКУПКИ(ОК) — ОБНАРУЖЕНО ДУБЛИРОВАНИЕ!`);
        console.log(`\n   📋 Детали дублей:`);
        purchaseEvents.forEach((p, i) => {
            const transId = p.data.transaction_id || p.data.ecommerce?.transaction_id;
            const time = new Date(p.timestamp).toLocaleTimeString();
            console.log(`      ${i+1}. ${time} — ID: ${transId || 'не указан'} — ${p.url.split('/').pop()}`);
            console.log(`         Источник: ${p.source}`);
        });
        console.log(`\n   🔧 РЕКОМЕНДАЦИИ ПО ИСПРАВЛЕНИЮ:`);
        console.log(`      1. Проверьте триггер purchase в GTM — он должен срабатывать 1 раз`);
        console.log(`      2. Убедитесь, что нет двух источников (GTM + прямой gtag)`);
        console.log(`      3. Реализуйте защиту через sessionStorage/localStorage`);
        console.log(`      4. Проверьте, не срабатывает ли событие при обновлении страницы "Спасибо"`);
    }
    
    console.log('\n' + '─'.repeat(80));
    console.log('📊 СВОДКА ПО ТИПАМ СОБЫТИЙ:');
    console.log('─'.repeat(80));
    
    const eventGroups = {};
    diagnosticData.events.forEach(e => {
        if (!eventGroups[e.name]) eventGroups[e.name] = [];
        eventGroups[e.name].push(e);
    });
    
    Object.keys(eventGroups).sort().forEach(eventName => {
        const events = eventGroups[eventName];
        const validCount = events.filter(e => e.validation.isValid).length;
        const invalidCount = events.length - validCount;
        const statusIcon = invalidCount > 0 ? '❌' : (events.some(e => e.validation.warnings.length > 0) ? '🟠' : '✅');
        
        console.log(`   ${statusIcon} ${eventName.padEnd(20)} → ${events.length} раз(а) (валидных: ${validCount}, невалидных: ${invalidCount})`);
        
        // Показываем пример параметров для первых 2 событий
        events.slice(0, 2).forEach(e => {
            const eventData = e.data.ecommerce || e.data;
            const items = eventData.items || [];
            if (items.length > 0) {
                const firstItem = items[0];
                console.log(`      📦 Пример: ${firstItem.item_name || firstItem.item_id} — ${eventData.value || firstItem.price} ${eventData.currency || 'USD'}`);
            } else if (eventData.value) {
                console.log(`      💰 Пример: value=${eventData.value} ${eventData.currency || 'USD'}`);
            }
        });
    });
    
    console.log('\n' + '█'.repeat(80));
    console.log('💾 Отчет сохранен в window.__ga4FullReport');
    console.log('📋 Чтобы скопировать JSON: copy(JSON.stringify(window.__ga4FullReport, null, 2))');
    console.log('📥 Доступные форматы скачивания: JSON, CSV, TXT, HTML');
    console.log('█'.repeat(80) + '\n');
    
    window.__ga4FullReport = {
        session: diagnosticData,
        summary: {
            totalPages: diagnosticData.pages.length,
            totalEvents: diagnosticData.events.length,
            validEvents: validEvents.length,
            invalidEvents: invalidEvents.length,
            purchaseCount: purchaseEvents.length,
            hasDuplicate: hasDuplicate,
            errorCount: diagnosticData.errors.length,
            warningCount: diagnosticData.warnings.length,
            funnel: diagnosticData.funnel
        }
    };
    
    return window.__ga4FullReport;
} 
    
    function setupUI() {
        let collapsed = false;
        const header = document.getElementById('ga4-diagnostic-header');
        const content = document.getElementById('ga4-diagnostic-content');
        header.addEventListener('click', (e) => {
            if (e.target.tagName === 'BUTTON') return;
            collapsed = !collapsed;
            content.style.display = collapsed ? 'none' : 'block';
            document.getElementById('ga4-diagnostic-collapse-icon').textContent = collapsed ? '▲' : '▼';
        });
        document.getElementById('ga4-btn-report').addEventListener('click', showReport);
        document.getElementById('ga4-download-json').addEventListener('click', downloadJSON);
        document.getElementById('ga4-download-csv').addEventListener('click', downloadCSV);
        document.getElementById('ga4-download-txt').addEventListener('click', downloadTXT);
        document.getElementById('ga4-download-html').addEventListener('click', downloadHTML);
        document.getElementById('ga4-download-validation').addEventListener('click', downloadValidationCSV);
        document.getElementById('ga4-btn-reset').addEventListener('click', () => {
            if (confirm('Сбросить все данные?')) { localStorage.removeItem(STORAGE_KEY); location.reload(); }
        });
    }
    
    // ========== ЗАПУСК ==========
    setupPassiveMonitoring();
    setupUI();
    updateFunnel();
    updateUI();
    
    console.log('\n' + '='.repeat(60));
    console.log('🔍 GA4 ДИАГНОСТИКА v5.1 ЗАПУЩЕНА');
    console.log('✅ Режим: ТОЛЬКО НАБЛЮДЕНИЕ (не мешает отправке)');
    console.log('📌 Панель в правом нижнем углу');
    console.log('='.repeat(60) + '\n');
    
})();
Install requires the InjectJS Chrome extension. Scripts run only on sites matching the pattern above. Review code before installing any community script.