/* ─── Wallet X-Ray — Solana Wallet Analyser ─────────────────
   Full client-side scan: SOL balance, SPL tokens, NFTs,
   transactions, and wallet scoring.
   ─────────────────────────────────────────────────────────── */
(function () {
    'use strict';

    // ── Config ──
    const RPC_ENDPOINTS = [
        'https://mainnet.helius-rpc.com/?api-key=18509625-b17b-44ed-9ad9-489d3bf05f41',
        'https://api.mainnet-beta.solana.com',
        'https://solana-rpc.publicnode.com'
    ];
    const JUP_PRICE = 'https://price.jup.ag/v6/price';
    const JUP_TOKEN_LIST = 'https://token.jup.ag/all';
    const SOLSCAN_TX = 'https://solscan.io/tx/';
    const SOLSCAN_ACCT = 'https://solscan.io/account/';
    const TOKEN_PROGRAM = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
    const TOKEN_2022 = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
    const SOL_MINT = 'So11111111111111111111111111111111111111112';
    const LAMPORTS = 1e9;

    // ── DOM refs ──
    const $addr    = document.getElementById('wx-address');
    const $scanBtn = document.getElementById('wx-scan-btn');
    const $walBtn  = document.getElementById('wx-wallet-btn');
    const $error   = document.getElementById('wx-error');
    const $loading = document.getElementById('wx-loading');
    const $loadTxt = document.getElementById('wx-loading-text');
    const $loadSub = document.getElementById('wx-loading-sub');
    const $results = document.getElementById('wx-results');

    // ── State ──
    let tokenListCache = null;
    let tokenMapCache  = null;  // mint → {name, symbol, logoURI, decimals}
    let activeRpc = RPC_ENDPOINTS[0];
    let rpcIndex = 0;
    let scanning = false;

    // ── Helpers ──
    function truncAddr(a) {
        if (!a || a.length < 12) return a || '';
        return a.slice(0, 4) + '...' + a.slice(-4);
    }

    function fmtUsd(n) {
        if (n == null || isNaN(n)) return '$—';
        if (n >= 1e6) return '$' + (n / 1e6).toFixed(2) + 'M';
        if (n >= 1e3) return '$' + (n / 1e3).toFixed(2) + 'K';
        return '$' + n.toFixed(2);
    }

    function fmtSol(n) {
        if (n == null || isNaN(n)) return '—';
        return n.toFixed(4) + ' SOL';
    }

    function fmtNum(n) {
        if (n == null || isNaN(n)) return '—';
        return n.toLocaleString('en-US', { maximumFractionDigits: 4 });
    }

    function fmtDate(ts) {
        if (!ts) return '—';
        const d = new Date(ts * 1000);
        return d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });
    }

    function fmtTime(ts) {
        if (!ts) return '—';
        const d = new Date(ts * 1000);
        return d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short' }) + ' ' +
               d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
    }

    function daysBetween(ts1, ts2) {
        return Math.floor(Math.abs(ts2 - ts1) / 86400);
    }

    function isValidSolAddress(addr) {
        return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(addr);
    }

    function showError(msg) {
        $error.textContent = '✖ ' + msg;
        $error.classList.add('active');
    }

    function hideError() {
        $error.classList.remove('active');
        $error.textContent = '';
    }

    function setLoading(show, text, sub) {
        if (show) {
            $loading.classList.add('active');
            $results.classList.remove('active');
            if (text) $loadTxt.textContent = text;
            if (sub) $loadSub.textContent = sub;
        } else {
            $loading.classList.remove('active');
        }
    }

    function sectionLoading(id, show) {
        const el = document.getElementById(id);
        if (el) el.style.display = show ? 'flex' : 'none';
    }

    // ── RPC Call ──
    async function rpc(method, params, retries = 4) {
        let lastError;
        for (let i = 0; i <= retries; i++) {
            const url = RPC_ENDPOINTS[(rpcIndex + i) % RPC_ENDPOINTS.length];
            try {
                const res = await fetch(url, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
                });
                if (res.status === 429 || res.status === 403) {
                    lastError = new Error('Rate limited (HTTP ' + res.status + ')');
                    await sleep(800 * (i + 1));
                    continue;
                }
                if (!res.ok) {
                    lastError = new Error('HTTP ' + res.status);
                    continue;
                }
                const json = await res.json();
                if (json.error) {
                    if (json.error.code === -32429 || (json.error.message && json.error.message.includes('rate'))) {
                        lastError = new Error(json.error.message);
                        await sleep(800 * (i + 1));
                        continue;
                    }
                    throw new Error(json.error.message || 'RPC error');
                }
                // Success — remember this working RPC
                rpcIndex = (rpcIndex + i) % RPC_ENDPOINTS.length;
                activeRpc = RPC_ENDPOINTS[rpcIndex];
                return json.result;
            } catch (e) {
                lastError = e;
                if (i < retries) await sleep(500);
            }
        }
        throw lastError || new Error('All RPC endpoints failed');
    }

    function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }

    // ── Token List ──
    async function loadTokenList() {
        if (tokenMapCache) return tokenMapCache;

        // Try sessionStorage first
        try {
            const cached = sessionStorage.getItem('wx_token_list');
            if (cached) {
                const parsed = JSON.parse(cached);
                if (parsed._ts && Date.now() - parsed._ts < 3600000) {
                    tokenListCache = parsed.list;
                    tokenMapCache = buildTokenMap(tokenListCache);
                    return tokenMapCache;
                }
            }
        } catch (e) { /* ignore */ }

        try {
            const res = await fetch(JUP_TOKEN_LIST);
            tokenListCache = await res.json();
            tokenMapCache = buildTokenMap(tokenListCache);

            try {
                sessionStorage.setItem('wx_token_list', JSON.stringify({
                    _ts: Date.now(),
                    list: tokenListCache.slice(0, 5000) // Store top tokens only
                }));
            } catch (e) { /* storage full */ }

            return tokenMapCache;
        } catch (e) {
            console.warn('[WX] Failed to load token list:', e);
            tokenMapCache = new Map();
            return tokenMapCache;
        }
    }

    function buildTokenMap(list) {
        const map = new Map();
        if (!Array.isArray(list)) return map;
        for (const t of list) {
            if (t.address) {
                map.set(t.address, {
                    name: t.name || 'Unknown',
                    symbol: t.symbol || '???',
                    logoURI: t.logoURI || null,
                    decimals: t.decimals ?? 0
                });
            }
        }
        return map;
    }

    // ── Jupiter Prices ──
    async function fetchPrices(mints) {
        if (!mints || mints.length === 0) return {};
        const prices = {};
        // Batch in groups of 100
        const batches = [];
        for (let i = 0; i < mints.length; i += 100) {
            batches.push(mints.slice(i, i + 100));
        }
        for (const batch of batches) {
            try {
                const ids = batch.join(',');
                const res = await fetch(JUP_PRICE + '?ids=' + ids);
                const json = await res.json();
                if (json.data) {
                    for (const [mint, info] of Object.entries(json.data)) {
                        prices[mint] = info.price || 0;
                    }
                }
            } catch (e) {
                console.warn('[WX] Price fetch error:', e);
            }
            if (batches.length > 1) await sleep(300);
        }
        return prices;
    }

    // ══════════════════════════════════════════════════════════
    //  MAIN SCAN
    // ══════════════════════════════════════════════════════════
    async function scan(address) {
        if (scanning) return;
        if (!isValidSolAddress(address)) {
            showError('Invalid Solana address. Check the format and try again.');
            return;
        }

        scanning = true;
        hideError();
        setLoading(true, 'SCANNING WALLET', 'Querying Solana mainnet...');
        $scanBtn.disabled = true;

        // Reset result sections
        document.getElementById('wx-overview').innerHTML = '';
        document.getElementById('wx-tokens-content').innerHTML = '';
        document.getElementById('wx-nfts-content').innerHTML = '';
        document.getElementById('wx-tx-content').innerHTML = '';
        document.getElementById('wx-score-content').innerHTML = '';
        document.getElementById('wx-wallet-short').textContent = truncAddr(address);
        document.getElementById('wx-token-count').textContent = '';
        document.getElementById('wx-nft-count').textContent = '';
        document.getElementById('wx-tx-count').textContent = '';

        try {
            // Load token list in background
            const tokenMapPromise = loadTokenList();

            // ── Step 1: SOL Balance ──
            setLoading(true, 'READING SOL BALANCE', 'getBalance...');
            const balResult = await rpc('getBalance', [address, { commitment: 'confirmed' }]);
            const solBalance = (balResult?.value || 0) / LAMPORTS;

            // ── Step 2: Token Accounts ──
            setLoading(true, 'SCANNING TOKEN ACCOUNTS', 'getTokenAccountsByOwner...');
            const tokenAccounts = await rpc('getTokenAccountsByOwner', [
                address,
                { programId: TOKEN_PROGRAM },
                { encoding: 'jsonParsed' }
            ]);

            // Also check Token-2022
            let token2022Accounts = null;
            try {
                token2022Accounts = await rpc('getTokenAccountsByOwner', [
                    address,
                    { programId: TOKEN_2022 },
                    { encoding: 'jsonParsed' }
                ]);
            } catch (e) { /* ignore */ }

            const allAccounts = [
                ...(tokenAccounts?.value || []),
                ...(token2022Accounts?.value || [])
            ];

            // Parse token accounts
            const tokens = [];
            const nftCandidates = [];

            for (const acct of allAccounts) {
                const info = acct.account?.data?.parsed?.info;
                if (!info) continue;
                const mint = info.mint;
                const amount = parseFloat(info.tokenAmount?.uiAmountString || '0');
                const decimals = info.tokenAmount?.decimals || 0;

                if (amount === 0) continue; // Skip zero balances

                if (decimals === 0 && amount === 1) {
                    nftCandidates.push({ mint, amount });
                } else {
                    tokens.push({ mint, amount, decimals });
                }
            }

            // ── Step 3: Fetch Prices ──
            setLoading(true, 'FETCHING PRICES', 'Querying Jupiter Price API...');
            const tokenMap = await tokenMapPromise;
            const priceMints = [SOL_MINT, ...tokens.map(t => t.mint)];
            const prices = await fetchPrices(priceMints);
            const solPrice = prices[SOL_MINT] || 0;

            // Enrich tokens with metadata and prices
            const enrichedTokens = tokens.map(t => {
                const meta = tokenMap.get(t.mint) || { name: 'Unknown Token', symbol: '???', logoURI: null };
                const price = prices[t.mint] || 0;
                const usdValue = t.amount * price;
                return { ...t, ...meta, price, usdValue };
            }).sort((a, b) => b.usdValue - a.usdValue);

            // ── Step 4: Transactions ──
            setLoading(true, 'LOADING TRANSACTIONS', 'getSignaturesForAddress...');
            let signatures = [];
            try {
                signatures = await rpc('getSignaturesForAddress', [
                    address,
                    { limit: 20, commitment: 'confirmed' }
                ]) || [];
            } catch (e) {
                console.warn('[WX] Failed to fetch signatures:', e);
            }

            // ── Step 5: Calculate wallet age ──
            let oldestTx = null;
            let walletAge = 0;
            let totalTxCount = 0;

            try {
                // Get oldest signature for age
                const oldestSigs = await rpc('getSignaturesForAddress', [
                    address,
                    { limit: 1, commitment: 'confirmed', before: undefined }
                ]);

                // Try to get a rough total count — fetch the last page
                // We'll use the first tx time and recent tx to estimate
                if (signatures.length > 0) {
                    const newest = signatures[0]?.blockTime || 0;
                    // Try to find oldest
                    let lastSig = signatures[signatures.length - 1]?.signature;
                    let oldest = signatures[signatures.length - 1]?.blockTime || 0;
                    let fetchMore = true;
                    let pages = 0;
                    totalTxCount = signatures.length;

                    while (fetchMore && pages < 50) {
                        try {
                            const more = await rpc('getSignaturesForAddress', [
                                address,
                                { limit: 1000, before: lastSig, commitment: 'confirmed' }
                            ]);
                            if (!more || more.length === 0) {
                                fetchMore = false;
                            } else {
                                totalTxCount += more.length;
                                lastSig = more[more.length - 1].signature;
                                oldest = more[more.length - 1].blockTime || oldest;
                                if (more.length < 1000) fetchMore = false;
                            }
                            pages++;
                            await sleep(200);
                        } catch (e) {
                            fetchMore = false;
                        }
                    }

                    oldestTx = oldest;
                    if (oldest) {
                        walletAge = daysBetween(oldest, Date.now() / 1000);
                    }
                }
            } catch (e) {
                console.warn('[WX] Error getting wallet age:', e);
                if (signatures.length > 0) {
                    oldestTx = signatures[signatures.length - 1]?.blockTime;
                    totalTxCount = signatures.length;
                    if (oldestTx) walletAge = daysBetween(oldestTx, Date.now() / 1000);
                }
            }

            // ── RENDER EVERYTHING ──
            setLoading(false);
            $results.classList.add('active');

            // Calculate portfolio total
            const solUsd = solBalance * solPrice;
            const tokenUsd = enrichedTokens.reduce((sum, t) => sum + t.usdValue, 0);
            const totalPortfolio = solUsd + tokenUsd;

            // Activity rating
            let activityRating = 'DORMANT';
            let activityClass = 'dormant';
            if (walletAge < 30) { activityRating = 'NEW'; activityClass = 'new'; }
            else if (totalTxCount > 500 && walletAge > 0 && (totalTxCount / walletAge) > 0.5) { activityRating = 'ACTIVE'; activityClass = 'active'; }
            else if (totalTxCount > 100) { activityRating = 'MODERATE'; activityClass = 'active'; }
            else if (totalTxCount < 10 && walletAge > 180) { activityRating = 'DEAD'; activityClass = 'dead'; }

            // ── Render Overview ──
            renderOverview({
                solBalance, solPrice, solUsd, totalPortfolio,
                walletAge, oldestTx, totalTxCount, activityRating, activityClass,
                tokenCount: enrichedTokens.length, nftCount: nftCandidates.length
            });

            // ── Render Tokens ──
            sectionLoading('wx-tokens-loading', false);
            renderTokens(enrichedTokens);

            // ── Render NFTs ──
            sectionLoading('wx-nfts-loading', false);
            renderNfts(nftCandidates, tokenMap);

            // ── Render Transactions ──
            sectionLoading('wx-tx-loading', false);
            renderTransactions(signatures, address);

            // ── Render Wallet Score ──
            sectionLoading('wx-score-loading', false);
            renderScore({
                walletAge, totalTxCount,
                tokenCount: enrichedTokens.length,
                nftCount: nftCandidates.length,
                solBalance, totalPortfolio
            });

        } catch (err) {
            setLoading(false);
            showError('Scan failed: ' + (err.message || 'Unknown error. The RPC may be rate-limited — try again in a few seconds.'));
            console.error('[WX] Scan error:', err);
        } finally {
            scanning = false;
            $scanBtn.disabled = false;
        }
    }

    // ══════════════════════════════════════════════════════════
    //  RENDERERS
    // ══════════════════════════════════════════════════════════

    function renderOverview(d) {
        const container = document.getElementById('wx-overview');
        container.innerHTML = `
            <div class="wx-stat-card">
                <div class="wx-stat-label">SOL BALANCE</div>
                <div class="wx-stat-value sol">${fmtSol(d.solBalance)}</div>
                <div class="wx-stat-sub">${fmtUsd(d.solUsd)} @ ${fmtUsd(d.solPrice)}/SOL</div>
            </div>
            <div class="wx-stat-card">
                <div class="wx-stat-label">PORTFOLIO VALUE</div>
                <div class="wx-stat-value usd">${fmtUsd(d.totalPortfolio)}</div>
                <div class="wx-stat-sub">${d.tokenCount} tokens + ${d.nftCount} NFTs</div>
            </div>
            <div class="wx-stat-card">
                <div class="wx-stat-label">WALLET AGE</div>
                <div class="wx-stat-value">${d.walletAge > 0 ? d.walletAge + 'd' : '—'}</div>
                <div class="wx-stat-sub">${d.oldestTx ? 'Since ' + fmtDate(d.oldestTx) : 'No transactions found'}</div>
            </div>
            <div class="wx-stat-card">
                <div class="wx-stat-label">TOTAL TRANSACTIONS</div>
                <div class="wx-stat-value">${d.totalTxCount.toLocaleString()}</div>
                <div class="wx-stat-sub">${d.walletAge > 0 ? (d.totalTxCount / d.walletAge).toFixed(1) + ' tx/day avg' : '—'}</div>
            </div>
            <div class="wx-stat-card">
                <div class="wx-stat-label">ACTIVITY RATING</div>
                <div class="wx-stat-value"><span class="wx-activity-badge ${d.activityClass}">${d.activityRating}</span></div>
            </div>
        `;
    }

    function renderTokens(tokens) {
        const container = document.getElementById('wx-tokens-content');
        const countEl = document.getElementById('wx-token-count');
        countEl.textContent = tokens.length + ' TOKEN' + (tokens.length !== 1 ? 'S' : '');

        if (tokens.length === 0) {
            container.innerHTML = '<div class="wx-empty">No SPL tokens found in this wallet.</div>';
            return;
        }

        let html = `
            <div style="overflow-x:auto">
            <table class="wx-token-table">
                <thead>
                    <tr>
                        <th>TOKEN</th>
                        <th>BALANCE</th>
                        <th>PRICE</th>
                        <th>VALUE</th>
                        <th class="wx-col-mint">MINT</th>
                    </tr>
                </thead>
                <tbody>
        `;

        for (const t of tokens) {
            const logo = t.logoURI
                ? `<img class="wx-token-logo" src="${t.logoURI}" alt="${t.symbol}" loading="lazy" onerror="this.outerHTML='<div class=wx-token-logo-placeholder>?</div>'">`
                : '<div class="wx-token-logo-placeholder">?</div>';

            html += `
                <tr>
                    <td>
                        <div class="wx-token-row-info">
                            ${logo}
                            <div>
                                <div class="wx-token-name">${escHtml(t.name)}</div>
                                <div class="wx-token-symbol">${escHtml(t.symbol)}</div>
                            </div>
                        </div>
                    </td>
                    <td>${fmtNum(t.amount)}</td>
                    <td>${t.price > 0 ? fmtUsd(t.price) : '—'}</td>
                    <td class="wx-token-value">${t.usdValue > 0 ? fmtUsd(t.usdValue) : '—'}</td>
                    <td class="wx-col-mint">
                        <a href="${SOLSCAN_ACCT}${t.mint}" target="_blank" rel="noopener" class="wx-token-mint" title="${t.mint}">${truncAddr(t.mint)}</a>
                    </td>
                </tr>
            `;
        }

        html += '</tbody></table></div>';
        container.innerHTML = html;
    }

    function renderNfts(nfts, tokenMap) {
        const container = document.getElementById('wx-nfts-content');
        const countEl = document.getElementById('wx-nft-count');
        countEl.textContent = nfts.length + ' NFT' + (nfts.length !== 1 ? 'S' : '');

        if (nfts.length === 0) {
            container.innerHTML = '<div class="wx-empty">No NFTs detected in this wallet.</div>';
            return;
        }

        let html = '<div class="wx-nft-grid">';
        for (const nft of nfts) {
            const meta = tokenMap.get(nft.mint) || { name: truncAddr(nft.mint), symbol: 'NFT', logoURI: null };
            const img = meta.logoURI
                ? `<img class="wx-nft-img" src="${meta.logoURI}" alt="${escHtml(meta.name)}" loading="lazy" onerror="this.outerHTML='<div class=wx-nft-img-placeholder>🖼</div>'">`
                : '<div class="wx-nft-img-placeholder">🖼</div>';

            html += `
                <a href="${SOLSCAN_ACCT}${nft.mint}" target="_blank" rel="noopener" class="wx-nft-card">
                    ${img}
                    <div class="wx-nft-info">
                        <div class="wx-nft-name">${escHtml(meta.name)}</div>
                        <div class="wx-nft-collection">${escHtml(meta.symbol)}</div>
                    </div>
                </a>
            `;
        }
        html += '</div>';
        container.innerHTML = html;
    }

    function renderTransactions(sigs, address) {
        const container = document.getElementById('wx-tx-content');
        const countEl = document.getElementById('wx-tx-count');
        countEl.textContent = 'LAST ' + sigs.length;

        if (sigs.length === 0) {
            container.innerHTML = '<div class="wx-empty">No recent transactions found.</div>';
            return;
        }

        let html = `
            <div class="wx-tx-header">
                <span>SIGNATURE</span>
                <span>TYPE</span>
                <span>STATUS</span>
                <span>TIME</span>
            </div>
            <div class="wx-tx-list">
        `;

        for (const sig of sigs) {
            const signature = sig.signature || '';
            const truncSig = signature.slice(0, 8) + '...' + signature.slice(-4);
            const time = fmtTime(sig.blockTime);
            const err = sig.err;
            const status = err ? 'FAILED' : 'SUCCESS';
            const statusClass = err ? 'wx-change-neg' : 'wx-change-pos';

            // Classify based on memo and other signals
            let type = 'UNKNOWN';
            let typeClass = 'unknown';
            if (sig.memo) {
                type = 'MEMO';
                typeClass = 'transfer';
            }
            // We'll use a basic heuristic — without fetching full tx details
            // (to avoid rate limiting), classify by slot patterns
            if (!err) {
                type = 'TRANSACTION';
                typeClass = 'transfer';
            }

            html += `
                <div class="wx-tx-row">
                    <a href="${SOLSCAN_TX}${signature}" target="_blank" rel="noopener" class="wx-tx-sig">${truncSig}</a>
                    <span class="wx-tx-type ${typeClass}">${type}</span>
                    <span class="wx-tx-amount ${statusClass}">${status}</span>
                    <span class="wx-tx-time">${time}</span>
                </div>
            `;
        }

        html += '</div>';
        container.innerHTML = html;
    }

    function renderScore(d) {
        const container = document.getElementById('wx-score-content');

        // Calculate individual scores (0–100)
        const ageScore = Math.min(100, Math.floor((d.walletAge / 730) * 100)); // 2 years = 100
        const activityScore = Math.min(100, Math.floor((d.totalTxCount / 1000) * 100)); // 1000 tx = 100
        const diversityScore = Math.min(100, Math.floor(((d.tokenCount + d.nftCount) / 50) * 100)); // 50 assets = 100
        const wealthScore = Math.min(100, Math.floor((d.totalPortfolio / 10000) * 100)); // $10k = 100

        // Overall
        const overall = Math.round((ageScore * 0.25) + (activityScore * 0.30) + (diversityScore * 0.20) + (wealthScore * 0.25));

        // Grade
        let grade, gradeClass;
        if (overall >= 85) { grade = 'A+'; gradeClass = 'wx-grade-a'; }
        else if (overall >= 70) { grade = 'A'; gradeClass = 'wx-grade-a'; }
        else if (overall >= 55) { grade = 'B'; gradeClass = 'wx-grade-b'; }
        else if (overall >= 40) { grade = 'C'; gradeClass = 'wx-grade-c'; }
        else if (overall >= 25) { grade = 'D'; gradeClass = 'wx-grade-d'; }
        else { grade = 'F'; gradeClass = 'wx-grade-f'; }

        function barColor(val) {
            if (val >= 70) return 'green';
            if (val >= 45) return 'yellow';
            if (val >= 25) return 'orange';
            return 'red';
        }

        container.innerHTML = `
            <div class="wx-score-container">
                <div>
                    <div class="wx-grade-circle ${gradeClass}">
                        <div class="wx-grade-letter">${grade}</div>
                        <div class="wx-grade-label">OVERALL ${overall}/100</div>
                    </div>
                </div>
                <div class="wx-score-bars">
                    <div class="wx-score-item">
                        <div class="wx-score-item-header">
                            <span class="wx-score-item-label">AGE SCORE</span>
                            <span class="wx-score-item-val">${ageScore}/100</span>
                        </div>
                        <div class="wx-score-bar-track">
                            <div class="wx-score-bar-fill ${barColor(ageScore)}" style="width:${ageScore}%"></div>
                        </div>
                    </div>
                    <div class="wx-score-item">
                        <div class="wx-score-item-header">
                            <span class="wx-score-item-label">ACTIVITY SCORE</span>
                            <span class="wx-score-item-val">${activityScore}/100</span>
                        </div>
                        <div class="wx-score-bar-track">
                            <div class="wx-score-bar-fill ${barColor(activityScore)}" style="width:${activityScore}%"></div>
                        </div>
                    </div>
                    <div class="wx-score-item">
                        <div class="wx-score-item-header">
                            <span class="wx-score-item-label">DIVERSITY SCORE</span>
                            <span class="wx-score-item-val">${diversityScore}/100</span>
                        </div>
                        <div class="wx-score-bar-track">
                            <div class="wx-score-bar-fill ${barColor(diversityScore)}" style="width:${diversityScore}%"></div>
                        </div>
                    </div>
                    <div class="wx-score-item">
                        <div class="wx-score-item-header">
                            <span class="wx-score-item-label">PORTFOLIO SCORE</span>
                            <span class="wx-score-item-val">${wealthScore}/100</span>
                        </div>
                        <div class="wx-score-bar-track">
                            <div class="wx-score-bar-fill ${barColor(wealthScore)}" style="width:${wealthScore}%"></div>
                        </div>
                    </div>
                </div>
            </div>
        `;
    }

    // ── HTML escape ──
    function escHtml(str) {
        if (!str) return '';
        const el = document.createElement('span');
        el.textContent = str;
        return el.innerHTML;
    }

    // ══════════════════════════════════════════════════════════
    //  EVENT HANDLERS
    // ═════════════════════════════════���════════════════════════

    // Scan button
    $scanBtn.addEventListener('click', () => {
        const addr = $addr.value.trim();
        if (addr) scan(addr);
        else showError('Please enter a Solana wallet address or connect your wallet.');
    });

    // Enter key
    $addr.addEventListener('keydown', (e) => {
        if (e.key === 'Enter') {
            e.preventDefault();
            $scanBtn.click();
        }
    });

    // Connect wallet button
    $walBtn.addEventListener('click', async () => {
        if (!window.solWallet) {
            showError('No wallet detected. Install Phantom, Solflare, or another Solana wallet.');
            return;
        }

        if (window.solWallet.connected && window.solWallet.address) {
            // Already connected — use address
            $addr.value = window.solWallet.address;
            scan(window.solWallet.address);
            return;
        }

        // Show wallet picker
        const available = window.solWallet.getAvailableWallets();
        if (available.length === 0) {
            showError('No Solana wallet extensions detected. Install Phantom or Solflare.');
            return;
        }

        // If only one wallet, connect directly
        if (available.length === 1) {
            try {
                const result = await window.solWallet.connect(available[0]);
                $addr.value = result.address;
                $walBtn.textContent = window.solWallet.truncAddr(result.address);
                $walBtn.classList.add('connected');
                scan(result.address);
            } catch (err) {
                showError('Wallet connection failed: ' + (err.message || 'User rejected'));
            }
            return;
        }

        // Multiple wallets — connect first available (nav handles picker)
        try {
            const result = await window.solWallet.connect(available[0]);
            $addr.value = result.address;
            $walBtn.textContent = window.solWallet.truncAddr(result.address);
            $walBtn.classList.add('connected');
            scan(result.address);
        } catch (err) {
            showError('Wallet connection failed: ' + (err.message || 'User rejected'));
        }
    });

    // Listen for wallet connection from nav
    window.addEventListener('wallet-connected', (e) => {
        const addr = e.detail?.address;
        if (addr) {
            $addr.value = addr;
            $walBtn.textContent = truncAddr(addr);
            $walBtn.classList.add('connected');
        }
    });

    window.addEventListener('wallet-disconnected', () => {
        $walBtn.textContent = 'CONNECT WALLET';
        $walBtn.classList.remove('connected');
    });

    // Auto-populate if wallet already connected
    if (window.solWallet?.connected && window.solWallet.address) {
        $addr.value = window.solWallet.address;
        $walBtn.textContent = truncAddr(window.solWallet.address);
        $walBtn.classList.add('connected');
    }

    // Check URL params for address
    const urlParams = new URLSearchParams(window.location.search);
    const paramAddr = urlParams.get('address') || urlParams.get('wallet');
    if (paramAddr && isValidSolAddress(paramAddr)) {
        $addr.value = paramAddr;
        // Auto-scan after slight delay
        setTimeout(() => scan(paramAddr), 500);
    }

})();
