/**
 * QuickBooks Migration Wizard — qb-migration-wizard.js
 * Embedded in Setup Wizard Step 2.
 * Manages a 9-step modal that walks the user through all QB Desktop data imports.
 */

(function () {
    'use strict';

    // ── Step Definitions ──────────────────────────────────────────────────────
    const STEPS = [
        {
            id: 1, key: 'chartOfAccounts', title: 'Chart of Accounts',
            icon: 'bi-diagram-3', fileAccept: '.iif',
            endpoint: '/Tools/ImportChartOfAccounts',
            deps: [],
            intro: 'Your Chart of Accounts defines the financial structure of your books. Import this first so vendor bills and other financial data can link to the correct accounts.',
            instructions: [
                'In QuickBooks Desktop, go to <strong>Lists → Chart of Accounts</strong>.',
                'Click the <strong>Account</strong> button at the bottom of the list.',
                'Choose <strong>Import/Export → Export Chart of Accounts</strong> (or use <em>File → Utilities → Export → Lists to IIF Files → Chart of Accounts</em>).',
                'Save the <code>.iif</code> file and upload it here.'
            ]
        },
        {
            id: 2, key: 'customers', title: 'Customers',
            icon: 'bi-people', fileAccept: '.iif',
            endpoint: '/Tools/ImportCustomers',
            deps: [],
            intro: 'Import your customer list so that historical invoices and payments can be matched to existing customer records.',
            instructions: [
                'In QuickBooks Desktop, go to <strong>File → Utilities → Export → Lists to IIF Files</strong>.',
                'Check <strong>Customer List</strong> and click OK.',
                'Save the <code>.iif</code> file and upload it here.'
            ]
        },
        {
            id: 3, key: 'vendors', title: 'Vendors',
            icon: 'bi-truck', fileAccept: '.iif',
            endpoint: '/Tools/ImportVendors',
            deps: [],
            intro: 'Import your vendor list. Vendors are required before importing inventory stock levels and vendor bills.',
            instructions: [
                'In QuickBooks Desktop, go to <strong>File → Utilities → Export → Lists to IIF Files</strong>.',
                'Check <strong>Vendor List</strong> and click OK.',
                'Save the <code>.iif</code> file and upload it here.'
            ]
        },
        {
            id: 4, key: 'catalogItems', title: 'Catalog Items',
            icon: 'bi-grid', fileAccept: '.iif',
            endpoint: '/Tools/ImportCatalogItems',
            deps: [],
            intro: 'Import service items from QuickBooks to populate your catalog with pre-priced services.',
            instructions: [
                'In QuickBooks Desktop, go to <strong>File → Utilities → Export → Lists to IIF Files</strong>.',
                'Check <strong>Item List</strong> and click OK.',
                'Save the <code>.iif</code> file and upload it here.'
            ]
        },
        {
            id: 5, key: 'inventory', title: 'Inventory',
            icon: 'bi-boxes', fileAccept: '.csv',
            endpoint: '/Tools/ImportQbInventoryValuation',
            deps: [3],
            intro: 'Import current stock levels from an Inventory Valuation Summary report. Vendors must be imported first for preferred vendor matching.',
            instructions: [
                'In QuickBooks Desktop, go to <strong>Reports → Inventory → Inventory Valuation Summary</strong>.',
                'Click <strong>Customize Report</strong>, open the <strong>Display</strong> tab, and add the <strong>Preferred Vendor</strong> column.',
                'Click <strong>Excel → Create New Worksheet → Comma Separated Values (.csv)</strong> and save.',
                'Upload the CSV file here.'
            ]
        },
        {
            id: 6, key: 'invoices', title: 'Invoices',
            icon: 'bi-receipt', fileAccept: '.csv',
            endpoint: '/Tools/ImportQbInvoices',
            deps: [2, 4],
            intro: 'Import historical invoices from a Customer Balance Detail report. Customers and catalog items should be imported first.',
            instructions: [
                'In QuickBooks Desktop, go to <strong>Reports → Customers &amp; Receivables → Customer Balance Detail</strong>.',
                'Set the date range to <strong>All</strong> to cover your full history.',
                'Click <strong>Excel → Create New Worksheet → Comma Separated Values (.csv)</strong> and save.',
                'Upload the CSV file here.'
            ]
        },
        {
            id: 7, key: 'transactions', title: 'Customer Payments',
            icon: 'bi-cash-coin', fileAccept: '.csv',
            endpoint: '/Tools/ImportQbTransactions',
            deps: [6],
            intro: 'Import payment records to update invoice balances. Invoices must be imported first.',
            instructions: [
                'In QuickBooks Desktop, go to <strong>Reports → Customers &amp; Receivables → Transaction List by Customer</strong>.',
                'Set the date range to <strong>All</strong> to cover your full history.',
                'Click <strong>Excel → Create New Worksheet → Comma Separated Values (.csv)</strong> and save.',
                'Upload the CSV file here.'
            ]
        },
        {
            id: 8, key: 'bills', title: 'Vendor Bills & Payments',
            icon: 'bi-file-earmark-text', fileAccept: '.csv',
            endpoint: '/Tools/ImportQbBillsAndPayments',
            deps: [1, 3],
            intro: 'Import vendor bills and payment history in a single step. The same Vendor Balance Detail file contains both — bills are imported first, then payments are matched against them.',
            instructions: [
                'In QuickBooks Desktop, go to <strong>Reports → Vendors &amp; Payables → Vendor Balance Detail</strong>.',
                'Set the date range to <strong>All</strong> to cover your full history.',
                'Click <strong>Excel → Create New Worksheet → Comma Separated Values (.csv)</strong> and save.',
                'Upload the CSV file here — bills and payments will both be processed automatically.'
            ]
        }
    ];

    // ── QuickBooks Online Step Definitions ────────────────────────────────────
    const QBO_STEPS = [
        {
            id: 1, key: 'qbo_coa', title: 'Chart of Accounts',
            icon: 'bi-diagram-3', fileAccept: '.xlsx,.xls',
            endpoint: '/Tools/ImportQboChartOfAccounts',
            deps: [],
            intro: 'Your Chart of Accounts defines the financial structure of your books. Import this first so financial data can link to the correct accounts.',
            instructions: [
                'In QuickBooks Online, go to <strong>Accounting → Chart of Accounts</strong>.',
                'Click the <strong>Run Report</strong> button at the top right.',
                'Click the <strong>Export</strong> icon (spreadsheet icon) and choose <strong>Export to Excel</strong>.',
                'Save the <code>.xlsx</code> file and upload it here.'
            ]
        },
        {
            id: 2, key: 'qbo_customers', title: 'Customers',
            icon: 'bi-people', fileAccept: '.xlsx,.xls',
            endpoint: '/Tools/ImportQboCustomers',
            deps: [],
            intro: 'Import your customer list so invoices and payments can be matched to existing customer records.',
            instructions: [
                'In QuickBooks Online, go to <strong>Reports</strong> and search for <strong>Customer Contact List</strong>.',
                'Click <strong>Customize</strong> if you want to include additional fields (address, terms, balance).',
                'Click the <strong>Export</strong> icon and choose <strong>Export to Excel</strong>.',
                'Save the <code>.xlsx</code> file and upload it here.'
            ]
        },
        {
            id: 3, key: 'qbo_vendors', title: 'Vendors',
            icon: 'bi-truck', fileAccept: '.xlsx,.xls',
            endpoint: '/Tools/ImportQboVendors',
            deps: [],
            intro: 'Import your vendor list before importing purchase orders or inventory.',
            instructions: [
                'In QuickBooks Online, go to <strong>Reports</strong> and search for <strong>Vendor Contact List</strong>.',
                'Click the <strong>Export</strong> icon and choose <strong>Export to Excel</strong>.',
                'Save the <code>.xlsx</code> file and upload it here.'
            ]
        },
        {
            id: 4, key: 'qbo_products', title: 'Products & Services',
            icon: 'bi-grid', fileAccept: '.xlsx,.xls',
            endpoint: '/Tools/ImportQboCatalogItems',
            deps: [],
            intro: 'Import your products and services. Inventory-type items will create inventory records; all others become catalog items.',
            instructions: [
                'In QuickBooks Online, go to <strong>Sales → Products and Services</strong>.',
                'Click the <strong>More</strong> (⋮) button at the top right.',
                'Choose <strong>Export to Excel</strong>.',
                'Save the <code>.xlsx</code> file and upload it here.'
            ]
        },
        {
            id: 5, key: 'qbo_invoices', title: 'Invoices',
            icon: 'bi-receipt', fileAccept: '.xlsx,.xls',
            endpoint: '/Tools/ImportQboInvoices',
            deps: [2],
            intro: 'Import historical invoices. Customers should be imported first for matching.',
            instructions: [
                'In QuickBooks Online, go to <strong>Reports</strong> and search for <strong>Invoice List</strong>.',
                'Set the date range to cover all of your history.',
                'Click the <strong>Export</strong> icon and choose <strong>Export to Excel</strong>.',
                'Save the <code>.xlsx</code> file and upload it here.'
            ]
        },
        {
            id: 6, key: 'qbo_transactions', title: 'Payments',
            icon: 'bi-cash-stack', fileAccept: '.xlsx,.xls',
            endpoint: '/Tools/ImportQboTransactions',
            deps: [5],
            intro: 'Import payment history to mark invoices as paid. Invoices must be imported first.',
            instructions: [
                'In QuickBooks Online, go to <strong>Reports</strong> and search for <strong>Transaction List by Date</strong>.',
                'Set the date range to cover all of your history.',
                'Click the <strong>Export</strong> icon and choose <strong>Export to Excel</strong>.',
                'Save the <code>.xlsx</code> file and upload it here.'
            ]
        }
    ];

    // ── Active step set (Desktop or Online) ───────────────────────────────────
    let activeSteps = STEPS; // default to Desktop
    let activeSource = 'desktop';

    // Statuses: pending | complete | skipped | error | blocked
    let currentStepIdx = 0; // index into activeSteps
    let stepState = {};      // key -> { status, result }

    // ── Init ──────────────────────────────────────────────────────────────────
    function init() {
        // Default all steps to pending
        activeSteps.forEach(s => {
            stepState[s.key] = stepState[s.key] || { status: 'pending', result: null };
        });
        refreshBlocked();
    }

    function refreshBlocked() {
        activeSteps.forEach(s => {
            if (s.deps.length === 0) return;
            const current = stepState[s.key].status;
            if (current === 'complete' || current === 'skipped') return;
            const anyDepUnresolved = s.deps.some(depId => {
                const depStep = activeSteps.find(x => x.id === depId);
                const depStatus = depStep ? stepState[depStep.key].status : 'pending';
                return depStatus === 'pending' || depStatus === 'blocked' || depStatus === 'error';
            });
            stepState[s.key].status = anyDepUnresolved ? 'blocked' : 'pending';
        });
    }

    // ── Open Modal ────────────────────────────────────────────────────────────
    window.openQbWizard = async function (source) {
        activeSteps = (source === 'online') ? QBO_STEPS : STEPS;
        activeSource = source;
        stepState = {}; // reset state when switching source
        init();
        // Load persisted state from server
        try {
            const resp = await fetch('/SetupWizard/GetQbMigrationState');
            const data = await resp.json();
            if (data.state) {
                const saved = JSON.parse(data.state);
                // Only restore state if same source
                if (saved && saved.source === source && saved.steps) {
                    Object.assign(stepState, saved.steps);
                }
                // Resume at first incomplete step
                const firstIncomplete = activeSteps.findIndex(s => {
                    const st = stepState[s.key]?.status;
                    return st !== 'complete' && st !== 'skipped';
                });
                currentStepIdx = firstIncomplete >= 0 ? firstIncomplete : activeSteps.length - 1;
            }
        } catch (e) {
            // Non-fatal — start from beginning
        }
        refreshBlocked();
        // Update modal title to reflect source
        var titleEl = document.getElementById('qbMigrationWizardLabel');
        if (titleEl) {
            titleEl.textContent = source === 'online'
                ? 'QuickBooks Online Migration Wizard'
                : 'QuickBooks Desktop Migration Wizard';
        }
        renderWizard();
        const modal = new bootstrap.Modal(document.getElementById('qbMigrationWizard'));
        modal.show();
    };

    // ── Render ────────────────────────────────────────────────────────────────
    function renderWizard() {
        renderStepIndicator();
        renderProgressBar();
        renderStepContent();
        renderFooterButtons();
    }

    function renderStepIndicator() {
        const container = document.getElementById('qbwStepIndicator');
        if (!container) return;
        container.innerHTML = activeSteps.map((s, idx) => {
            const st = stepState[s.key]?.status || 'pending';
            const isActive = idx === currentStepIdx;
            let statusClass = isActive ? 'active' : st;
            let icon = '';
            if (st === 'complete') icon = '<i class="bi bi-check-lg"></i>';
            else if (st === 'skipped') icon = '<i class="bi bi-skip-forward-fill"></i>';
            else if (st === 'error')   icon = '<i class="bi bi-exclamation-triangle-fill"></i>';
            else if (st === 'blocked') icon = '<i class="bi bi-lock-fill"></i>';
            else icon = s.id;
            return `<div class="qbw-dot ${statusClass}" onclick="qbwGoTo(${idx})" title="${s.title}">
                        <div class="qbw-dot-circle">${icon}</div>
                        <div class="qbw-dot-label">${s.title}</div>
                    </div>`;
        }).join('');

        // Update badge
        const badge = document.getElementById('qbwStepBadge');
        if (badge) badge.textContent = `Step ${currentStepIdx + 1} of ${activeSteps.length}`;
    }

    function renderProgressBar() {
        const completed = Object.values(stepState).filter(s => s.status === 'complete' || s.status === 'skipped').length;
        const pct = Math.round((completed / activeSteps.length) * 100);
        const bar = document.getElementById('qbwProgressBar');
        if (bar) bar.style.width = pct + '%';
    }

    function renderStepContent() {
        const container = document.getElementById('qbwStepContent');
        if (!container) return;
        const step = activeSteps[currentStepIdx];
        const st = stepState[step.key];
        container.innerHTML = buildStepHtml(step, st);
    }

    function buildStepHtml(step, st) {
        const isBlocked = st.status === 'blocked';
        const blockedNames = step.deps.map(depId => {
            const depStep = activeSteps.find(x => x.id === depId);
            return depStep ? depStep.title : '';
        }).filter(Boolean).join(', ');

        let resultHtml = '';
        if (st.result) {
            resultHtml = buildResultHtml(st.result, st.status);
        }

        const instructionItems = step.instructions.map(i => `<li class="mb-1">${i}</li>`).join('');

        return `
<div class="row g-4">
    <div class="col-12">
        <div class="d-flex align-items-start gap-3 mb-3">
            <div class="rounded-3 p-3 d-flex align-items-center justify-content-center flex-shrink-0"
                 style="background:var(--bs-primary-bg-subtle); color:var(--bs-primary); width:60px; height:60px;">
                <i class="bi ${step.icon} fs-3"></i>
            </div>
            <div>
                <h5 class="mb-1">${step.title}</h5>
                <p class="text-secondary mb-0">${step.intro}</p>
            </div>
        </div>

        ${isBlocked ? `
        <div class="alert alert-warning d-flex gap-2 mb-3">
            <i class="bi bi-lock-fill flex-shrink-0 mt-1"></i>
            <div>
                <strong>Step locked.</strong>
                This step requires the following to be completed first:
                <strong>${blockedNames}</strong>.
                Go back and complete those steps, then return here.
            </div>
        </div>` : ''}

        <div class="accordion mb-3" id="qbwInstructions-${step.id}">
            <div class="accordion-item">
                <h2 class="accordion-header">
                    <button class="accordion-button ${st.status === 'complete' ? 'collapsed' : ''}" type="button"
                            data-bs-toggle="collapse"
                            data-bs-target="#qbwInstructionsBody-${step.id}"
                            aria-expanded="${st.status === 'complete' ? 'false' : 'true'}">
                        <i class="bi bi-info-circle me-2"></i>How to export from QuickBooks Desktop
                    </button>
                </h2>
                <div id="qbwInstructionsBody-${step.id}" class="accordion-collapse collapse ${st.status === 'complete' ? '' : 'show'}">
                    <div class="accordion-body pb-2">
                        <ol class="mb-0">${instructionItems}</ol>
                    </div>
                </div>
            </div>
        </div>

        ${!isBlocked ? `
        <div class="qbw-upload-zone mb-3">
            <label class="form-label fw-medium mb-2" for="qbwFile-${step.id}">
                <i class="bi bi-cloud-upload me-1"></i>Select file to import
                <span class="text-secondary fw-normal">(${step.fileAccept})</span>
            </label>
            <input type="file" class="form-control" id="qbwFile-${step.id}" accept="${step.fileAccept}">
        </div>
        <button class="btn btn-primary" id="qbwImportBtn-${step.id}"
                onclick="qbwRunImport(${currentStepIdx})"
                ${isBlocked ? 'disabled' : ''}>
            <i class="bi bi-cloud-upload me-1"></i>Import Now
        </button>
        <div id="qbwImportSpinner-${step.id}" class="d-none d-inline-flex align-items-center gap-2 ms-3 text-secondary">
            <span class="spinner-border spinner-border-sm"></span> Importing&hellip;
        </div>` : ''}

        ${resultHtml ? `<div class="mt-3 qbw-result-box">${resultHtml}</div>` : ''}

        <div id="qbwResultContainer-${step.id}" class="mt-3 qbw-result-box"></div>
    </div>
</div>`;
    }

    function buildResultHtml(result, status) {
        const alertClass = status === 'error' ? 'alert-danger' : status === 'complete' ? 'alert-success' : 'alert-warning';
        const icon = status === 'error' ? 'bi-x-circle-fill' : status === 'complete' ? 'bi-check-circle-fill' : 'bi-exclamation-triangle-fill';
        let html = `<div class="alert ${alertClass} mb-0">
            <div class="d-flex align-items-center gap-2 mb-2">
                <i class="bi ${icon}"></i>
                <strong>${status === 'complete' ? 'Import successful' : status === 'error' ? 'Import failed' : 'Import completed with issues'}</strong>
            </div>
            <div class="row g-2 text-center mb-2">
                ${(result.billsImported != null || result.paymentsImported != null) ? `
                <div class="col-auto"><span class="badge bg-success"><i class="bi bi-file-earmark-text me-1"></i>${result.billsImported ?? 0} Bills Imported</span></div>
                <div class="col-auto"><span class="badge bg-info"><i class="bi bi-credit-card me-1"></i>${result.paymentsImported ?? 0} Payments Applied</span></div>
                ` : `
                <div class="col-auto"><span class="badge bg-secondary">${result.totalRecords ?? 0} Total</span></div>
                <div class="col-auto"><span class="badge bg-success">${result.importedCount ?? 0} Imported</span></div>
                <div class="col-auto"><span class="badge bg-info">${result.updatedCount ?? 0} Updated</span></div>
                `}
                ${(result.skippedCount ?? 0) > 0 ? `<div class="col-auto"><span class="badge bg-secondary">${result.skippedCount} Skipped</span></div>` : ''}
                ${(result.alreadyRecordedCount ?? 0) > 0 ? `<div class="col-auto"><span class="badge bg-secondary">${result.alreadyRecordedCount} Already Recorded</span></div>` : ''}
                <div class="col-auto"><span class="badge bg-danger">${result.errorCount ?? 0} Errors</span></div>
            </div>`;
        if (result.errors && result.errors.length > 0) {
            html += `<details><summary class="small fw-medium">${result.errors.length} error message(s) — click to expand</summary>
                <ul class="small mt-1 mb-0">
                    ${result.errors.slice(0, 20).map(e => `<li>${escHtml(e)}</li>`).join('')}
                    ${result.errors.length > 20 ? `<li>… and ${result.errors.length - 20} more</li>` : ''}
                </ul></details>`;
        }
        if (result.warnings && result.warnings.length > 0) {
            html += `<details><summary class="small fw-medium">${result.warnings.length} warning(s) — click to expand</summary>
                <ul class="small mt-1 mb-0">
                    ${result.warnings.slice(0, 10).map(w => `<li>${escHtml(w)}</li>`).join('')}
                </ul></details>`;
        }
        html += '</div>';
        return html;
    }

    function renderFooterButtons() {
        const btnBack   = document.getElementById('qbwBtnBack');
        const btnSkip   = document.getElementById('qbwBtnSkip');
        const btnNext   = document.getElementById('qbwBtnNext');
        const btnFinish = document.getElementById('qbwBtnFinish');
        if (!btnBack) return;

        const isLast = currentStepIdx === activeSteps.length - 1;
        const st = stepState[activeSteps[currentStepIdx].key];

        btnBack.classList.toggle('d-none', currentStepIdx === 0);
        btnSkip.classList.toggle('d-none', isLast || st.status === 'complete');
        btnNext.classList.toggle('d-none', isLast);
        btnFinish.classList.toggle('d-none', !isLast);

        // Disable Next if current step is still pending (not complete/skipped)
        const canAdvance = st.status === 'complete' || st.status === 'skipped' || st.status === 'blocked';
        btnNext.disabled = !canAdvance;
        if (isLast) {
            btnFinish.disabled = false;
        }
    }

    // ── Navigation ────────────────────────────────────────────────────────────
    window.qbwGoTo = function (idx) {
        if (idx < 0 || idx >= activeSteps.length) return;
        currentStepIdx = idx;
        renderWizard();
    };

    window.qbwBack = function () {
        if (currentStepIdx > 0) {
            currentStepIdx--;
            renderWizard();
        }
    };

    window.qbwNext = function () {
        if (currentStepIdx < activeSteps.length - 1) {
            currentStepIdx++;
            renderWizard();
        }
    };

    window.qbwSkip = function () {
        const step = activeSteps[currentStepIdx];
        stepState[step.key].status = 'skipped';
        refreshBlocked();
        saveState();
        window.qbwNext();
    };

    window.qbwFinish = function () {
        saveState();
        // Show completion message on Step 2 page if present
        const completionMsg = document.getElementById('qbWizardCompletionAlert');
        if (completionMsg) completionMsg.classList.remove('d-none');
    };

    // ── Import ────────────────────────────────────────────────────────────────
    window.qbwRunImport = async function (stepIdx) {
        const step = activeSteps[stepIdx];
        const fileInput = document.getElementById(`qbwFile-${step.id}`);
        const importBtn = document.getElementById(`qbwImportBtn-${step.id}`);
        const spinner   = document.getElementById(`qbwImportSpinner-${step.id}`);
        const resultContainer = document.getElementById(`qbwResultContainer-${step.id}`);

        if (!fileInput || !fileInput.files.length) {
            showToast('Please select a file to import.', 'warning');
            return;
        }

        importBtn.disabled = true;
        spinner?.classList.remove('d-none');
        resultContainer.innerHTML = '';

        try {
            const formData = new FormData();
            formData.append('file', fileInput.files[0]);

            // Include anti-forgery token if present on page
            const aft = document.querySelector('input[name="__RequestVerificationToken"]');
            if (aft) formData.append('__RequestVerificationToken', aft.value);

            const resp = await fetch(step.endpoint, {
                method: 'POST',
                body: formData
            });

            if (!resp.ok) {
                throw new Error(`Server returned ${resp.status}`);
            }

            const data = await resp.json();
            const result = data.result || data;

            // Errors array contains ImportErrorDto objects — split into hard errors vs warnings by severity
            const allErrors = result.errors ?? result.Errors ?? [];
            const toStr = e => (typeof e === 'string') ? e : (e.displayMessage ?? e.errorMessage ?? JSON.stringify(e));
            const hardErrors = allErrors.filter(e => !e.severity || e.severity === 'Error');
            const warnings   = allErrors.filter(e => e.severity === 'Warning' || e.severity === 'Skipped');

            const normalised = {
                totalRecords:        result.totalRecords        ?? result.TotalRecords        ?? 0,
                importedCount:       result.importedCount       ?? result.ImportedCount       ?? 0,
                updatedCount:        result.updatedCount        ?? result.UpdatedCount        ?? 0,
                skippedCount:        result.skippedCount        ?? result.SkippedCount        ?? 0,
                alreadyRecordedCount: result.alreadyRecordedCount ?? result.AlreadyRecordedCount ?? 0,
                billsImported:       result.billsImported       ?? result.BillsImported       ?? null,
                paymentsImported:    result.paymentsImported    ?? result.PaymentsImported    ?? null,
                errorCount:          hardErrors.length,
                errors:              hardErrors.map(toStr),
                warnings:            warnings.map(toStr)
            };

            // Hard failure = server returned success:false AND nothing was imported at all
            const hasHardError = result.success === false
                && normalised.importedCount === 0
                && normalised.updatedCount === 0;
            const tooManyErrors = false; // covered by hasHardError logic above

            if (hasHardError || tooManyErrors) {
                stepState[step.key] = { status: 'error', result: normalised };
                // Prompt: continue anyway?
                const proceed = confirm(
                    `Import completed with ${normalised.errorCount} error(s) out of ${normalised.totalRecords} records.\n\n` +
                    'Do you want to mark this step as done and continue anyway?\n' +
                    'Click OK to continue, or Cancel to stay on this step and review the errors.'
                );
                if (proceed) {
                    stepState[step.key].status = 'complete';
                }
            } else if (normalised.errorCount > 0) {
                // Partial errors — still counts as complete but show warning
                stepState[step.key] = { status: 'complete', result: normalised };
                showToast(`${step.title} imported with ${normalised.errorCount} errors. Review the details below.`, 'warning');
            } else {
                stepState[step.key] = { status: 'complete', result: normalised };
                showToast(`${step.title} imported successfully!`, 'success');
            }

            refreshBlocked();
            saveState();
            renderWizard();

        } catch (err) {
            const errResult = { totalRecords: 0, importedCount: 0, updatedCount: 0, skippedCount: 0, errorCount: 1, errors: [err.message], warnings: [] };
            stepState[step.key] = { status: 'error', result: errResult };
            refreshBlocked();
            saveState();
            renderWizard();
            showToast('Import failed: ' + err.message, 'danger');
        } finally {
            // Re-enable button (in case re-render didn't)
            const btn2 = document.getElementById(`qbwImportBtn-${step.id}`);
            if (btn2) btn2.disabled = false;
            const sp2 = document.getElementById(`qbwImportSpinner-${step.id}`);
            if (sp2) sp2.classList.add('d-none');
        }
    };

    // ── Persistence ───────────────────────────────────────────────────────────
    async function saveState() {
        try {
            const payload = JSON.stringify({ source: activeSource, steps: stepState });
            await fetch('/SetupWizard/SaveQbMigrationState', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ state: payload })
            });
        } catch (e) {
            // Non-fatal
        }
    }

    // ── Toast Helper ──────────────────────────────────────────────────────────
    function showToast(message, type) {
        // Use existing site toast infrastructure if available
        if (typeof window.showNotification === 'function') {
            window.showNotification(message, type);
            return;
        }
        // Fallback: simple Bootstrap toast
        const container = document.getElementById('toastContainer') || createToastContainer();
        const id = 'qbwToast-' + Date.now();
        const bgClass = type === 'success' ? 'bg-success' : type === 'warning' ? 'bg-warning' : type === 'danger' ? 'bg-danger' : 'bg-primary';
        const textClass = type === 'warning' ? 'text-dark' : 'text-white';
        const html = `<div id="${id}" class="toast align-items-center ${bgClass} ${textClass} border-0" role="alert">
            <div class="d-flex">
                <div class="toast-body">${escHtml(message)}</div>
                <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
            </div>
        </div>`;
        container.insertAdjacentHTML('beforeend', html);
        const el = document.getElementById(id);
        if (el) new bootstrap.Toast(el, { delay: 5000 }).show();
    }

    function createToastContainer() {
        const div = document.createElement('div');
        div.id = 'toastContainer';
        div.className = 'toast-container position-fixed bottom-0 end-0 p-3';
        div.style.zIndex = '9999';
        document.body.appendChild(div);
        return div;
    }

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

})();
