// ==UserScript==
// @name         Sofascore Pro Export — PDF & CSV
// @namespace    https://github.com/ChunkOfSnippet
// @version      2.0
// @description  One-click export of Sofascore matches & live scores to PDF and CSV with a polished dark UI
// @author       Ali
// @match        https://www.sofascore.com/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @require      https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js
// ==/UserScript==

(async function () {
    'use strict';

    // ─────────────────────────────────────────────────────────────────────────
    //  CONSTANTS
    // ─────────────────────────────────────────────────────────────────────────
    const BRAND = {
        purple:      '#7a84ff',
        purpleDim:   'rgba(122, 132, 255, 0.15)',
        purpleGlow:  'rgba(122, 132, 255, 0.35)',
        green:       '#46c252',
        live:        '#e73b3b',
        liveDim:     'rgba(231, 59, 59, 0.18)',
        surface0:    '#000000',
        surface1:    '#171c1f',
        surfaceP:    '#272c32',
        surfacePH:   '#2f353c',
        nLv1:        '#ecedef',
        nLv3:        'rgba(255,255,255,0.5)',
        nLv4:        'rgba(255,255,255,0.15)',
        font:        "'SofascoreSans', 'Inter', 'Segoe UI', Arial, sans-serif",
    };

    // ─────────────────────────────────────────────────────────────────────────
    //  STYLES
    // ─────────────────────────────────────────────────────────────────────────
    GM_addStyle(`
        /* ── Panel Container ─────────────────────────────────── */
        #sfe-panel {
            position: fixed;
            top: 18px;
            right: 18px;
            z-index: 2147483647;
            width: 290px;
            background: ${BRAND.surface1};
            border: 1px solid ${BRAND.nLv4};
            border-radius: 14px;
            box-shadow: 0 8px 32px rgba(0,0,0,0.55), 0 0 0 1px rgba(122,132,255,0.08);
            font-family: ${BRAND.font};
            color: ${BRAND.nLv1};
            overflow: hidden;
            transition: box-shadow 0.2s;
            user-select: none;
        }
        #sfe-panel:hover {
            box-shadow: 0 12px 40px rgba(0,0,0,0.65), 0 0 0 1px ${BRAND.purpleGlow};
        }

        /* ── Header ──────────────────────────────────────────── */
        #sfe-header {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 12px 14px 10px;
            background: linear-gradient(135deg, rgba(122,132,255,0.12) 0%, rgba(122,132,255,0.03) 100%);
            border-bottom: 1px solid ${BRAND.nLv4};
            cursor: pointer;
        }
        #sfe-logo {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 28px;
            height: 28px;
            background: ${BRAND.purple};
            border-radius: 7px;
            font-size: 14px;
            font-weight: 700;
            color: #fff;
            flex-shrink: 0;
            letter-spacing: -0.5px;
        }
        #sfe-title {
            flex: 1;
            font-size: 13px;
            font-weight: 600;
            color: ${BRAND.nLv1};
            letter-spacing: 0.2px;
        }
        #sfe-title span {
            display: block;
            font-size: 10px;
            font-weight: 400;
            color: ${BRAND.nLv3};
            margin-top: 1px;
        }
        #sfe-toggle {
            width: 22px;
            height: 22px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: none;
            border: none;
            color: ${BRAND.nLv3};
            cursor: pointer;
            border-radius: 5px;
            transition: background 0.15s, color 0.15s;
            font-size: 12px;
        }
        #sfe-toggle:hover { background: ${BRAND.nLv4}; color: ${BRAND.nLv1}; }

        /* ── Body ────────────────────────────────────────────── */
        #sfe-body {
            transition: max-height 0.3s ease, opacity 0.25s ease;
            max-height: 500px;
            opacity: 1;
            overflow: hidden;
        }
        #sfe-body.collapsed {
            max-height: 0;
            opacity: 0;
        }

        /* ── Stats Row ───────────────────────────────────────── */
        #sfe-stats {
            display: flex;
            gap: 6px;
            padding: 10px 14px 8px;
        }
        .sfe-stat {
            flex: 1;
            background: ${BRAND.surfaceP};
            border-radius: 8px;
            padding: 7px 6px;
            text-align: center;
            border: 1px solid ${BRAND.nLv4};
        }
        .sfe-stat-val {
            font-size: 18px;
            font-weight: 700;
            line-height: 1;
            color: ${BRAND.nLv1};
        }
        .sfe-stat-val.live { color: ${BRAND.live}; }
        .sfe-stat-val.sport { color: ${BRAND.purple}; }
        .sfe-stat-lbl {
            font-size: 9.5px;
            color: ${BRAND.nLv3};
            margin-top: 3px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        /* ── Divider ─────────────────────────────────────────── */
        .sfe-divider {
            height: 1px;
            background: ${BRAND.nLv4};
            margin: 0 14px;
        }

        /* ── Section Label ───────────────────────────────────── */
        .sfe-section-lbl {
            font-size: 9.5px;
            font-weight: 600;
            color: ${BRAND.nLv3};
            text-transform: uppercase;
            letter-spacing: 0.8px;
            padding: 10px 14px 5px;
        }

        /* ── Format Toggle ───────────────────────────────────── */
        #sfe-format-row {
            display: flex;
            gap: 6px;
            padding: 0 14px 10px;
        }
        .sfe-fmt-btn {
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
            padding: 7px 0;
            border-radius: 8px;
            border: 1.5px solid ${BRAND.nLv4};
            background: ${BRAND.surfaceP};
            color: ${BRAND.nLv3};
            font-size: 12px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.15s;
            font-family: ${BRAND.font};
        }
        .sfe-fmt-btn:hover {
            border-color: ${BRAND.purple};
            color: ${BRAND.purple};
            background: ${BRAND.purpleDim};
        }
        .sfe-fmt-btn.active {
            border-color: ${BRAND.purple};
            background: ${BRAND.purpleDim};
            color: ${BRAND.purple};
        }

        /* ── Export Buttons ──────────────────────────────────── */
        #sfe-actions {
            display: flex;
            gap: 8px;
            padding: 0 14px 14px;
        }
        .sfe-btn {
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 7px;
            padding: 10px 8px;
            border-radius: 10px;
            border: none;
            font-family: ${BRAND.font};
            font-size: 13px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.18s;
            position: relative;
            overflow: hidden;
        }
        .sfe-btn::after {
            content: '';
            position: absolute;
            inset: 0;
            background: rgba(255,255,255,0);
            transition: background 0.15s;
        }
        .sfe-btn:hover::after { background: rgba(255,255,255,0.07); }
        .sfe-btn:active::after { background: rgba(255,255,255,0.14); }

        #sfe-btn-csv {
            background: linear-gradient(135deg, #1e4a2e 0%, #166534 100%);
            color: #6ee7a0;
            box-shadow: 0 2px 8px rgba(70,194,82,0.2);
        }
        #sfe-btn-csv:hover {
            box-shadow: 0 4px 16px rgba(70,194,82,0.35);
            transform: translateY(-1px);
        }
        #sfe-btn-pdf {
            background: linear-gradient(135deg, #1e1f48 0%, #2a1f5e 100%);
            color: #a5acff;
            box-shadow: 0 2px 8px rgba(122,132,255,0.2);
        }
        #sfe-btn-pdf:hover {
            box-shadow: 0 4px 16px rgba(122,132,255,0.35);
            transform: translateY(-1px);
        }
        .sfe-btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
            transform: none !important;
        }

        /* ── Progress Bar ────────────────────────────────────── */
        #sfe-progress-wrap {
            padding: 0 14px 12px;
            display: none;
        }
        #sfe-progress-wrap.visible { display: block; }
        #sfe-progress-track {
            height: 3px;
            background: ${BRAND.nLv4};
            border-radius: 2px;
            overflow: hidden;
        }
        #sfe-progress-bar {
            height: 100%;
            width: 0%;
            background: linear-gradient(90deg, ${BRAND.purple}, #a8aeff);
            border-radius: 2px;
            transition: width 0.3s ease;
        }
        #sfe-progress-lbl {
            font-size: 10px;
            color: ${BRAND.nLv3};
            margin-top: 5px;
            text-align: center;
        }

        /* ── Footer ──────────────────────────────────────────── */
        #sfe-footer {
            padding: 8px 14px;
            background: rgba(0,0,0,0.2);
            border-top: 1px solid ${BRAND.nLv4};
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
        #sfe-footer-txt {
            font-size: 9.5px;
            color: ${BRAND.nLv3};
        }
        #sfe-refresh {
            background: none;
            border: none;
            color: ${BRAND.nLv3};
            cursor: pointer;
            font-size: 11px;
            padding: 2px 5px;
            border-radius: 4px;
            transition: all 0.15s;
            font-family: ${BRAND.font};
        }
        #sfe-refresh:hover { color: ${BRAND.purple}; background: ${BRAND.purpleDim}; }

        /* ── Toast ───────────────────────────────────────────── */
        #sfe-toast {
            position: fixed;
            bottom: 24px;
            right: 18px;
            z-index: 2147483647;
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 11px 16px;
            border-radius: 10px;
            font-family: ${BRAND.font};
            font-size: 13px;
            font-weight: 500;
            color: ${BRAND.nLv1};
            background: ${BRAND.surfaceP};
            border: 1px solid ${BRAND.nLv4};
            box-shadow: 0 8px 24px rgba(0,0,0,0.5);
            transform: translateY(80px);
            opacity: 0;
            transition: transform 0.3s cubic-bezier(0.34,1.56,0.64,1), opacity 0.25s;
            pointer-events: none;
            max-width: 320px;
        }
        #sfe-toast.show {
            transform: translateY(0);
            opacity: 1;
        }
        #sfe-toast.success { border-color: ${BRAND.green}; }
        #sfe-toast.error   { border-color: ${BRAND.live}; }
        #sfe-toast.info    { border-color: ${BRAND.purple}; }
        #sfe-toast-icon { font-size: 16px; }
        #sfe-toast-msg  { flex: 1; line-height: 1.35; }
    `);

    // ─────────────────────────────────────────────────────────────────────────
    //  PANEL MARKUP
    // ─────────────────────────────────────────────────────────────────────────
    const panel = document.createElement('div');
    panel.id = 'sfe-panel';
    panel.innerHTML = `
        <div id="sfe-header">
            <div id="sfe-logo">S</div>
            <div id="sfe-title">
                Sofascore Export
                <span>Pro Data Exporter</span>
            </div>
            <button id="sfe-toggle" title="Collapse">▾</button>
        </div>

        <div id="sfe-body">
            <div id="sfe-stats">
                <div class="sfe-stat">
                    <div class="sfe-stat-val" id="sfe-cnt-total">—</div>
                    <div class="sfe-stat-lbl">Matches</div>
                </div>
                <div class="sfe-stat">
                    <div class="sfe-stat-val live" id="sfe-cnt-live">—</div>
                    <div class="sfe-stat-lbl">Live</div>
                </div>
                <div class="sfe-stat">
                    <div class="sfe-stat-val sport" id="sfe-cnt-sports">—</div>
                    <div class="sfe-stat-lbl">Sports</div>
                </div>
            </div>

            <div class="sfe-divider"></div>

            <div class="sfe-section-lbl">Scope</div>
            <div id="sfe-format-row">
                <button class="sfe-fmt-btn active" data-scope="page" title="All matches visible on this page">
                    📄 This Page
                </button>
                <button class="sfe-fmt-btn" data-scope="live" title="Only live matches">
                    🔴 Live Only
                </button>
                <button class="sfe-fmt-btn" data-scope="highlighted" title="Highlighted / featured matches">
                    ⭐ Featured
                </button>
            </div>

            <div class="sfe-divider"></div>
            <div class="sfe-section-lbl">Export</div>

            <div id="sfe-actions">
                <button class="sfe-btn" id="sfe-btn-csv">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
                    CSV
                </button>
                <button class="sfe-btn" id="sfe-btn-pdf">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><path d="M9 15h1.5a1.5 1.5 0 0 0 0-3H9v6"/></svg>
                    PDF
                </button>
            </div>

            <div id="sfe-progress-wrap">
                <div id="sfe-progress-track">
                    <div id="sfe-progress-bar"></div>
                </div>
                <div id="sfe-progress-lbl">Preparing…</div>
            </div>

            <div id="sfe-footer">
                <span id="sfe-footer-txt">sofascore.com</span>
                <button id="sfe-refresh" title="Re-scan matches">↺ Rescan</button>
            </div>
        </div>
    `;
    document.body.appendChild(panel);

    const toast = document.createElement('div');
    toast.id = 'sfe-toast';
    toast.innerHTML = `<span id="sfe-toast-icon"></span><span id="sfe-toast-msg"></span>`;
    document.body.appendChild(toast);

    // ─────────────────────────────────────────────────────────────────────────
    //  UI HELPERS
    // ─────────────────────────────────────────────────────────────────────────
    let toastTimer = null;

    function showToast(msg, type = 'success', duration = 3500) {
        const icons = { success: '✅', error: '❌', info: 'ℹ️', warning: '⚠️' };
        toast.className = `show ${type}`;
        document.getElementById('sfe-toast-icon').textContent = icons[type] ?? '•';
        document.getElementById('sfe-toast-msg').textContent = msg;
        clearTimeout(toastTimer);
        toastTimer = setTimeout(() => { toast.className = ''; }, duration);
    }

    function setProgress(pct, label) {
        const wrap = document.getElementById('sfe-progress-wrap');
        wrap.classList.toggle('visible', pct >= 0);
        if (pct >= 0) {
            document.getElementById('sfe-progress-bar').style.width = `${pct}%`;
            document.getElementById('sfe-progress-lbl').textContent = label ?? `${pct}%`;
        }
    }

    function setButtons(disabled) {
        document.getElementById('sfe-btn-csv').disabled = disabled;
        document.getElementById('sfe-btn-pdf').disabled = disabled;
    }

    function updateStats(matches) {
        const live   = matches.filter(m => m.isLive).length;
        const sports = new Set(matches.map(m => m.sport).filter(Boolean)).size;
        document.getElementById('sfe-cnt-total').textContent  = matches.length;
        document.getElementById('sfe-cnt-live').textContent   = live;
        document.getElementById('sfe-cnt-sports').textContent = sports || '—';
    }

    // ─────────────────────────────────────────────────────────────────────────
    //  COLLAPSE TOGGLE
    // ─────────────────────────────────────────────────────────────────────────
    let collapsed = false;
    document.getElementById('sfe-header').addEventListener('click', () => {
        collapsed = !collapsed;
        document.getElementById('sfe-body').classList.toggle('collapsed', collapsed);
        document.getElementById('sfe-toggle').textContent = collapsed ? '▸' : '▾';
    });

    // ─────────────────────────────────────────────────────────────────────────
    //  SCOPE BUTTONS
    // ─────────────────────────────────────────────────────────────────────────
    let currentScope = 'page';
    document.getElementById('sfe-format-row').addEventListener('click', e => {
        const btn = e.target.closest('.sfe-fmt-btn');
        if (!btn) return;
        document.querySelectorAll('.sfe-fmt-btn').forEach(b => b.classList.remove('active'));
        btn.classList.add('active');
        currentScope = btn.dataset.scope;
        const all = scrapeMatches();
        updateStats(filterByScope(all, currentScope));
    });

    // ─────────────────────────────────────────────────────────────────────────
    //  DATA SCRAPING
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Walks up the DOM from `el` looking for a tournament/category name.
     */
    function getLeagueContext(el) {
        let node = el.parentElement;
        for (let depth = 0; depth < 15 && node; depth++) {
            // Sofascore tournament headers often carry data in their class
            const classStr = node.className || '';
            const tournamentEl =
                node.querySelector('[class*="unique-tournament"]') ||
                node.querySelector('[class*="tournament-header"]') ||
                node.querySelector('[class*="league-name"]') ||
                node.querySelector('[class*="category-name"]');

            if (tournamentEl) {
                const text = tournamentEl.textContent.trim().replace(/\s+/g, ' ');
                if (text && text.length < 80) return text;
            }

            // Also look for text in a sibling element that looks like a league name
            if (node.previousElementSibling) {
                const prev = node.previousElementSibling;
                const prevText = prev.textContent.trim().replace(/\s+/g, ' ');
                if (prevText && prevText.length < 60 && /[A-Z]/.test(prevText)) {
                    const isHeader = prev.tagName === 'H1' || prev.tagName === 'H2' ||
                                     prev.tagName === 'H3' || prev.tagName === 'H4' ||
                                     (prev.className && prev.className.includes('header'));
                    if (isHeader) return prevText;
                }
            }
            node = node.parentElement;
        }

        // Fall back: extract sport from URL path segments
        return '';
    }

    /**
     * Determines if the match element is currently live.
     */
    function detectLive(anchor) {
        const html = anchor.innerHTML;
        return (
            html.includes('sofa-singles-live') ||
            html.includes('status.live') ||
            html.includes('c_status.live') ||
            anchor.querySelector('[class*="live"]') !== null
        );
    }

    /**
     * Extracts score from an event anchor.
     * Returns { home, away } or { home: '-', away: '-' } if not started.
     */
    function extractScore(anchor) {
        const scoreEls = anchor.querySelectorAll('[class*="score"]');
        if (scoreEls.length >= 2) {
            const h = scoreEls[0].textContent.trim();
            const a = scoreEls[1].textContent.trim();
            if (h && a && h !== '-') return { home: h, away: a };
        }
        // Fall back: find two adjacent numeric bdi elements
        const bdis = Array.from(anchor.querySelectorAll('bdi'));
        for (let i = 0; i < bdis.length - 1; i++) {
            if (/^\d+$/.test(bdis[i].textContent.trim()) && /^\d+$/.test(bdis[i + 1].textContent.trim())) {
                return { home: bdis[i].textContent.trim(), away: bdis[i + 1].textContent.trim() };
            }
        }
        return { home: '-', away: '-' };
    }

    /**
     * Main scraper — returns all match objects from the current page.
     */
    function scrapeMatches() {
        const seen = new Set();
        const matches = [];

        // Collect all anchor elements that link to a match
        const anchors = Array.from(document.querySelectorAll('a[href*="/match/"]'));

        anchors.forEach(anchor => {
            const href = anchor.getAttribute('href') || '';
            const idMatch = href.match(/#id:(\d+)/);
            if (!idMatch) return;

            const eventId = idMatch[1];
            if (seen.has(eventId)) return;
            seen.add(eventId);

            // Sport from URL  (first path segment: /football/match/...)
            const sportMatch = href.match(/^\/([^/]+)\/match\//);
            const sport = sportMatch ? capitalise(sportMatch[1].replace(/-/g, ' ')) : 'Sport';

            // Teams — prefer img alt (full names), fall back to bdi text
            const imgs = Array.from(anchor.querySelectorAll('img[alt]')).filter(i => i.alt);
            const homeTeam = imgs[0]?.alt || '';
            const awayTeam = imgs[1]?.alt || '';

            // Time — first bdi matching HH:MM pattern (or minute' for live)
            const bdis = Array.from(anchor.querySelectorAll('bdi')).map(b => b.textContent.trim());
            const time = bdis.find(t => /^\d{1,2}:\d{2}$/.test(t) || /^\d{1,3}'[+\d]*$/.test(t)) || bdis[0] || '—';

            // Short display names (fallback from bdi if no img)
            const shortNames = bdis.filter(t => t && !/^\d{1,2}:\d{2}$/.test(t) && !/^[-\d]$/.test(t) && t !== '—' && t.length > 1);

            const score   = extractScore(anchor);
            const isLive  = detectLive(anchor);
            const league  = getLeagueContext(anchor);

            // Determine status label
            let status = 'Scheduled';
            if (isLive)                                          status = 'Live';
            else if (score.home !== '-' && score.away !== '-')   status = 'Finished';

            matches.push({
                eventId,
                sport,
                league,
                time,
                homeTeam: homeTeam || shortNames[0] || 'Home',
                awayTeam: awayTeam || shortNames[1] || 'Away',
                homeScore: score.home,
                awayScore: score.away,
                score: score.home === '-' ? 'vs' : `${score.home} – ${score.away}`,
                isLive,
                status,
                url: 'https://www.sofascore.com' + href,
            });
        });

        return matches;
    }

    function filterByScope(matches, scope) {
        if (scope === 'live')        return matches.filter(m => m.isLive);
        if (scope === 'highlighted') {
            // Highlighted events have class event-hl-{id} on the anchor
            const hlIds = new Set(
                Array.from(document.querySelectorAll('[class*="event-hl-"]'))
                     .map(el => { const m = el.className.match(/event-hl-(\d+)/); return m ? m[1] : null; })
                     .filter(Boolean)
            );
            return matches.filter(m => hlIds.has(m.eventId));
        }
        return matches; // 'page'
    }

    function capitalise(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }

    // ─────────────────────────────────────────────────────────────────────────
    //  CSV EXPORT
    // ─────────────────────────────────────────────────────────────────────────
    function exportCSV(matches) {
        if (!matches.length) {
            showToast('No matches found on this page.', 'warning');
            return;
        }

        const escCSV = v => `"${String(v).replace(/"/g, '""')}"`;

        const headers = ['#', 'Sport', 'League / Tournament', 'Time', 'Home Team', 'Score', 'Away Team', 'Status', 'Match URL'];
        const rows = matches.map((m, i) => [
            i + 1,
            m.sport,
            m.league || '—',
            m.time,
            m.homeTeam,
            m.score,
            m.awayTeam,
            m.status,
            m.url,
        ].map(escCSV).join(','));

        // Group by sport with mini-headers
        const bySport = {};
        matches.forEach((m, i) => {
            const key = m.sport || 'Sport';
            if (!bySport[key]) bySport[key] = [];
            bySport[key].push({ ...m, _i: i });
        });

        let csvContent = `"Sofascore Export — ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}"\n`;
        csvContent += `"Page: ${window.location.href}"\n\n`;

        Object.entries(bySport).forEach(([sport, sportMatches]) => {
            csvContent += `\n"=== ${sport.toUpperCase()} ==="\n`;
            csvContent += headers.map(escCSV).join(',') + '\n';
            sportMatches.forEach((m, i) => {
                csvContent += [
                    i + 1,
                    m.sport,
                    m.league || '—',
                    m.time,
                    m.homeTeam,
                    m.score,
                    m.awayTeam,
                    m.status,
                    m.url,
                ].map(escCSV).join(',') + '\n';
            });
        });

        const bom  = '\uFEFF'; // UTF-8 BOM for Excel compatibility
        const blob = new Blob([bom + csvContent], { type: 'text/csv;charset=utf-8;' });
        const url  = URL.createObjectURL(blob);
        const a    = document.createElement('a');
        a.href     = url;
        a.download = `Sofascore_${new Date().toISOString().slice(0, 10)}_${currentScope}.csv`;
        document.body.appendChild(a);
        a.click();
        setTimeout(() => { URL.revokeObjectURL(url); a.remove(); }, 1000);

        showToast(`CSV exported — ${matches.length} match${matches.length !== 1 ? 'es' : ''}`, 'success');
    }

    // ─────────────────────────────────────────────────────────────────────────
    //  PDF EXPORT
    // ─────────────────────────────────────────────────────────────────────────
    async function exportPDF(matches) {
        if (!matches.length) {
            showToast('No matches found on this page.', 'warning');
            return;
        }

        if (!window.jspdf) {
            showToast('jsPDF library not loaded. Check @require URL.', 'error');
            return;
        }

        setButtons(true);
        setProgress(5, 'Initialising PDF…');

        const { jsPDF } = window.jspdf;
        const pdf       = new jsPDF('p', 'mm', 'a4');
        const PW        = pdf.internal.pageSize.getWidth();   // 210
        const PH        = pdf.internal.pageSize.getHeight();  // 297
        const ML        = 16; // margin left
        const MR        = 16; // margin right
        const CW        = PW - ML - MR; // content width
        const now       = new Date();
        const dateStr   = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });

        // ── Colour helpers ──
        const hexToRgb = hex => {
            const r = parseInt(hex.slice(1, 3), 16);
            const g = parseInt(hex.slice(3, 5), 16);
            const b = parseInt(hex.slice(5, 7), 16);
            return [r, g, b];
        };
        const setPurple   = () => pdf.setTextColor(122, 132, 255);
        const setWhite    = () => pdf.setTextColor(236, 237, 239);
        const setGray     = () => pdf.setTextColor(140, 148, 160);
        const setRed      = () => pdf.setTextColor(231, 59, 59);
        const setGreen    = () => pdf.setTextColor(70, 194, 82);
        const setDarkBg   = (x, y, w, h) => {
            pdf.setFillColor(23, 28, 31);
            pdf.rect(x, y, w, h, 'F');
        };
        const setCardBg   = (x, y, w, h, radius = 3) => {
            pdf.setFillColor(39, 44, 50);
            pdf.roundedRect(x, y, w, h, radius, radius, 'F');
        };
        const setAccentBar = (x, y, h = 1) => {
            pdf.setFillColor(122, 132, 255);
            pdf.rect(x, y, CW, h, 'F');
        };

        const addPageNum = () => {
            const p = pdf.internal.getCurrentPageInfo().pageNumber;
            pdf.setFontSize(8);
            setGray();
            pdf.text(`${p}`, PW / 2, PH - 8, { align: 'center' });
            pdf.text('Sofascore Pro Export', PW - MR, PH - 8, { align: 'right' });
        };

        // ── COVER PAGE ────────────────────────────────────────────────────────
        setProgress(10, 'Building cover…');

        setDarkBg(0, 0, PW, PH);

        // Top accent stripe
        pdf.setFillColor(122, 132, 255);
        pdf.rect(0, 0, PW, 4, 'F');

        // Logo box
        setCardBg(ML, 40, 28, 28, 4);
        setPurple();
        pdf.setFontSize(22);
        pdf.setFont('helvetica', 'bold');
        pdf.text('S', ML + 14, 57.5, { align: 'center' });

        // Title
        setWhite();
        pdf.setFontSize(26);
        pdf.setFont('helvetica', 'bold');
        pdf.text('Sofascore', ML + 34, 52);

        setGray();
        pdf.setFontSize(11);
        pdf.setFont('helvetica', 'normal');
        pdf.text('Pro Match Export', ML + 34, 61);

        // Separator line
        setAccentBar(ML, 80, 0.5);

        // Report metadata block
        const meta = [
            ['Date',    dateStr],
            ['Source',  window.location.hostname],
            ['Page',    document.title.slice(0, 60) || 'Sofascore'],
            ['Scope',   capitalise(currentScope)],
            ['Matches', matches.length.toString()],
        ];
        let my = 96;
        meta.forEach(([key, val]) => {
            setGray();
            pdf.setFontSize(9);
            pdf.setFont('helvetica', 'normal');
            pdf.text(key, ML, my);
            setWhite();
            pdf.setFontSize(10);
            pdf.setFont('helvetica', 'bold');
            pdf.text(val, ML + 26, my);
            my += 11;
        });

        // Summary stats boxes
        const liveCount     = matches.filter(m => m.isLive).length;
        const finishedCount = matches.filter(m => m.status === 'Finished').length;
        const sportsCount   = new Set(matches.map(m => m.sport)).size;

        const statBoxes = [
            { label: 'Total',    val: matches.length,  color: [122, 132, 255] },
            { label: 'Live',     val: liveCount,        color: [231, 59,  59]  },
            { label: 'Finished', val: finishedCount,    color: [70,  194, 82]  },
            { label: 'Sports',   val: sportsCount,      color: [168, 161, 110] },
        ];
        const boxW = (CW - 9) / 4;
        statBoxes.forEach((sb, i) => {
            const bx = ML + i * (boxW + 3);
            setCardBg(bx, 178, boxW, 30, 3);
            pdf.setFillColor(...sb.color);
            pdf.rect(bx, 178, boxW, 2.5, 'F');
            pdf.setTextColor(...sb.color);
            pdf.setFontSize(18);
            pdf.setFont('helvetica', 'bold');
            pdf.text(sb.val.toString(), bx + boxW / 2, 193, { align: 'center' });
            setGray();
            pdf.setFontSize(7.5);
            pdf.setFont('helvetica', 'normal');
            pdf.text(sb.label, bx + boxW / 2, 200, { align: 'center' });
        });

        // Footer
        pdf.setFillColor(20, 24, 27);
        pdf.rect(0, PH - 22, PW, 22, 'F');
        setGray();
        pdf.setFontSize(8);
        pdf.setFont('helvetica', 'normal');
        pdf.text('Generated by Sofascore Pro Export Tampermonkey Script', PW / 2, PH - 10, { align: 'center' });
        pdf.text(now.toISOString().replace('T', '  ').slice(0, 19), PW / 2, PH - 4, { align: 'center' });

        // ── MATCH PAGES ───────────────────────────────────────────────────────
        setProgress(25, 'Generating match tables…');

        // Group matches by sport
        const bySport = {};
        matches.forEach(m => {
            const key = m.sport || 'Sport';
            if (!bySport[key]) bySport[key] = [];
            bySport[key].push(m);
        });

        const sports = Object.keys(bySport);

        // Column widths (in mm) inside CW = 178
        const COL = {
            num:   7,
            time:  14,
            home:  42,
            score: 18,
            away:  42,
            league:36,
            status:19,
        };
        const ROW_H   = 8;
        const HEAD_H  = 6.5;
        const COL_X = {
            num:    ML,
            time:   ML + COL.num,
            home:   ML + COL.num + COL.time,
            score:  ML + COL.num + COL.time + COL.home,
            away:   ML + COL.num + COL.time + COL.home + COL.score,
            league: ML + COL.num + COL.time + COL.home + COL.score + COL.away,
            status: ML + COL.num + COL.time + COL.home + COL.score + COL.away + COL.league,
        };

        const drawTableHeader = (y) => {
            // Header BG
            pdf.setFillColor(32, 36, 42);
            pdf.rect(ML, y, CW, HEAD_H, 'F');

            // Accent bottom
            pdf.setFillColor(122, 132, 255);
            pdf.rect(ML, y + HEAD_H - 0.5, CW, 0.5, 'F');

            const headers = ['#', 'Time', 'Home Team', 'Score', 'Away Team', 'League', 'Status'];
            const xs      = Object.values(COL_X);
            const widths  = Object.values(COL);

            pdf.setFontSize(7);
            pdf.setFont('helvetica', 'bold');
            setGray();
            headers.forEach((h, i) => {
                if (i === 4 || i === 2) {
                    pdf.text(h.toUpperCase(), xs[i] + widths[i] / 2, y + 4.2, { align: 'center' });
                } else {
                    pdf.text(h.toUpperCase(), xs[i] + 1, y + 4.2);
                }
            });
            return y + HEAD_H;
        };

        const drawRow = (m, idx, y, isAlternate) => {
            if (isAlternate) {
                pdf.setFillColor(26, 30, 35);
                pdf.rect(ML, y, CW, ROW_H, 'F');
            }

            // Live badge
            if (m.isLive) {
                pdf.setFillColor(231, 59, 59, 0.15);
                pdf.setFillColor(50, 20, 20);
                pdf.rect(ML, y, CW, ROW_H, 'F');
            }

            const cy = y + ROW_H / 2 + 1.5; // vertical center for text

            // Row number
            setGray();
            pdf.setFontSize(7);
            pdf.setFont('helvetica', 'normal');
            pdf.text((idx + 1).toString(), COL_X.num + 1, cy);

            // Time
            if (m.isLive) setRed();
            else setGray();
            pdf.setFontSize(7.5);
            pdf.text(m.time, COL_X.time + 1, cy);

            // Home team (right-aligned)
            setWhite();
            pdf.setFontSize(8);
            pdf.setFont('helvetica', 'bold');
            const homeText = m.homeTeam.length > 22 ? m.homeTeam.slice(0, 20) + '…' : m.homeTeam;
            pdf.text(homeText, COL_X.home + COL.home - 2, cy, { align: 'right' });

            // Score (centred)
            if (m.isLive) setRed();
            else if (m.status === 'Finished') setGreen();
            else setGray();
            pdf.setFontSize(9);
            pdf.setFont('helvetica', 'bold');
            pdf.text(m.score, COL_X.score + COL.score / 2, cy, { align: 'center' });

            // Away team (left-aligned)
            setWhite();
            pdf.setFontSize(8);
            pdf.setFont('helvetica', 'bold');
            const awayText = m.awayTeam.length > 22 ? m.awayTeam.slice(0, 20) + '…' : m.awayTeam;
            pdf.text(awayText, COL_X.away + 2, cy);

            // League
            setGray();
            pdf.setFontSize(6.5);
            pdf.setFont('helvetica', 'normal');
            const leagueText = (m.league || '—').slice(0, 24);
            pdf.text(leagueText, COL_X.league + 1, cy);

            // Status badge
            const statusColors = {
                Live:      [231, 59,  59],
                Finished:  [70,  194, 82],
                Scheduled: [140, 148, 160],
            };
            const sc = statusColors[m.status] || statusColors.Scheduled;
            pdf.setFillColor(...sc, 0.2);
            pdf.setFillColor(sc[0] * 0.25, sc[1] * 0.25, sc[2] * 0.25);
            pdf.roundedRect(COL_X.status, y + 1.5, COL.status - 2, ROW_H - 3, 1.5, 1.5, 'F');
            pdf.setTextColor(...sc);
            pdf.setFontSize(6.5);
            pdf.setFont('helvetica', 'bold');
            pdf.text(m.status, COL_X.status + (COL.status - 2) / 2, cy, { align: 'center' });

            // Bottom row separator
            pdf.setDrawColor(255, 255, 255, 0.05);
            pdf.setLineWidth(0.1);
            pdf.setDrawColor(45, 50, 58);
            pdf.line(ML, y + ROW_H, ML + CW, y + ROW_H);
        };

        let sportIdx = 0;
        for (const [sport, sportMatches] of Object.entries(bySport)) {
            pdf.addPage();
            setDarkBg(0, 0, PW, PH);

            // Top accent
            pdf.setFillColor(122, 132, 255);
            pdf.rect(0, 0, PW, 2, 'F');

            let y = 12;

            // Sport section heading
            setPurple();
            pdf.setFontSize(15);
            pdf.setFont('helvetica', 'bold');
            pdf.text(sport, ML, y + 6);

            setGray();
            pdf.setFontSize(9);
            pdf.setFont('helvetica', 'normal');
            pdf.text(`${sportMatches.length} match${sportMatches.length !== 1 ? 'es' : ''}`, ML + 2, y + 13);

            y += 20;
            setAccentBar(ML, y, 0.4);
            y += 4;

            y = drawTableHeader(y);

            for (let i = 0; i < sportMatches.length; i++) {
                if (y + ROW_H > PH - 18) {
                    addPageNum();
                    pdf.addPage();
                    setDarkBg(0, 0, PW, PH);
                    y = 14;
                    y = drawTableHeader(y);
                }
                drawRow(sportMatches[i], i, y, i % 2 === 1);
                y += ROW_H;
            }

            addPageNum();
            setProgress(25 + Math.round((++sportIdx / sports.length) * 70), `Sport ${sportIdx}/${sports.length}…`);
        }

        // ── SAVE ──────────────────────────────────────────────────────────────
        setProgress(98, 'Saving PDF…');
        const filename = `Sofascore_${now.toISOString().slice(0, 10)}_${currentScope}.pdf`;
        pdf.save(filename);

        setProgress(-1);
        setButtons(false);
        showToast(`PDF saved — ${matches.length} match${matches.length !== 1 ? 'es' : ''} across ${sports.length} sport${sports.length !== 1 ? 's' : ''}`, 'success', 4500);
    }

    // ─────────────────────────────────────────────────────────────────────────
    //  EVENT WIRING
    // ─────────────────────────────────────────────────────────────────────────
    document.getElementById('sfe-btn-csv').addEventListener('click', () => {
        const all = scrapeMatches();
        exportCSV(filterByScope(all, currentScope));
    });

    document.getElementById('sfe-btn-pdf').addEventListener('click', async () => {
        const all = scrapeMatches();
        await exportPDF(filterByScope(all, currentScope));
    });

    document.getElementById('sfe-refresh').addEventListener('click', () => {
        const all = scrapeMatches();
        updateStats(filterByScope(all, currentScope));
        showToast(`Rescanned — ${all.length} match${all.length !== 1 ? 'es' : ''} found`, 'info', 2500);
    });

    // ─────────────────────────────────────────────────────────────────────────
    //  INIT — wait for React to render, then scan
    // ─────────────────────────────────────────────────────────────────────────
    async function init() {
        // Sofascore is a React SPA; wait for match links to appear
        let attempts = 0;
        while (attempts < 30) {
            const links = document.querySelectorAll('a[href*="/match/"]');
            if (links.length > 0) break;
            await new Promise(r => setTimeout(r, 500));
            attempts++;
        }

        const all = scrapeMatches();
        updateStats(filterByScope(all, currentScope));

        // Update footer with current page name
        const pageLabel = document.title.replace(' | Sofascore', '').slice(0, 30) || 'sofascore.com';
        document.getElementById('sfe-footer-txt').textContent = pageLabel;

        if (all.length === 0) {
            showToast('No match links detected yet. Try rescanning after page loads.', 'info', 5000);
        }

        // Re-scan when the SPA navigates (Sofascore uses pushState)
        const origPush = history.pushState.bind(history);
        history.pushState = (...args) => {
            origPush(...args);
            setTimeout(() => {
                const updated = scrapeMatches();
                updateStats(filterByScope(updated, currentScope));
            }, 1500);
        };
        window.addEventListener('popstate', () => {
            setTimeout(() => {
                const updated = scrapeMatches();
                updateStats(filterByScope(updated, currentScope));
            }, 1500);
        });
    }

    // Small delay to let Tampermonkey inject required libs
    await new Promise(r => setTimeout(r, 800));
    init();

    console.log('%c✅ Sofascore Pro Export v2.0 loaded!', 'color:#7a84ff;font-weight:bold;font-size:13px');
})();
