// ============================================================
// MEMBERSHIP GATE
// Plans with access: Trial (active), Novice, Professional, Prestige.
// ============================================================

const FAST_TRACKER_ALLOWED_PLANS = ['trial', 'novice', 'professional', 'prestige'];

async function verifyFastTrackerAccess() {
    const { _trackerUid } = await chrome.storage.local.get('_trackerUid');
    if (!_trackerUid) {
        showFastTrackerGateModal('none', null);
        return false;
    }

    const result = await new Promise(resolve => {
        chrome.runtime.sendMessage({ type: 'checkMembership' }, res => {
            void chrome.runtime.lastError;
            resolve(res || null);
        });
    });

    if (result?.allowed === true) return true;

    const { _cachedPlan } = await chrome.storage.local.get('_cachedPlan');
    if (_cachedPlan && FAST_TRACKER_ALLOWED_PLANS.includes(_cachedPlan.toLowerCase().trim())) {
        return true;
    }

    showFastTrackerGateModal(result?.membership ?? null, _cachedPlan ?? null);
    return false;
}

function showFastTrackerGateModal(membership, cachedPlan) {
    const existing = document.getElementById('ss-fast-tracker-gate-modal');
    if (existing) existing.remove();

    let bodyText;
    if (membership === 'expired') {
        bodyText = 'Your membership has <strong style="color:#f87171">expired</strong>. Please renew to continue using the Fast Tracker.';
    } else if (cachedPlan && !FAST_TRACKER_ALLOWED_PLANS.includes(cachedPlan.toLowerCase().trim())) {
        bodyText = `Your current plan <strong style="color:#f87171">${cachedPlan}</strong> does not have Fast Tracker access.`;
    } else {
        bodyText = 'No active membership was found. Please log in through the website.';
    }

    const overlay = document.createElement('div');
    overlay.id = 'ss-fast-tracker-gate-modal';
    overlay.style.cssText = [
        'position:fixed', 'inset:0', 'z-index:99999',
        'background:rgba(0,0,0,0.65)', 'backdrop-filter:blur(4px)',
        'display:flex', 'align-items:center', 'justify-content:center',
    ].join(';');

    overlay.innerHTML = `
        <div style="background:#0f1624;border:1px solid #2a3a5c;border-radius:16px;
                    padding:44px 48px;max-width:480px;width:90%;text-align:center;
                    box-shadow:0 12px 60px rgba(0,0,0,0.7);">
            <div style="font-size:52px;margin-bottom:18px;">🔒</div>
            <h2 style="color:#e2e8f0;font-size:22px;margin:0 0 12px 0;font-weight:700;">Access Restricted</h2>
            <p style="color:#94a3b8;font-size:15px;margin:0 0 20px 0;line-height:1.7;">
                ${bodyText}
            </p>
            <p style="color:#64748b;font-size:13px;margin:0 0 28px 0;line-height:1.6;">
                Eligible plans:<br>
                Trial  ·  Novice  ·  Professional  ·  Prestige
            </p>
            <button id="ss-ft-gate-close"
                style="background:#1e3a5f;color:#e2e8f0;border:1px solid #2a4a7f;
                       border-radius:8px;padding:12px 36px;cursor:pointer;font-size:15px;font-weight:600;">
                Close
            </button>
        </div>`;

    document.body.appendChild(overlay);
    document.getElementById('ss-ft-gate-close').addEventListener('click', () => overlay.remove());
    overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });
}

// ============================================================
// eBay Dropshipping Tool - Chrome Extension Content Script
// Deobfuscated from minified/obfuscated source
// ============================================================

// ============================================================
// SECTION 1: PapaParse CSV Library (bundled, standard OSS lib)
// ============================================================
// (Omitted for brevity — this is the unmodified PapaParse library
//  available at https://github.com/mholt/PapaParse)



// ============================================================
// SECTION 3: CONSTANTS & STATE
// ============================================================

let blockCount = 0;           // Number of times Amazon has soft-blocked us
let blockTimer = null;        // Timer reference for block cooldown
let isBlockCooldown = false;  // Whether we're currently in a block cooldown

// Maps country codes to Amazon/eBay TLDs
const COUNTRY_TLD_MAP = {
    'US': 'com',
    'CA': 'ca',
    'UK': 'co.uk',
    'AU': 'com.au',
    'DE': 'de',
    'FR': 'fr',
    'IT': 'it',
    'ES': 'es'
};

// Regex patterns to validate supplier SKU/custom label formats
const SKU_PATTERNS = {
    'home_depot': /^\d{9}$/,
    'amazon': /==$/          // Base64-encoded ASINs end with ==
};

// Runtime counters & state
let processedItemCount = 0;    // How many items have been processed this session
let timerInterval;             // Setinterval reference for the run timer
let timerStartTime;            // Timestamp when processing started
let runTimerEl = document.getElementById('runTimer');
let errorCount = 0;

// ============================================================
// SECTION 4: DOM ELEMENT REFERENCES
// ============================================================

const fileInput = document.getElementById('fileInput');
const supplierSelect = document.getElementById('supplierSelect');
const listingSiteSelect = document.getElementById('listingSiteSelect');
const threadCountInput = document.getElementById('threadCount');
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const clearRawBtn = document.getElementById('clearRawBtn');
const clearProcessedBtn = document.getElementById('clearProcessedBtn');
const exportBtn = document.getElementById('exportBtn');
const resetFiltersBtn = document.getElementById('resetFiltersBtn');
const rawTable = document.getElementById('rawTable');
const processedTable = document.getElementById('processedTable');
const rawCountEl = document.getElementById('rawCount');
const processedCountEl = document.getElementById('processedCount');
const importStatusEl = document.getElementById('importStatus');
const importCountEl = document.getElementById('importCount');
const positionInput = document.getElementById('positionInput');
const downloadActiveListingsBtn = document.getElementById('downloadActiveListingsBtn');
const uploadToEbayCheckbox = document.getElementById('uploadToEbayCheckbox');
const priceEndingInput = document.getElementById('priceEndingInput');

// ============================================================
// SECTION 5: RUNTIME STATE VARIABLES
// ============================================================

let selectedFile = null;   // The currently loaded CSV file object
let rawRows = [];     // Parsed rows from the imported CSV
let csvHeaders = [];     // Column headers from the CSV
let retryQueue = [];     // Items queued for retry (ERROR_503)
let processedRows = [];     // Results after price/stock lookup
let currentIndex = 0;      // Index into rawRows for next item to process
let isPaused = true;   // Whether processing is paused
let concurrency = parseInt(threadCountInput.value, 10) || 1; // Worker thread count
let saveDebounceTimer;        // Debounce timer for saving state to chrome.storage
let pendingSaveTimeout = null;
let retryList = [];     // Subset of processedRows that failed with ERROR_503
let isVeroMode = false;  // Whether VERO (brand protection) scan mode is active
let sortAscending = true;   // Sort direction for the status column

// VERO scan checkbox state
const scanVeroCheckbox = document.getElementById('scanVeroCheckbox');
const scanBlacklistCheckbox = document.getElementById('scanBlacklistCheckbox');

// Restore VERO/blacklist scan preferences from storage
chrome.storage.local.get(['scanVero', 'scanBlacklist'], ({ scanVero, scanBlacklist }) => {
    if (scanVeroCheckbox) scanVeroCheckbox.checked = !!scanVero;
    if (scanBlacklistCheckbox) scanBlacklistCheckbox.checked = !!scanBlacklist;
    refreshUIForVeroMode();
});

scanVeroCheckbox && scanVeroCheckbox.addEventListener('change', () => {
    chrome.storage.local.set({ scanVero: scanVeroCheckbox.checked });
    refreshUIForVeroMode();
});

scanBlacklistCheckbox && scanBlacklistCheckbox.addEventListener('change', () => {
    chrome.storage.local.set({ scanBlacklist: scanBlacklistCheckbox.checked });
    refreshUIForVeroMode();
});


// ============================================================
// SECTION 6: INITIALIZATION
// ============================================================

function loadSavedState() {
    chrome.storage.local.get(
        ['currentIndex', 'isPaused', 'processedRows', 'supplierType', 'listingSite', 'domain', 'priceEndingFilter'],
        (saved) => {
            currentIndex = saved.currentIndex || 0;
            isPaused = saved.isPaused ?? true;
            processedRows = saved.processedRows || [];
            supplierSelect.value = saved.supplierType || 'amazon';

            // Resolve listing site from saved value or domain TLD
            let listingSite = saved.listingSite;
            if (!listingSite || !COUNTRY_TLD_MAP[listingSite]) {
                // Reverse-lookup TLD -> country code
                const tldToCountry = Object.fromEntries(
                    Object.entries(COUNTRY_TLD_MAP).map(([country, tld]) => [tld, country])
                );
                listingSite = tldToCountry[saved.domain] || 'US';
            }
            listingSiteSelect.value = listingSite;
            priceEndingInput.value = saved.priceEndingFilter || '';
            positionInput.value = currentIndex;

            // Deduplicate processedRows by ItemID (keep last occurrence)
            const seenIds = new Set();
            processedRows = processedRows
                .slice()
                .reverse()
                .filter(row => {
                    if (seenIds.has(row.ItemID)) return false;
                    seenIds.add(row.ItemID);
                    return true;
                })
                .reverse();

            chrome.storage.local.set({ processedRows });
            renderProcessedTable();
            updateCounts();

            // Re-enable start if there are raw rows or retryable items
            startBtn.disabled = !(
                rawRows.length ||
                processedRows.some(r => r.Status?.includes('ERROR_503'))
            );
            pauseBtn.disabled = true;
        }
    );
}

function initializeEventListeners() {
    const importProcessedBtn = document.getElementById('importProcessedBtn');
    document.getElementById('exportProcessedBtn').addEventListener('click', exportProcessedCSV);
    importProcessedBtn.addEventListener('click', () => document.getElementById('processedFileInput').click());
    document.getElementById('processedFileInput').addEventListener('change', handleProcessedImport);

    processedTable && processedTable.addEventListener('click', handleProcessedTableClick);
    fileInput.addEventListener('change', handleFileInputChange);

    supplierSelect.addEventListener('change', () => {
        chrome.storage.local.set({ supplierType: supplierSelect.value });
        reloadAndFilterFile();
    });

    listingSiteSelect.addEventListener('change', () => {
        chrome.storage.local.set({ listingSite: listingSiteSelect.value });
        reloadAndFilterFile();
    });

    resetFiltersBtn.addEventListener('click', () => {
        supplierSelect.value = '';
        listingSiteSelect.value = '';
        priceEndingInput.value = '';
        chrome.storage.local.remove('priceEndingFilter');
        reloadAndFilterFile();
    });

    priceEndingInput.addEventListener('change', () => {
        chrome.storage.local.set({ priceEndingFilter: priceEndingInput.value.trim() });
        reloadAndFilterFile();
    });

    positionInput.addEventListener('blur', handlePositionChange);
    threadCountInput.addEventListener('change', handleThreadCountChange);
    startBtn.addEventListener('click', handleStartClick);
    pauseBtn.addEventListener('click', handlePauseClick);
    clearRawBtn.addEventListener('click', clearRawData);
    clearProcessedBtn.addEventListener('click', clearProcessedData);

    exportBtn.addEventListener('click', () => {
        if (typeof window.openExportModal === 'function') {
            window.openExportModal();
        } else {
            console.error('Modal script not loaded.');
        }
    });

    document.getElementById('statusSortHeader')?.addEventListener('click', () => {
        const spinner = document.getElementById('statusSortSpinner');
        const header = document.getElementById('statusSortHeader');
        header.style.pointerEvents = 'none';
        spinner.classList.remove('hidden');
        sortAscending = !sortAscending;
        setTimeout(() => {
            sortProcessedByStatus();
            spinner.classList.add('hidden');
            header.style.pointerEvents = 'auto';
        }, 50);
    });
}

// Initialize on load
loadSavedState();
initializeEventListeners();
initExportModal();


// ============================================================
// SECTION 7: VERO MODE (Brand Protection Scanning)
// ============================================================

const veroModeBadge = document.getElementById('veroModeBadge');
const exportVeroNullBtn = document.getElementById('exportVeroNullBtn');
const exportVeroEndBtn = document.getElementById('exportVeroEndBtn');
const updateListingsMainBtn = document.getElementById('exportBtn');
const veroEasterEggBtn = document.getElementById('veroEasterV');
const scanTogglesEl = document.getElementById('scanToggles');
const quickStartBtn = document.getElementById('quickStartBtn');
const retryFailedBtn = document.getElementById('retryFailedBtn');

// Stores VERO match details per item: Map<ItemID, { hasVero, matches, amazonUrl }>
const veroDetailsMap = new Map();

// Hidden Easter egg: click "V" to toggle VERO mode
if (veroEasterEggBtn) {
    veroEasterEggBtn.addEventListener('click', () => {
        toggleVeroMode(!isVeroMode, true);
    });
    veroEasterEggBtn.addEventListener('mouseenter', () => {
        veroEasterEggBtn.title = isVeroMode ? 'Exit VERO mode' : 'Enter VERO mode';
    });
}

function toggleVeroMode(enabled, isManualToggle = false) {
    isVeroMode = !!enabled;

    veroModeBadge && veroModeBadge.classList.toggle('hidden', !isVeroMode);
    exportVeroNullBtn && exportVeroNullBtn.classList.toggle('hidden', !isVeroMode);
    exportVeroEndBtn && exportVeroEndBtn.classList.toggle('hidden', !isVeroMode);
    updateListingsMainBtn && updateListingsMainBtn.classList.toggle('hidden', isVeroMode);
    scanTogglesEl && scanTogglesEl.classList.toggle('hidden', !isVeroMode);
    quickStartBtn && (quickStartBtn.style.display = isVeroMode ? 'none' : '');
    retryFailedBtn && (retryFailedBtn.style.display = isVeroMode ? 'none' : '');

    // Clear price ending filter in VERO mode
    if (priceEndingInput && isVeroMode) priceEndingInput.value = '';

    if (isManualToggle && isVeroMode) {
        supplierSelect.value = 'amazon';
        chrome.storage.local.set({ supplierType: 'amazon' });

        // Auto-enable VERO scan checkbox if neither is checked
        if (scanVeroCheckbox && !scanVeroCheckbox.checked && !scanBlacklistCheckbox?.checked) {
            scanVeroCheckbox.checked = true;
            chrome.storage.local.set({ scanVero: true });
        }
        veroDetailsMap.clear();
    }

    if (veroEasterEggBtn) {
        veroEasterEggBtn.title = isVeroMode ? 'Exit VERO mode' : 'Enter VERO mode';
    }

    renderProcessedTable();
}

function refreshUIForVeroMode(isManualToggle = false) {
    veroModeBadge && veroModeBadge.classList.toggle('hidden', !isVeroMode);
    exportVeroNullBtn && exportVeroNullBtn.classList.toggle('hidden', !isVeroMode);
    exportVeroEndBtn && exportVeroEndBtn.classList.toggle('hidden', !isVeroMode);
    updateListingsMainBtn && updateListingsMainBtn.classList.toggle('hidden', isVeroMode);
    scanTogglesEl && scanTogglesEl.classList.toggle('hidden', !isVeroMode);
    quickStartBtn && (quickStartBtn.style.display = isVeroMode ? 'none' : '');
    retryFailedBtn && (retryFailedBtn.style.display = isVeroMode ? 'none' : '');

    if (priceEndingInput && isVeroMode) priceEndingInput.value = '';

    if (isManualToggle && isVeroMode) {
        supplierSelect.value = 'amazon';
        chrome.storage.local.set({ supplierType: 'amazon' });
        if (scanVeroCheckbox && !scanVeroCheckbox.checked && !scanBlacklistCheckbox?.checked) {
            scanVeroCheckbox.checked = true;
            chrome.storage.local.set({ scanVero: true });
        }
    }

    if (veroEasterEggBtn) {
        veroEasterEggBtn.title = isVeroMode ? 'Exit VERO mode' : 'Enter VERO mode';
    }

    renderProcessedTable();
}

exportVeroNullBtn && exportVeroNullBtn.addEventListener('click', exportVeroNullifyCSV);
exportVeroEndBtn && exportVeroEndBtn.addEventListener('click', exportVeroEndCSV);


// ============================================================
// SECTION 8: WAITING / POLLING UTILITIES
// ============================================================

/** Polls until the raw import finishes (import status element is hidden & rows exist) */
async function waitForRawImport(timeoutMs = 60000) {
    const startTime = Date.now();
    return new Promise((resolve, reject) => {
        const check = () => {
            try {
                if (importStatusEl?.classList?.contains('hidden') && Array.isArray(rawRows) && rawRows.length > 0) {
                    return resolve();
                }
            } catch (e) { }
            if (Date.now() - startTime > timeoutMs) {
                return reject(new Error('Timed out waiting for raw import'));
            }
            setTimeout(check, 200);
        };
        check();
    });
}

/** Normalizes a value into a clean string array (handles arrays, comma/newline-separated strings) */
function toStringArray(value) {
    if (!value) return [];
    if (Array.isArray(value)) {
        return value.filter(Boolean).map(String).map(s => s.trim()).filter(Boolean);
    }
    if (typeof value === 'string') {
        return value.split(/\r?\n|,/g).map(s => s.trim()).filter(Boolean);
    }
    return [];
}


// ============================================================
// SECTION 9: VERO SCANNING (per-item)
// ============================================================

async function scanItemForVero(rawRow, { veroList, blacklistList }) {
    const itemId = rawRow['Item number'];
    const customLabel = rawRow['Custom label (SKU)'];
    const isValidSku = SKU_PATTERNS[supplierSelect.value]?.test(customLabel);
    const amazonUrl = 'https://www.amazon.' + (COUNTRY_TLD_MAP[listingSiteSelect.value] || 'com')
        + '/dp/' + atob(customLabel) + '?th=1&psc=1';

    if (!isValidSku) {
        return { itemId: Number(itemId), hasVero: false, matches: [], amazonUrl };
    }

    const pageText = (await fetchAmazonPageText(customLabel, amazonUrl)).toLowerCase();
    const matchedTerms = new Set();

    for (const term of veroList) {
        if (term && pageText.includes(term.toLowerCase())) matchedTerms.add(term);
    }
    for (const term of blacklistList) {
        if (term && pageText.includes(term.toLowerCase())) matchedTerms.add(term);
    }

    const hasVero = matchedTerms.size > 0;
    const resultRow = buildResultRow(itemId, customLabel, '', '', hasVero ? 'VERO_MATCH' : 'OK', amazonUrl, '');

    processedRows.push(resultRow);
    appendProcessedRow(resultRow);
    updateCounts();
    scheduleSave();

    return {
        itemId: Number(itemId),
        hasVero,
        matches: Array.from(matchedTerms),
        amazonUrl
    };
}

function fetchAmazonPageText(customLabel, itemUrl) {
    return new Promise((resolve, reject) => {
        chrome.runtime.sendMessage(
            { type: 'fetch_amazon_text', itemUrl },
            (response) => {
                if (!response) return reject(new Error('No response'));
                if (response.error) return reject(new Error(response.error));
                resolve(response.text || '');
            }
        );
    });
}


// ============================================================
// SECTION 10: VERO CSV EXPORTS
// ============================================================

/** Export a "nullify" CSV to set VERO-flagged listings to a placeholder image / null content */
function exportVeroNullifyCSV() {
    const veroRows = getVeroFlaggedRows();
    const uniqueIds = Array.from(new Set(veroRows.map(r => String(r.ItemID ?? '')).filter(Boolean)));

    if (!uniqueIds.length) {
        alert('No VERO items flagged.');
        return;
    }

    const columns = ['Action', 'ItemID', 'StartPrice', 'Quantity', 'EndingReason',
        'Title', 'Description', 'ItemSpecifics', 'PictureURL'];
    const lines = [columns.join(',')];

    for (const id of uniqueIds) {
        const row = {
            Action: 'Revise',
            ItemID: id,
            StartPrice: '',
            Quantity: '',
            EndingReason: '',
            Title: 'null',
            Description: 'null',
            ItemSpecifics: 'null',
            PictureURL: 'https://e7.pngegg.com/pngimages/52/84/png-clipart-no-symbol-icon-cancel-s-angle-text-thumbnail.png'
        };
        lines.push(columns.map(col => csvEscape(row[col])).join(','));
    }

    downloadCSV(lines.join('\r\n'), 'vero_nullify_' + new Date().toISOString().slice(0, 10) + '.csv');
}

/** Export an "end item" CSV to end all VERO-flagged listings */
function exportVeroEndCSV() {
    const veroRows = getVeroFlaggedRows();
    const uniqueIds = Array.from(new Set(veroRows.map(r => String(r.ItemID ?? '')).filter(Boolean)));

    if (!uniqueIds.length) {
        alert('No VERO items flagged.');
        return;
    }

    const columns = ['Action', 'ItemID', 'StartPrice', 'Quantity', 'EndingReason'];
    const lines = [columns.join(',')];

    for (const id of uniqueIds) {
        const row = {
            Action: 'End',
            ItemID: id,
            StartPrice: '',
            Quantity: '',
            EndingReason: 'NotAvailable'
        };
        lines.push(columns.map(col => csvEscape(row[col])).join(','));
    }

    downloadCSV(lines.join('\r\n'), 'vero_end_' + new Date().toISOString().slice(0, 10) + '.csv');
}

/** Wraps a value in CSV-safe double quotes */
function csvEscape(value) {
    return '"' + String(value ?? '').replace(/"/g, '""') + '"';
}

/** Triggers a browser download for a CSV string */
function downloadCSV(csvString, filename) {
    const blob = new Blob([csvString], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
}


// ============================================================
// SECTION 11: PROCESSED TABLE — IMPORT / EXPORT
// ============================================================

function exportProcessedCSV() {
    if (!processedRows.length) {
        alert('No processed rows to export.');
        return;
    }

    const columns = ['Action', 'ItemID', 'CustomLabel', 'StartPrice', 'Quantity', 'Brand', 'Status'];
    const lines = [
        columns.join(','),
        ...processedRows.map(row =>
            columns.map(col => csvEscape(row[col])).join(',')
        )
    ];

    const blob = new Blob([lines.join('\r\n')], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'processed_items_' + new Date().toISOString().slice(0, 10) + '.csv';
    a.click();
    URL.revokeObjectURL(url);
}

function handleProcessedImport() {
    const file = document.getElementById('processedFileInput').files[0];
    if (!file) return;

    const reader = new FileReader();
    reader.onload = (e) => {
        const text = e.target.result.trim();

        // Try JSON first
        if (text.startsWith('[') && text.endsWith(']')) {
            try {
                processedRows = JSON.parse(text);
                renderProcessedTable();
                updateCounts();
                scheduleSave();
                updateErrorCount();
                return;
            } catch (err) {
                console.error('Failed to parse JSON. Trying CSV next...');
            }
        }

        // Fall back to CSV via PapaParse
        Papa.parse(text, {
            header: true,
            skipEmptyLines: true,
            complete: (results) => {
                const rows = results.data;
                if (rows.length && rows[0].ItemID && rows[0].CustomLabel) {
                    processedRows = rows.map(r => ({
                        Action: r.Action || 'Revise',
                        ItemID: Number(r.ItemID),
                        CustomLabel: r.CustomLabel,
                        StartPrice: r.StartPrice,
                        Quantity: r.Quantity,
                        Brand: r.Brand || '',
                        Status: r.Status || 'Unknown',
                        itemUrl: ''
                    }));
                    renderProcessedTable();
                    updateCounts();
                    scheduleSave();
                    updateErrorCount();
                } else {
                    alert('Invalid CSV structure for processed import.');
                }
            }
        });
    };
    reader.readAsText(file);
}


// ============================================================
// SECTION 12: RAW FILE INPUT & FILTERING
// ============================================================

function handleFileInputChange() {
    const file = fileInput.files[0];
    if (file) {
        selectedFile = file;
        reloadAndFilterFile();
    }
}

function reloadAndFilterFile() {
    if (!selectedFile) return;

    importStatusEl.classList.remove('hidden');
    rawRows = [];
    csvHeaders = [];
    showLoadingOverlay();

    let debounceTimer;
    const reader = new FileReader();
    reader.onload = () => {
        // Detect delimiter: if more semicolons than commas in first 1000 chars, use semicolon
        const sample = reader.result.slice(0, 1000);
        const commaCount = (sample.match(/,/g) || []).length;
        const semiCount = (sample.match(/;/g) || []).length;
        const delimiter = semiCount > commaCount ? ';' : ',';

        console.log('🕵️ Detected delimiter:', JSON.stringify(delimiter));

        Papa.parse(selectedFile, {
            header: true,
            skipEmptyLines: true,
            dynamicTyping: true,
            worker: true,
            delimiter,
            chunk: ({ data }) => {
                // Parse price-ending filters like ".99", "0.99"
                const priceEndings = priceEndingInput.value
                    .split(',')
                    .map(s => s.trim())
                    .filter(s => s !== '')
                    .map(s => s.startsWith('.') ? s : '.' + s);

                const filtered = data.filter(row => {
                    const sku = row['Custom label (SKU)'];
                    const site = row['Listing site'];
                    const supplier = supplierSelect.value;
                    const listing = listingSiteSelect.value;

                    const matchesSupplier = !supplier || SKU_PATTERNS[supplier]?.test(sku);
                    const matchesSite = !listing || site === listing;

                    let matchesPrice = true;
                    if (priceEndings.length > 0) {
                        const priceStr = row['Start price'] != null ? String(row['Start price']) : '';
                        matchesPrice = priceEndings.some(ending => priceStr.endsWith(ending));
                    }

                    return matchesSupplier && matchesSite && matchesPrice;
                });

                rawRows.push(...filtered);

                clearTimeout(debounceTimer);
                debounceTimer = setTimeout(() => {
                    importCountEl.textContent = rawRows.length;
                }, 100);
            },
            complete: () => {
                console.log('✅ All chunks parsed. Total filtered rows:', rawRows.length);
                csvHeaders = rawRows.length ? Object.keys(rawRows[0]) : [];

                const savedPos = parseInt(positionInput.value, 10);
                if (!isNaN(savedPos) && savedPos >= 0 && savedPos < rawRows.length) {
                    currentIndex = savedPos;
                }
                positionInput.value = currentIndex;

                // Render raw table (idle callback if available)
                if ('requestIdleCallback' in window) {
                    requestIdleCallback(() => { renderRawTable(); hideLoadingOverlay(); });
                } else {
                    setTimeout(() => { renderRawTable(); hideLoadingOverlay(); }, 50);
                }

                renderProcessedTable();
                updateCounts();
                startBtn.disabled = false;
                pauseBtn.disabled = false;
                importStatusEl.classList.add('hidden');
                scheduleSave();
            }
        });
    };
    reader.readAsText(selectedFile);
}


// ============================================================
// SECTION 13: UI EVENT HANDLERS
// ============================================================

function handlePositionChange() {
    const pos = parseInt(positionInput.value, 10);
    if (!isNaN(pos) && pos >= 0) {
        currentIndex = pos;
        if (rawRows.length > 0) {
            if ('requestIdleCallback' in window) {
                requestIdleCallback(() => renderRawTable());
            } else {
                setTimeout(renderRawTable, 50);
            }
        }
        updateCounts();
        scheduleSave();
    }
}

function handleThreadCountChange() {
    concurrency = parseInt(threadCountInput.value, 10) || 1;
}


// ============================================================
// SECTION 14: MAIN PROCESSING LOOP
// ============================================================

async function handleStartClick() {
    // Membership gate
    const hasAccess = await verifyFastTrackerAccess();
    if (!hasAccess) return;
    // ------------------------------------------------

    // If a file import is still in progress, wait for it
    if (importStatusEl && !importStatusEl.classList.contains('hidden')) {
        try { await waitForRawImport(60000); } catch (e) { }
    }

    // Clear any existing auto-upload interval
    if (autoUploadInterval) {
        clearInterval(autoUploadInterval);
        autoUploadInterval = null;
    }

    const hasRetryItems = processedRows.some(r => r.Status?.includes('ERROR_503'));
    if (!(rawRows.length > 0 || hasRetryItems)) {
        alert('No raw rows or failed items to process.');
        return;
    }

    startBtn.disabled = true;
    pauseBtn.disabled = false;

    const prevProcessedCount = processedRows.length;

    // Collect items that need to be retried
    retryList = processedRows.filter(r => r.Status?.includes('ERROR_503'));
    const hasRetries = retryList.length > 0;
    const expectedTotal = prevProcessedCount + (rawRows.length + retryList.length);

    console.log(`Retrying ${retryList.length} items, then processing ${rawRows.length} raw items.`);
    console.log('Expected processedRows length when done:', expectedTotal);

    isPaused = false;
    processedItemCount = 0;
    clearInterval(timerInterval);
    timerStartTime = Date.now();
    updateRunTimer();
    timerInterval = setInterval(updateRunTimer, 1000);

    // Process retries first
    if (hasRetries) {
        const retryWorkers = Array.from({ length: concurrency }, (_, i) => processRetryQueue(i));
        await Promise.all(retryWorkers);
    }

    // If in VERO mode, make sure at least one scan type is enabled
    let inVeroMode = !!isVeroMode;
    if (!inVeroMode || scanVeroCheckbox?.checked || scanBlacklistCheckbox?.checked) {
        // ok
    } else if (scanVeroCheckbox) {
        scanVeroCheckbox.checked = true;
    }
    if (inVeroMode) refreshUIForVeroMode?.();

    // Launch concurrent workers
    for (let i = 0; i < concurrency; i++) {
        processNextItem(i);
    }

    // If "auto-upload to eBay" is checked, watch for completion then upload
    if (uploadToEbayCheckbox.checked) {
        autoUploadInterval = setInterval(() => {
            if (processedRows.length >= expectedTotal) {
                clearInterval(autoUploadInterval);
                autoUploadInterval = null;
                console.log('All rows done; checkbox was checked → calling updateListings()');
                updateListings();
            }
        }, 500);
    }
}

let autoUploadInterval = null;

/** Worker: drains the retryList queue */
async function processRetryQueue(workerId = 0) {
    while (!isPaused && retryList.length > 0) {
        console.log('Processing retry queue, items left: ' + retryList.length);

        const item = retryList.shift();
        const customLabel = item.CustomLabel;
        const itemId = item.ItemID;
        const fakeRawRow = { 'Custom label (SKU)': customLabel, 'Item number': itemId };

        const rawIdx = rawRows.findIndex(r => r['Custom label (SKU)'] === customLabel);
        if (rawIdx !== -1) setRowStatus(rawIdx, 'processing');

        const result = await fetchAndBuildResult(fakeRawRow, workerId);

        if (rawIdx !== -1) setRowStatus(rawIdx, 'done');

        // Replace existing row in processedRows
        const existingIdx = processedRows.findIndex(r => r.CustomLabel === customLabel);
        if (existingIdx !== -1) processedRows.splice(existingIdx, 1);

        processedRows.push(result);
        appendProcessedRow(result, true);
        updateCounts();
        scheduleSave();
    }
}

function handlePauseClick() {
    isPaused = true;
    clearInterval(timerInterval);
    scheduleSave();
    startBtn.disabled = false;
    pauseBtn.disabled = true;
}

function updateRunTimer() {
    const elapsed = Math.floor((Date.now() - timerStartTime) / 1000);
    const minutes = String(Math.floor(elapsed / 60)).padStart(2, '0');
    const seconds = String(elapsed % 60).padStart(2, '0');
    runTimerEl.textContent = `Time: ${minutes}:${seconds}`;
}

function updateProcessedItemCountDisplay() {
    document.getElementById('processedItemsCount').textContent = processedItemCount;
}

/** Worker: processes a single item from rawRows at currentIndex, then recurses */
async function processNextItem(workerId = 0) {
    if (isPaused || currentIndex >= rawRows.length) return;

    const myIndex = currentIndex++;
    setRowStatus(myIndex, 'processing');

    const result = isVeroMode
        ? await processItemVeroMode(rawRows[myIndex], workerId)
        : await fetchAndBuildResult(rawRows[myIndex], workerId);

    setRowStatus(myIndex, 'done');
    processedRows.push(result);
    appendProcessedRow(result);
    updateCounts();
    scheduleSave();

    console.log(`Processor ${workerId} processed item ${myIndex + 1}/${rawRows.length}:`, result);

    // Recurse to process the next item
    processNextItem(workerId);
}

/** Processes an item in VERO scan mode */
async function processItemVeroMode(rawRow, workerId = 0) {
    const { blacklist } = await chrome.storage.local.get(['blacklist']);
    toStringArray(blacklist); // normalize (side-effect: used downstream)

    const customLabel = rawRow['Custom label (SKU)'];
    const itemId = rawRow['Item number'];
    const isValidSku = SKU_PATTERNS[supplierSelect.value]?.test(customLabel);
    const amazonUrl = 'https://www.amazon.' + (COUNTRY_TLD_MAP[listingSiteSelect.value] || 'com')
        + '/dp/' + atob(customLabel) + '?th=1&psc=1';

    if (!isValidSku) return buildResultRow(itemId, customLabel, '', '', 'Skipped', amazonUrl, '');

    try {
        const evalResult = await new Promise((resolve, reject) => {
            chrome.runtime.sendMessage(
                { type: 'eval_amazon_vero_blacklist', url: amazonUrl, workerId },
                (response) => {
                    if (chrome.runtime.lastError) return reject(chrome.runtime.lastError);
                    if (!response) return reject(new Error('No response from eval'));
                    resolve(response);
                }
            );
        });

        // Collect matches from background page evaluation
        let matches = { vero: [], blacklist: [] };

        if (scanVeroCheckbox?.checked && isVeroMode) {
            matches.vero = Array.isArray(evalResult?.veroMatches)
                ? evalResult.veroMatches
                : evalResult?.isVero ? ['VERO_BRAND'] : [];
        }
        if (scanBlacklistCheckbox?.checked && isVeroMode) {
            matches.blacklist = Array.isArray(evalResult?.blacklistMatches)
                ? evalResult.blacklistMatches : [];
        }

        const hasVeroMatch = matches.vero.length + matches.blacklist.length > 0;

        veroDetailsMap.set(Number(itemId), { hasVero: hasVeroMatch, matches, amazonUrl });

        return buildResultRow(
            itemId, customLabel, '', '',
            hasVeroMatch ? formatVeroStatus(matches) : 'OK',
            amazonUrl, ''
        );
    } catch (err) {
        return buildResultRow(itemId, customLabel, '', '', 'ERROR: ' + err.message, amazonUrl, '');
    } finally {
        processedItemCount++;
        updateProcessedItemCountDisplay();
    }
}

/** Filters an array to only items whose lowercase string appears in a text blob */
function findMatchesInText(text, termList) {
    if (!Array.isArray(termList) || !termList.length) return [];
    const results = [];
    const seen = new Set();
    for (const term of termList) {
        if (!term) continue;
        const lower = String(term).toLowerCase().trim();
        if (lower && !seen.has(lower) && text.includes(lower)) {
            seen.add(lower);
            results.push(String(term));
        }
    }
    return results;
}

/** Builds a human-readable status string from VERO/blacklist match sets */
function formatVeroStatus(matches) {
    const parts = [];
    if (matches.vero?.length) parts.push('VERO: ' + matches.vero.join(', '));
    if (matches.blacklist?.length) parts.push('BLACKLIST: ' + matches.blacklist.join(', '));
    return parts.join(' | ');
}

/** Opens a modal showing VERO match details for an item */
function showVeroDetailsModal(itemOrId) {
    try {
        const itemId = Number(itemOrId?.ItemID ?? itemOrId);
        const details = veroDetailsMap.get(itemId);
        if (!details) {
            alert('No VERO details recorded for this item.');
            return;
        }

        const { matches, amazonUrl } = details;

        // Backdrop
        const backdrop = document.createElement('div');
        Object.assign(backdrop.style, {
            position: 'fixed', inset: '0',
            background: 'rgba(0,0,0,0.35)', zIndex: '9999'
        });

        // Modal box
        const modal = document.createElement('div');
        Object.assign(modal.style, {
            position: 'absolute', top: '50%', left: '50%',
            transform: 'translate(-50%, -50%)',
            background: '#fff', borderRadius: '8px',
            boxShadow: '0 8px 30px rgba(0,0,0,0.2)',
            width: 'min(720px, 95vw)', maxHeight: '80vh',
            overflow: 'auto', padding: '16px'
        });

        const title = document.createElement('h3');
        title.textContent = `VERO details for Item ${itemId}`;
        title.style.marginTop = '0';
        modal.appendChild(title);

        // Helper to render a match category
        const renderSection = (label, items) => {
            const section = document.createElement('div');
            const heading = document.createElement('div');
            heading.textContent = `${label} (${items.length})`;
            heading.style.fontWeight = '600';
            heading.style.marginTop = '8px';
            section.appendChild(heading);
            if (items.length) {
                const ul = document.createElement('ul');
                for (const item of items) {
                    const li = document.createElement('li');
                    li.textContent = String(item);
                    ul.appendChild(li);
                }
                section.appendChild(ul);
            } else {
                const none = document.createElement('div');
                none.textContent = 'None';
                none.style.color = '#555';
                section.appendChild(none);
            }
            return section;
        };

        modal.appendChild(renderSection('Vero words', matches.vero || []));
        modal.appendChild(renderSection('Blacklist words', matches.blacklist || []));
        modal.appendChild(renderSection('Restricted words', matches.restricted || []));

        const link = document.createElement('a');
        link.href = amazonUrl;
        link.target = '_blank';
        link.rel = 'noopener';
        link.textContent = 'Open Amazon page';
        link.style.display = 'inline-block';
        link.style.marginTop = '10px';
        modal.appendChild(link);

        const actions = document.createElement('div');
        Object.assign(actions.style, { marginTop: '14px', display: 'flex', gap: '8px' });

        const closeBtn = document.createElement('button');
        closeBtn.textContent = 'Close';
        closeBtn.addEventListener('click', () => backdrop.remove());
        actions.appendChild(closeBtn);

        modal.appendChild(actions);
        backdrop.appendChild(modal);
        document.body.appendChild(backdrop);
    } catch (err) {
        console.warn('Failed to open VERO details modal:', err);
    }
}


// ============================================================
// SECTION 15: PRODUCT DATA FETCHING (Amazon / Home Depot)
// ============================================================

/** Handles a detected Amazon soft-block / failed page load: tracks consecutive
 *  failures and, after enough of them, auto-pauses, reduces concurrency, and
 *  shows a cooldown modal before resuming. */
function handleSoftBlockDetected() {
    blockCount++;
    console.warn('Amazon soft block detected. Count: ' + blockCount);

    // After 10 consecutive 503s/failed loads, auto-pause and show cooldown timer
    if (!isPaused && blockCount > 10 && !isBlockCooldown) {
        isBlockCooldown = true;
        isPaused = true;
        pauseBtn.disabled = true;
        startBtn.disabled = false;
        blockCount = 0;
        clearInterval(timerInterval);

        // Reduce concurrency to ease load
        if (concurrency > 1) {
            concurrency--;
            threadCountInput.value = concurrency;
            console.warn('🔻 Reducing concurrency to ' + concurrency);
        }

        // Exponential back-off: 2 min, 3 min, 4 min...
        const cooldownSeconds = 60 * (2 + blockCount);
        blockCount++;
        console.warn(`🔒 Blocked. Waiting ${cooldownSeconds / 60} minutes before retrying.`);
        showBlockModal(cooldownSeconds);
    }
}

async function fetchAndBuildResult(rawRow, workerId = 0) {
    const customLabel = rawRow['Custom label (SKU)'];
    const itemId = rawRow['Item number'];
    const isValidSku = SKU_PATTERNS[supplierSelect.value]?.test(customLabel);

    if (!isValidSku) return buildResultRow(itemId, customLabel, '', '', 'Skipped');

    const tld = COUNTRY_TLD_MAP[listingSiteSelect.value] || 'com';
    const productUrl = supplierSelect.value === 'amazon'
        ? `https://www.amazon.${tld}/dp/${atob(customLabel)}?th=1&psc=1`
        : `https://www.homedepot.com/p/${encodeURIComponent(customLabel)}`;

    try {
        const productData = await fetchProductData(customLabel, productUrl, workerId);

        // fetchAmazonData() can resolve "successfully" while still handing back
        // a failure placeholder (page never loaded, soft-blocked, timed out)
        // instead of rejecting. Treat those as ERROR_503 so they get retried.
        if (productData.hasNetworkError || productData.isPageCorrectlyOpened === false) {
            handleSoftBlockDetected();
            const reason = productData.availabilityMessage || 'Page failed to load';
            return buildResultRow(itemId, customLabel, '', '', `ERROR_503: ${reason}`, productUrl, '');
        }

        // Page loaded, but the item is unavailable / no price could be found
        // (wrong ASIN, out of stock, listing removed, etc.)
        if (productData.price == null || productData.price === -1 || productData.isItemAvailable === false) {
            const reason = productData.availabilityMessage || 'Price not found / item unavailable';
            return buildResultRow(
                itemId, customLabel,
                productData.price ?? '', productData.quantity ?? '',
                `ERROR: ${reason}`, productUrl, productData.brand ?? ''
            );
        }

        return buildResultRow(itemId, customLabel, productData.price, productData.quantity, 'Success', productUrl, productData.brand);
    } catch (err) {
        if (err.message.includes('ERROR_503')) {
            handleSoftBlockDetected();
            return buildResultRow(itemId, customLabel, '', '', err.message, productUrl, '');
        }
        return buildResultRow(itemId, customLabel, '', '', err.message, productUrl, '');
    } finally {
        processedItemCount++;
        updateProcessedItemCountDisplay();
    }
}

/** Constructs a standard processed-row object */
function buildResultRow(itemId, customLabel, price, quantity, status, itemUrl = '', brand = '') {
    return {
        Action: 'Revise',
        ItemID: Number(itemId),
        CustomLabel: customLabel,
        StartPrice: price,
        Quantity: quantity,
        Brand: brand,
        Status: status,
        itemUrl: itemUrl
    };
}

function fetchProductData(customLabel, itemUrl, workerId = 0) {
    return new Promise((resolve, reject) => {
        chrome.runtime.sendMessage(
            {
                type: 'fetch_product_data',
                supplier: supplierSelect.value,
                customLabel,
                itemUrl,
                workerId
            },
            (response) => {
                if (chrome.runtime.lastError) return reject(chrome.runtime.lastError);
                if (!response || response.error) return reject(new Error(response?.error || 'Unknown error'));
                resolve(response.productData || {});
            }
        );
    });
}


// ============================================================
// SECTION 16: TABLE RENDERING
// ============================================================

const HTML_ESCAPE_MAP = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };

function escapeHtml(value) {
    return String(value ?? '').replace(/[&<>"']/g, ch => HTML_ESCAPE_MAP[ch] || ch);
}

/** Builds the row key used to match DOM rows to data rows */
function getRowKey(row) {
    return String(row?.ItemID ?? '') + '::' + String(row?.CustomLabel ?? '');
}

function getDomRowKey(trEl) {
    return trEl?.dataset
        ? (trEl.dataset.itemId ?? '') + '::' + (trEl.dataset.customLabel ?? '')
        : '';
}

function isVeroFlaggedRow(row) {
    const status = String(row?.Status ?? '').toLowerCase();
    return status.includes('vero') || status.includes('blacklist');
}

/** Renders the Status cell, including a "Remove" chip in VERO mode */
function renderStatusCell(row) {
    const statusHtml = escapeHtml(String(row?.Status ?? ''));
    if (isVeroMode && isVeroFlaggedRow(row)) {
        return `
    <div class="vero-status-container">
      <div class="vero-status-text">${statusHtml}</div>
      <button type="button" class="btn-remove-chip" title="Remove this row from exports">✕ Remove</button>
    </div>
  `;
    }
    return statusHtml;
}

/** Builds the HTML for a single processed-table row */
function buildProcessedRowHtml(row) {
    const country = listingSiteSelect.value || 'US';
    const tld = COUNTRY_TLD_MAP[country] || 'com';
    const itemIdStr = String(row?.ItemID ?? '');
    const label = String(row?.CustomLabel ?? '');

    // Build the item URL (Amazon link from base64-decoded ASIN)
    let itemUrl = row?.itemUrl || '';
    if (!itemUrl && label) {
        try { itemUrl = `https://www.amazon.${tld}/dp/${atob(label)}`; } catch { itemUrl = ''; }
    }

    const ebayLink = itemIdStr
        ? `<a href="${escapeHtml('https://www.ebay.' + tld + '/sh/lst/active?keyword=' + itemIdStr + '&source=filterbar&action=search')}" target="_blank" rel="noopener">${escapeHtml(itemIdStr)}</a>`
        : escapeHtml(itemIdStr);

    const labelLink = itemUrl
        ? `<a href="${escapeHtml(itemUrl)}" target="_blank" rel="noopener">${escapeHtml(label)}</a>`
        : escapeHtml(label);

    return `
    <tr data-item-id="${escapeHtml(itemIdStr)}" data-custom-label="${escapeHtml(label)}">
      <td>${escapeHtml(row?.Action ?? '')}</td>
      <td>${ebayLink}</td>
      <td>${labelLink}</td>
      <td>${escapeHtml(row?.StartPrice ?? '')}</td>
      <td>${escapeHtml(row?.Quantity ?? '')}</td>
      <td>${escapeHtml(row?.Brand ?? '')}</td>
      <td>${renderStatusCell(row)}</td>
    </tr>
  `;
}

function findDomRowForProcessedRow(tbody, row) {
    const key = getRowKey(row);
    return [...tbody.querySelectorAll('tr')].find(tr => getDomRowKey(tr) === key) || null;
}

function getVeroFlaggedRows() {
    return processedRows.filter(isVeroFlaggedRow);
}

/** Full re-render of the processed table */
function renderProcessedTable() {
    const tbody = processedTable?.querySelector('tbody');
    if (!tbody) return;

    tbody.innerHTML = processedRows.length
        ? [...processedRows].reverse().map(buildProcessedRowHtml).join('')
        : '';

    updateErrorCount();
}

/** Renders the raw items table */
function renderRawTable() {
    const thead = rawTable.querySelector('thead');
    const tbody = rawTable.querySelector('tbody');

    if (!csvHeaders.length) {
        thead.innerHTML = '';
        tbody.innerHTML = '';
        return;
    }

    thead.innerHTML = '<tr>' + csvHeaders.map(h => `<th>${h}</th>`).join('') + '</tr>';

    let html = '';
    rawRows.forEach((row, idx) => {
        const cells = csvHeaders.map(h => `<td>${row[h] || ''}</td>`).join('');
        html += `<tr data-index="${idx}"${idx < currentIndex ? ' class="done"' : ''}>${cells}</tr>`;
    });
    tbody.innerHTML = html;
    updateErrorCount();
}

/** Appends or updates a single row in the processed table */
function appendProcessedRow(row, replace = false) {
    console.log('Appending processed item:', row);
    const tbody = processedTable?.querySelector('tbody');
    if (!tbody) return;

    if (replace) {
        const existing = findDomRowForProcessedRow(tbody, row);
        if (existing) existing.remove();
    }

    const temp = document.createElement('tbody');
    temp.innerHTML = buildProcessedRowHtml(row).trim();
    const newTr = temp.firstElementChild;

    if (newTr) {
        tbody.insertBefore(newTr, tbody.firstChild || null);
        requestAnimationFrame(() => {
            newTr.classList.add('highlighted-row');
            setTimeout(() => newTr.classList.remove('highlighted-row'), 1200);
        });
        updateErrorCount();
    }
}

/** Handles clicks in the processed table (e.g. the VERO "Remove" button) */
function handleProcessedTableClick(e) {
    const removeBtn = e.target.closest('.btn-remove-chip');
    if (!removeBtn) return;
    e.preventDefault();
    e.stopPropagation();
    if (!isVeroMode) return;

    const tr = removeBtn.closest('tr');
    if (!tr?.dataset) return;

    const itemId = String(tr.dataset.itemId ?? '');
    const customLabel = String(tr.dataset.customLabel ?? '');

    const rowIdx = processedRows.findIndex(
        r => String(r?.ItemID ?? '') === itemId && String(r?.CustomLabel ?? '') === customLabel
    );

    if (rowIdx === -1) {
        renderProcessedTable();
        return;
    }

    const [removed] = processedRows.splice(rowIdx, 1);
    const removedId = Number(removed?.ItemID);
    if (Number.isFinite(removedId)) veroDetailsMap.delete(removedId);

    renderProcessedTable();
    updateCounts();
    saveNow();
}

/** Updates the done/processing CSS classes on a raw table row */
function setRowStatus(rowIndex, status) {
    const tr = rawTable.querySelector(`tr[data-index="${rowIndex}"]`);
    if (tr) {
        tr.classList.toggle('processing', status === 'processing');
        if (status === 'done') tr.classList.add('done');
    }
}

function updateCounts() {
    rawCountEl.textContent = '(' + rawRows.length + ')';
    processedCountEl.textContent = '(' + processedRows.length + ')';
    positionInput.value = currentIndex;
}

function scheduleSave() {
    if (!pendingSaveTimeout) {
        pendingSaveTimeout = setTimeout(persistState, 500);
    }
}

function persistState() {
    chrome.storage.local.set({
        currentIndex,
        isPaused,
        processedRows
    });
    pendingSaveTimeout = null;
}

function saveNow() {
    clearTimeout(pendingSaveTimeout);
    persistState();
}

function clearRawData() {
    rawRows = [];
    csvHeaders = [];
    selectedFile = null;
    currentIndex = 0;
    positionInput.value = 0;
    rawTable.querySelector('thead').innerHTML = '';
    rawTable.querySelector('tbody').innerHTML = '';
    updateCounts();
    saveNow();
}

function clearProcessedData() {
    processedRows = [];
    processedTable.querySelector('tbody').innerHTML = '';
    updateCounts();
    scheduleSave();
}


// ============================================================
// SECTION 17: SORT PROCESSED TABLE BY STATUS
// ============================================================

function sortProcessedByStatus() {
    const tbody = processedTable?.querySelector('tbody');
    if (!tbody) return;

    const sorted = [...processedRows].sort((a, b) => {
        const aStatus = (a.Status || '').toLowerCase();
        const bStatus = (b.Status || '').toLowerCase();
        return sortAscending
            ? aStatus.localeCompare(bStatus)
            : bStatus.localeCompare(aStatus);
    });

    tbody.innerHTML = sorted.map(buildProcessedRowHtml).join('');
    updateErrorCount();
}


// ============================================================
// SECTION 18: LOADING OVERLAY
// ============================================================

const rawContainer = document.getElementById('rawContainer');
const loadingOverlay = document.getElementById('loadingOverlay');

function showLoadingOverlay() { loadingOverlay.classList.remove('hidden'); }
function hideLoadingOverlay() { loadingOverlay.classList.add('hidden'); }


// ============================================================
// SECTION 19: BLOCK COOLDOWN MODAL
// ============================================================

function showBlockModal(countdownSeconds) {
    const modal = document.getElementById('blockModal');
    const countdownEl = document.getElementById('blockCountdown');

    modal.classList.remove('hidden');
    startBtn.disabled = true;
    document.body.style.overflow = 'hidden';

    let remaining = countdownSeconds;
    countdownEl.textContent = remaining;

    const interval = setInterval(() => {
        remaining--;
        countdownEl.textContent = remaining;

        if (remaining <= 0) {
            clearInterval(interval);
            modal.classList.add('hidden');
            startBtn.disabled = false;
            document.body.style.overflow = '';
            isBlockCooldown = false;
            console.log('✅ Auto-resuming after block cooldown...');
            handleStartClick();  // Auto-resume
        }
    }, 1000);
}


// ============================================================
// SECTION 20: UPDATE ERROR COUNT BADGE
// ============================================================

function updateErrorCount() {
    const count = processedRows.filter(
        r => r.Status && r.Status.toLowerCase().includes('error')
    ).length;
    const el = document.getElementById('errorRowCount');
    if (el) el.textContent = count;
}


// ============================================================
// SECTION 21: EXPORT SETTINGS & PREVIEW MODAL
// ============================================================

// Default export settings
const DEFAULT_EXPORT_SETTINGS = {
    excludeFail: false,
    markupType: 'markup',      // 'markup' (%) or 'fixed' ($)
    markupValue: 100,           // e.g. 100 = 100% markup (2x cost)
    useVariable: false,         // Whether to use tiered variable pricing
    roundingOption: '0.99',        // Round to .99, .95, etc.
    quantityMode: 'fixed',       // 'fixed' or 'source'
    fixedQty: 1,
    failAction: 'zero',        // 'zero' quantity or 'end' listing on failure
    variableRules: [
        { min: 0, max: 5, markup: 400 },
        { min: 5, max: 15, markup: 200 },
        { min: 15, max: 30, markup: 100 },
        { min: 30, max: 60, markup: 100 },
        { min: 60, max: 100, markup: 100 },
        { min: 100, max: 9999, markup: 100 }
    ],
    updateStock: true,
    updatePrice: true
};

/**
 * Applies markup rules to processedRows and returns the eBay-upload-ready rows.
 * Handles: fixed markup, percentage markup, variable tiered markup, rounding, fail actions.
 */
async function buildExportRows() {
    const { csvExportSettings } = await chrome.storage.local.get('csvExportSettings');
    const settings = { ...DEFAULT_EXPORT_SETTINGS, ...csvExportSettings };

    return processedRows
        .filter(row => !settings.excludeFail || row.Status === 'Success')
        .map(row => {
            let price = parseFloat(row.StartPrice) || 0;
            let quantity = settings.quantityMode === 'fixed' ? settings.fixedQty : row.Quantity;

            if (row.Status === 'Success') {
                // Apply markup
                if (settings.useVariable) {
                    const rule = settings.variableRules.find(r => price >= r.min && price <= r.max);
                    if (rule) price *= 1 + rule.markup / 100;
                } else if (settings.markupType === 'markup') {
                    price *= 1 + settings.markupValue / 100;
                } else {
                    price += settings.markupValue;
                }

                // Rounding (e.g. $14.99 instead of $15.00)
                if (settings.roundingOption !== 'none') {
                    const cents = parseFloat(settings.roundingOption);
                    const floor = Math.floor(price);
                    const roundUp = floor + cents;
                    const roundDown = floor - 1 + cents;
                    price = Math.abs(price - roundUp) < Math.abs(price - roundDown)
                        ? roundUp : roundDown;
                }

                price = price.toFixed(2);
            }

            const isError = row.Status.toLowerCase().includes('error');
            const action = isError && settings.failAction === 'end' ? 'End' : 'Revise';
            const endingReason = action === 'End' ? 'NotAvailable' : '';

            if (isError && settings.failAction === 'zero') { quantity = 0; price = ''; }
            if (action === 'End') { quantity = ''; price = ''; }

            return {
                Action: action,
                ItemID: row.ItemID,
                StartPrice: settings.updatePrice ? price : '',
                Quantity: settings.updateStock ? quantity : '',
                EndingReason: endingReason
            };
        });
}

async function exportCSV() {
    const columns = ['Action', 'ItemID', 'StartPrice', 'Quantity', 'EndingReason'];
    const exportRows = await buildExportRows();
    const lines = [
        columns.join(','),
        ...exportRows.map(row => columns.map(col => csvEscape(row[col] ?? '')).join(','))
    ];

    const blob = new Blob([lines.join('\n')], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'eBay_update_' + new Date().toISOString().slice(0, 10) + '.csv';
    a.click();
    URL.revokeObjectURL(url);
}

async function updateListings() {
    const exportRows = await buildExportRows();
    if (!exportRows.length) {
        alert('No processed rows to update.');
        return;
    }

    const columns = ['Action', 'ItemID', 'StartPrice', 'Quantity', 'EndingReason'];
    const lines = [
        columns.join(','),
        ...exportRows.map(row => columns.map(col => csvEscape(row[col] ?? '')).join(','))
    ];
    const csvText = lines.join('\n');

    chrome.runtime.sendMessage(
        {
            type: 'upload_csv_to_ebay',
            csvText: csvText,
            filename: 'update_' + new Date().toISOString().slice(0, 10) + '.csv',
            domain: COUNTRY_TLD_MAP[listingSiteSelect.value] || 'com'
        },
        (response) => {
            if (chrome.runtime.lastError || !response?.success) {
                console.error('Upload failed:', chrome.runtime.lastError || response?.error);
                alert('Upload failed: ' + (response?.error || chrome.runtime.lastError?.message || 'Unknown error'));
            } else {
                alert('Listings successfully uploaded to eBay.');
            }
        }
    );
}


// ============================================================
// SECTION 22: DOWNLOAD ACTIVE LISTINGS FROM eBAY
// ============================================================

async function downloadAndInjectActiveListings() {
    try {
        const result = await new Promise((resolve, reject) => {
            chrome.runtime.sendMessage(
                { type: 'download_active_listings_csv' },
                (response) => {
                    if (response?.success) resolve(response);
                    else reject(new Error(response?.error || 'Unknown error'));
                }
            );
        });

        const blob = new Blob([result.csv], { type: 'text/csv' });
        const filename = 'eBay_active_listings_' + new Date().toISOString().slice(0, 10) + '.csv';
        const file = new File([blob], filename, { type: 'text/csv' });
        const dt = new DataTransfer();
        dt.items.add(file);

        fileInput.files = dt.files;
        fileInput.dispatchEvent(new Event('change', { bubbles: true }));
        console.log('✅ Injected downloaded CSV into file input');
    } catch (err) {
        console.error('Download failed:', err);
        alert('Error downloading CSV: ' + err.message);
    }
}

downloadActiveListingsBtn.addEventListener('click', async () => {
    // Membership gate
    const hasAccess = await verifyFastTrackerAccess();
    if (!hasAccess) return;
    // ------------------------------------------------
    downloadAndInjectActiveListings();
});


// ============================================================
// SECTION 23: EXPORT MODAL — SETTINGS PERSISTENCE & PREVIEW
// ============================================================

function addVariablePricingRule(rule = { min: '', max: '', markup: '' }) {
    const container = document.getElementById('priceRules');
    const div = document.createElement('div');
    div.className = 'price-rule';
    div.innerHTML = `
    <div class="price-rule-text" style="font-size: 0.9rem; margin-bottom: 4px; color: #444;">
      If cost is between 
      $<input type="number" class="minPrice" value="${rule.min}" style="width: 60px;" /> and 
      $<input type="number" class="maxPrice" value="${rule.max}" style="width: 60px;" />,
      apply 
      <input type="number" class="rangeMarkup" value="${rule.markup}" style="width: 60px;" />% markup
      <button class="remove-rule" style="margin-left: 8px;">Remove</button>
    </div>
  `;
    div.querySelector('.remove-rule').addEventListener('click', () => {
        div.remove();
        saveExportSettings();
        populatePreviewTable();
    });
    div.querySelectorAll('input').forEach(input => {
        input.addEventListener('input', () => {
            saveExportSettings();
            populatePreviewTable();
        });
    });
    container.appendChild(div);
}

async function saveExportSettings() {
    const pricingMode = document.querySelector('input[name="pricingMode"]:checked').value;
    const settings = {
        excludeFail: document.getElementById('excludeFailed').checked,
        markupType: pricingMode,
        markupValue: pricingMode === 'markup'
            ? parseFloat(document.getElementById('globalMarkup').value) || 0
            : parseFloat(document.getElementById('fixedIncrease').value) || 0,
        useVariable: document.getElementById('enableVariablePricing').checked,
        roundingOption: document.getElementById('roundingOption').value,
        quantityMode: document.querySelector('input[name="quantityMode"]:checked').value,
        fixedQty: parseInt(document.getElementById('fixedQuantity').value, 10) || 1,
        failAction: document.querySelector('input[name="failAction"]:checked').value,
        variableRules: [...document.getElementById('priceRules').children].map(el => ({
            min: parseFloat(el.querySelector('.minPrice').value) || 0,
            max: parseFloat(el.querySelector('.maxPrice').value) || Infinity,
            markup: parseFloat(el.querySelector('.rangeMarkup').value) || 0
        })),
        updateStock: document.getElementById('updateStockCheckbox')?.checked || false,
        updatePrice: document.getElementById('updatePriceCheckbox')?.checked || false
    };
    await chrome.storage.local.set({ csvExportSettings: settings });
}

async function loadExportSettings() {
    const { csvExportSettings } = await chrome.storage.local.get('csvExportSettings');
    const settings = { ...DEFAULT_EXPORT_SETTINGS, ...(csvExportSettings || {}) };

    document.getElementById('excludeFailed').checked = settings.excludeFail;
    document.querySelector(`input[name="pricingMode"][value="${settings.markupType}"]`).checked = true;
    document.getElementById('globalMarkup').value = settings.markupType === 'markup' ? settings.markupValue : '';
    document.getElementById('fixedIncrease').value = settings.markupType === 'fixed' ? settings.markupValue : '';
    document.getElementById('enableVariablePricing').checked = settings.useVariable;
    document.getElementById('roundingOption').value = settings.roundingOption;
    document.querySelector(`input[name="quantityMode"][value="${settings.quantityMode}"]`).checked = true;
    document.getElementById('fixedQuantity').value = settings.fixedQty;
    document.querySelector(`input[name="failAction"][value="${settings.failAction}"]`).checked = true;

    const variableContainer = document.getElementById('variablePricingContainer');
    document.getElementById('priceRules').innerHTML = '';

    if (settings.useVariable) {
        variableContainer.classList.remove('hidden');
        settings.variableRules.forEach(rule => addVariablePricingRule(rule));
        document.getElementById('globalMarkup').disabled = true;
        document.getElementById('fixedIncrease').disabled = true;
    } else {
        variableContainer.classList.add('hidden');
        document.getElementById('globalMarkup').disabled = false;
        document.getElementById('fixedIncrease').disabled = false;
    }

    document.getElementById('updateStockCheckbox').checked = settings.updateStock;
    document.getElementById('updatePriceCheckbox').checked = settings.updatePrice;
    populatePreviewTable();

    // Show current variable rules JSON in import textarea
    const jsonInput = document.getElementById('importVariableJsonInput');
    if (jsonInput) jsonInput.value = JSON.stringify(settings.variableRules || [], null, 2);
}

function attachExportModalListeners() {
    const onChange = () => { saveExportSettings(); populatePreviewTable(); };

    document.getElementById('excludeFailed').addEventListener('change', onChange);

    document.querySelectorAll('input[name="pricingMode"]').forEach(radio => {
        radio.addEventListener('change', () => {
            const varEnabled = document.getElementById('enableVariablePricing').checked;
            document.getElementById('globalMarkup').disabled = radio.value !== 'markup' || varEnabled;
            document.getElementById('fixedIncrease').disabled = radio.value !== 'fixed' || varEnabled;
            onChange();
        });
    });

    document.getElementById('enableVariablePricing').addEventListener('change', () => {
        const checked = document.getElementById('enableVariablePricing').checked;
        document.getElementById('variablePricingContainer').classList.toggle('hidden', !checked);
        if (checked && !document.getElementById('priceRules').children.length) {
            DEFAULT_EXPORT_SETTINGS.variableRules.forEach(r => addVariablePricingRule(r));
        }
        document.getElementById('globalMarkup').disabled = checked;
        document.getElementById('fixedIncrease').disabled = checked;
        onChange();
    });

    document.getElementById('roundingOption').addEventListener('change', onChange);
    document.getElementById('globalMarkup').addEventListener('input', onChange);
    document.getElementById('fixedIncrease').addEventListener('input', onChange);
    document.getElementById('fixedQuantity').addEventListener('input', onChange);
    document.querySelectorAll('input[name="quantityMode"]').forEach(r => r.addEventListener('change', onChange));
    document.querySelectorAll('input[name="failAction"]').forEach(r => r.addEventListener('change', onChange));

    document.getElementById('addPriceRule').addEventListener('click', () => {
        addVariablePricingRule();
        onChange();
    });

    document.getElementById('importVariableJsonBtn').addEventListener('click', () => {
        const jsonText = document.getElementById('importVariableJsonInput').value.trim();
        if (!jsonText) return alert('Please paste variable pricing rules as JSON.');
        let rules;
        try {
            rules = JSON.parse(jsonText);
            if (!Array.isArray(rules)) throw new Error('Not an array');
        } catch {
            return alert("Invalid JSON array. Make sure it's a list of rules.");
        }
        chrome.storage.local.get('csvExportSettings', ({ csvExportSettings }) => {
            const merged = { ...DEFAULT_EXPORT_SETTINGS, ...(csvExportSettings || {}) };
            merged.variableRules = rules;
            chrome.storage.local.set({ csvExportSettings: merged }, () => {
                alert('Variable pricing rules updated.');
                loadExportSettings();
            });
        });
    });

    const stockCheckbox = document.getElementById('updateStockCheckbox');
    const priceCheckbox = document.getElementById('updatePriceCheckbox');
    stockCheckbox && stockCheckbox.addEventListener('change', () => { saveExportSettings(); populatePreviewTable(); });
    priceCheckbox && priceCheckbox.addEventListener('change', () => { saveExportSettings(); populatePreviewTable(); });
}

function initExportModal() {
    loadExportSettings();
    attachExportModalListeners();
}

document.addEventListener('DOMContentLoaded', () => {
    console.log('CSV Export Modal initialized');
    initExportModal();
    // Membership gate on open
    verifyFastTrackerAccess();
    // ------------------------------------------------
});


// ============================================================
// SECTION 24: PREVIEW TABLE (inside export modal)
// ============================================================

function populatePreviewTable() {
    const tbody = document.querySelector('#previewTable tbody');
    const thead = document.querySelector('#previewTable thead');
    tbody.innerHTML = '';

    if (!window.processedRows || processedRows.length === 0) {
        tbody.innerHTML = '<tr><td colspan="6">No processed items yet.</td></tr>';
        return;
    }

    thead.innerHTML = `
    <tr>
      <th>Action</th><th>ItemID</th><th>CustomLabel</th><th>StartPrice</th><th>Quantity</th><th>Status</th>
    </tr>
  `;

    const pricingMode = document.querySelector('input[name="pricingMode"]:checked').value;
    const markupValue = pricingMode === 'markup'
        ? parseFloat(document.getElementById('globalMarkup').value || 0)
        : parseFloat(document.getElementById('fixedIncrease').value || 0);
    const roundingOption = document.getElementById('roundingOption')?.value || 'none';
    const quantityMode = document.querySelector('input[name="quantityMode"]:checked').value;
    const fixedQty = parseInt(document.getElementById('fixedQuantity').value, 10);
    const failAction = document.querySelector('input[name="failAction"]:checked').value;
    const useVariable = document.getElementById('enableVariablePricing').checked;
    const excludeFail = document.getElementById('excludeFailed').checked;
    const updateStock = document.getElementById('updateStockCheckbox')?.checked ?? true;
    const updatePrice = document.getElementById('updatePriceCheckbox')?.checked ?? true;

    processedRows
        .filter(row => !excludeFail || row.Status === 'Success')
        .slice(0, 3)  // preview first 3 rows
        .forEach(row => {
            let price = parseFloat(row.StartPrice) || 0;
            let action = row.Action || 'Revise';
            let quantity = quantityMode === 'fixed' ? fixedQty : row.Quantity;

            if (row.Status === 'Success') {
                if (useVariable) {
                    const rules = [...document.getElementById('priceRules').children].map(el => ({
                        min: parseFloat(el.querySelector('.minPrice').value || 0),
                        max: parseFloat(el.querySelector('.maxPrice').value || 0),
                        markup: parseFloat(el.querySelector('.rangeMarkup').value || 0)
                    }));
                    const rule = rules.find(r => price >= r.min && price <= r.max);
                    if (rule) price *= 1 + rule.markup / 100;
                } else if (pricingMode === 'markup') {
                    price *= 1 + markupValue / 100;
                } else {
                    price += markupValue;
                }

                if (roundingOption !== 'none') {
                    const cents = parseFloat(roundingOption);
                    const floor = Math.floor(price);
                    const roundUp = floor + cents;
                    const roundDown = floor - 1 + cents;
                    price = Math.abs(price - roundUp) < Math.abs(price - roundDown)
                        ? roundUp : roundDown;
                }
                price = price.toFixed(2);
            }

            if (row.Status.toLowerCase().includes('error')) {
                if (failAction === 'end') { action = 'End'; quantity = ''; price = ''; }
                else { quantity = 0; price = ''; }
            }

            const tr = document.createElement('tr');
            tr.innerHTML = `
      <td>${action}</td>
      <td>${row.ItemID || '-'}</td>
      <td>${row.CustomLabel || '-'}</td>
      <td>${updatePrice ? price : ''}</td>
      <td>${updateStock ? quantity : ''}</td>
      <td>${row.Status || '-'}</td>
    `;
            tbody.appendChild(tr);
        });

    // Show total error/zero-quantity count
    const problemCount = processedRows.filter(
        r => (r.Status && r.Status.toLowerCase().includes('error')) ||
            r.Quantity === 0 || r.Quantity === '0'
    ).length;
    document.getElementById('errorCount').textContent = problemCount;
}

window.openExportModal = function () {
    document.getElementById('exportModal').classList.remove('hidden');
    populatePreviewTable();
};


// ============================================================
// SECTION 25: QUICK START BUTTON
// ============================================================

document.getElementById('quickStartBtn').addEventListener('click', () => {
    const modal = document.getElementById('exportModal');
    modal.classList.remove('hidden');

    // Hide normal modal footer buttons and show only "Start Tracking"
    const footer = modal.querySelector('.modal-footer');
    const footerBtns = footer ? Array.from(footer.querySelectorAll('button')) : [];
    footerBtns.forEach(btn => btn.classList.add('hidden'));

    const startTrackingBtn = modal.querySelector('#startTrackingBtn');
    if (!startTrackingBtn) return;
    startTrackingBtn.classList.remove('hidden');

    const onStartTracking = async () => {
        modal.classList.add('hidden');
        console.log('Starting processing immediately after quick start...');

        // Restore footer buttons
        footerBtns.forEach(btn => btn.classList.remove('hidden'));
        startTrackingBtn.classList.add('hidden');
        startTrackingBtn.removeEventListener('click', onStartTracking);

        // Ensure auto-upload is on
        if (!uploadToEbayCheckbox.checked) uploadToEbayCheckbox.checked = true;

        // Reset state
        clearProcessedData();
        clearRawData();
        positionInput.value = 0;
        currentIndex = 0;
        pauseBtn.disabled = false;
        startBtn.disabled = false;

        chrome.storage.local.set({ currentIndex, isPaused, processedRows });

        // Download current eBay listings → inject into file input → wait for parse → start
        await downloadAndInjectActiveListings();
        await waitForRawTablePopulated();
        handleStartClick();
    };

    startTrackingBtn.addEventListener('click', onStartTracking);
});

/** Waits until the raw table tbody has at least one row */
function waitForRawTablePopulated() {
    return new Promise((resolve) => {
        const tbody = document.querySelector('#rawTable tbody');
        if (tbody.children.length > 0) return resolve();
        new MutationObserver((mutations, observer) => {
            if (tbody.children.length > 0) {
                observer.disconnect();
                resolve();
            }
        }).observe(tbody, { childList: true });
    });
}


// ============================================================
// SECTION 26: MESSAGE LISTENER (from background / other scripts)
// ============================================================

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    // Convert raw rows to SilentSniper format (base64 encode SKUs)
    if (message.type === 'convert_skus_to_SilentSniper') {
        const SKU_FIELD = 'Custom label (SKU)';
        const b0Rows = rawRows.filter(
            row => typeof row[SKU_FIELD] === 'string' && row[SKU_FIELD].startsWith('B0')
        );

        if (!b0Rows.length) {
            alert('No items with B0 CustomLabel found in raw data.');
            return;
        }

        rawRows = b0Rows;
        currentIndex = 0;
        renderRawTable();
        updateCounts();
        exportFileExchangeCSV();
    }
});

/** Exports the current rawRows as a File Exchange CSV (base64-encodes SKUs) */
function exportFileExchangeCSV() {
    if (!rawRows.length) {
        alert('No rows to export');
        return;
    }

    const rows = rawRows.map(row => {
        const itemId = row['Item number'] ?? row['ItemID'] ?? '';
        const sku = String(row['Custom label (SKU)'] || '');
        return ['Revise', itemId, btoa(sku)];
    });

    const escape = val => '"' + String(val).replace(/"/g, '""') + '"';
    const csv = [
        ['Action', 'Item number', 'Custom label'].map(escape).join(','),
        ...rows.map(r => r.map(escape).join(','))
    ].join('\r\n');

    const blob = new Blob([csv], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'file_exchange_' + new Date().toISOString().slice(0, 10) + '.csv';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
}


// ============================================================
// SECTION 27: RETRY FAILED ITEMS
// ============================================================

const retryState = {
    running: false,
    abort: false,
    failures: [],
    total: 0,
    nextIndex: 0,
    doneCount: 0
};

document.getElementById('retryFailedBtn').addEventListener('click', retryFailedItems);

async function retryFailedItems() {
    const btn = document.getElementById('retryFailedBtn');

    // If already running, clicking again aborts
    if (retryState.running) {
        retryState.abort = true;
        return;
    }

    retryState.failures = processedRows.filter(r => r.Status !== 'Success');
    retryState.total = retryState.failures.length;

    if (!retryState.total) {
        alert('No failed listings to retry.');
        return;
    }

    retryState.nextIndex = 0;
    retryState.doneCount = 0;
    retryState.abort = false;
    retryState.running = true;
    btn.textContent = `Retry (0 / ${retryState.total})`;
    updateErrorCount();

    const threadCount = parseInt(threadCountInput.value, 10) || 1;

    await Promise.all(
        Array.from({ length: threadCount }, (_, workerId) =>
            (async function retryWorker() {
                while (!retryState.abort) {
                    const idx = retryState.nextIndex++;
                    if (idx >= retryState.total) break;

                    const { CustomLabel, ItemID } = retryState.failures[idx];
                    const fakeRow = { 'Custom label (SKU)': CustomLabel, 'Item number': ItemID };
                    const result = await fetchAndBuildResult(fakeRow, workerId);

                    // If we hit another block, start cooldown and abort this retry batch
                    if (result.Status.includes('ERROR_503')) {
                        const cooldown = 60 * (2 + blockCount);
                        blockCount++;
                        showBlockModal(cooldown);
                        retryState.abort = true;
                        blockTimer = setTimeout(() => {
                            retryState.abort = false;
                            retryFailedItems();
                        }, 1000 * cooldown);
                        break;
                    }

                    // Update the existing row in-place
                    const existingIdx = processedRows.findIndex(
                        r => r.CustomLabel === CustomLabel && r.ItemID === ItemID
                    );
                    if (existingIdx !== -1) processedRows[existingIdx] = result;

                    appendProcessedRow(result, true);
                    updateCounts();
                    scheduleSave();
                    updateErrorCount();
                    retryState.doneCount++;
                    btn.textContent = `Retry (${retryState.doneCount} / ${retryState.total})`;
                }
            })()
        )
    );

    retryState.running = false;
}


// ============================================================
// SECTION 28: EXPORT MODAL BUTTON WIRING
// ============================================================

document.getElementById('cancelExport').addEventListener('click', () => {
    document.getElementById('exportModal').classList.add('hidden');
});

document.getElementById('confirmExport').addEventListener('click', () => {
    document.getElementById('exportModal').classList.add('hidden');
    window.exportCSV && window.exportCSV();
});

document.getElementById('updateListingsBtn').addEventListener('click', async () => {
    // Membership gate
    const hasAccess = await verifyFastTrackerAccess();
    if (!hasAccess) return;
    // ------------------------------------------------
    document.getElementById('exportModal').classList.add('hidden');
    window.updateListings && window.updateListings();
});


// ============================================================
// SECTION 29: PREVIEW TABLE LIVE INPUT WIRING
// ============================================================

function wirePreviewTableInputs() {
    ['globalMarkup', 'fixedIncrease', 'fixedQuantity', 'roundingOption'].forEach(id => {
        const el = document.getElementById(id);
        el && el.addEventListener('input', populatePreviewTable);
    });
    document.querySelectorAll('input[name="pricingMode"]').forEach(r => r.addEventListener('change', populatePreviewTable));
    document.querySelectorAll('input[name="quantityMode"]').forEach(r => r.addEventListener('change', populatePreviewTable));
    document.querySelectorAll('input[name="failAction"]').forEach(r => r.addEventListener('change', populatePreviewTable));
    document.getElementById('excludeFailed').addEventListener('change', populatePreviewTable);
}

wirePreviewTableInputs();


// ============================================================
// SECTION 30: EXPOSE GLOBALS
// ============================================================

window.processedRows = processedRows;
window.exportCSV = exportCSV;



