// Predictalytics Analytics — Dashboard Application
const API_BASE = '';

// ─── Theme Toggle ───
const themeToggle = document.getElementById('themeToggle');
const html = document.documentElement;
const savedTheme = localStorage.getItem('ba-theme') || 'dark';
html.setAttribute('data-theme', savedTheme);
themeToggle.addEventListener('click', () => {
    const current = html.getAttribute('data-theme');
    const next = current === 'dark' ? 'light' : 'dark';
    html.setAttribute('data-theme', next);
    localStorage.setItem('ba-theme', next);
    updateChartColors();
});

// ─── Navigation ───
document.querySelectorAll('.nav-item[data-page]').forEach(item => {
    item.addEventListener('click', e => {
        e.preventDefault();
        const page = item.dataset.page;
        document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
        item.classList.add('active');
        document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
        document.getElementById(`page-${page}`).classList.add('active');
        if (page === 'traders') loadTraders();
        if (page === 'alerts') loadAlerts();
        if (page === 'markets') loadMarkets();
        if (page === 'jobs') loadJobs();
        if (page === 'watchlist') loadWatchlist();
    });
});

let pageHistory = ['dashboard'];
function navigateTo(pageId) {
    document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
    document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
    const page = document.getElementById(`page-${pageId}`);
    if (page) {
        page.classList.add('active');
        pageHistory.push(pageId);
    }
}

function navigateBack() {
    if (pageHistory.length > 1) {
        pageHistory.pop();
        const prev = pageHistory[pageHistory.length - 1];
        document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
        document.getElementById(`page-${prev}`).classList.add('active');
    }
}

// ─── Search ───
const searchInput = document.getElementById('searchInput');
searchInput?.addEventListener('keypress', e => {
    if (e.key === 'Enter') {
        const query = searchInput.value.trim();
        if (query) performSearch(query);
    }
});

async function performSearch(query) {
    navigateTo('search');
    document.getElementById('search-title').textContent = `Search Results for "${query}"`;
    const data = await api(`/api/search?q=${encodeURIComponent(query)}`);
    const tBody = document.getElementById('searchTradersBody');
    const mBody = document.getElementById('searchMarketsBody');

    if (!data) return;

    tBody.innerHTML = data.traders.map((t, i) => `
        <tr>
            <td>${i + 1}</td>
            <td><strong>${t.displayName}</strong></td>
            <td>${t.platform}</td>
            <td>${Number(t.combinedScore).toFixed(1)}</td>
            <td>${fmt.tier(t.tier)}</td>
            <td><button class="btn-sm" onclick="viewTrader(${t.id})">View</button></td>
        </tr>
    `).join('') || '<tr><td colspan="6">No traders found</td></tr>';

    mBody.innerHTML = data.markets.map(m => `
        <tr>
            <td>${m.platform}</td>
            <td title="${m.question}">${m.question.substring(0, 50)}...</td>
            <td>${fmt.usd(m.volume)}</td>
            <td>${m.isResolved ? 'Resolved' : 'Active'}</td>
            <td><button class="btn-sm" onclick="viewMarket(${m.id})">View</button></td>
        </tr>
    `).join('') || '<tr><td colspan="5">No markets found</td></tr>';
}

async function manualAddTrader() {
    const platform = document.getElementById('addPlatform').value;
    const wallet = document.getElementById('addWallet').value.trim();
    if (!wallet) return;

    const res = await fetch(`/api/traders?platform=${platform}&wallet=${wallet}`, { method: 'POST' });
    if (res.ok) {
        const data = await res.json();
        alert('Trader added successfully! Sync will start shortly.');
        document.getElementById('addWallet').value = '';
        viewTrader(data.id);
    } else {
        alert('Failed to add trader.');
    }
}



let currentPlatform = 'All';
let currentSort = 'default';
let sortDirection = -1;

document.getElementById('platformSelect')?.addEventListener('change', (e) => {
    currentPlatform = e.target.value;
    refreshActivePage();
});

document.getElementById('sortSelect')?.addEventListener('change', (e) => {
    currentSort = e.target.value;
    refreshActivePage();
});

function refreshActivePage() {
    const activePage = document.querySelector('.page.active')?.id;
    if (activePage === 'page-dashboard') loadDashboard();
    else if (activePage === 'page-traders') loadTraders();
    else if (activePage === 'page-markets') loadMarkets();
    else if (activePage === 'page-watchlist') loadWatchlist();
}

// ─── API Helpers ───
async function api(endpoint, options = {}) {
    try {
        const res = await fetch(`${API_BASE}${endpoint}`, options);
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        const text = await res.text();
        return text ? JSON.parse(text) : true;
    } catch (err) {
        console.error(`API Error [${endpoint}]:`, err);
        return null;
    }
}

// ─── Format Helpers ───
const fmt = {
    usd: v => { if (v === null || v === undefined) return '$0'; const n = Number(v); return n >= 1000000 ? `$${(n/1000000).toFixed(1)}M` : n >= 1000 ? `$${(n/1000).toFixed(1)}K` : `$${n.toFixed(0)}`; },
    pct: v => { 
        if (v === null || v === undefined) return '0%'; 
        const n = Number(v); 
        const color = n > 55 ? 'var(--pnl-positive)' : n < 45 ? 'var(--pnl-negative)' : 'var(--text)';
        return `<span style="color:${color};font-weight:${n>55||n<45?'600':'normal'};">${n.toFixed(1)}%</span>`; 
    },
    num: v => { if (v === null || v === undefined) return '0'; return Number(v).toLocaleString(); },
    time: v => { if (!v) return '—'; const d = new Date(v); const now = new Date(); const diff = (now - d) / 1000;
        if (diff < 60) return `${Math.floor(diff)}s ago`;
        if (diff < 3600) return `${Math.floor(diff/60)}m ago`;
        if (diff < 86400) return `${Math.floor(diff/3600)}h ago`;
        return d.toLocaleDateString(); },
    tier: t => { if (!t) return '—'; const cls = `tier-${t.toString().toLowerCase()}`; return `<span class="tier-badge ${cls}">${t}</span>`; },
    side: s => { if (!s) return '—'; return `<span class="side-${s.toString().toLowerCase()}">${s}</span>`; },
    pnl: v => { if (v === null || v === undefined) return '$0'; const n = Number(v); return `<span class="${n >= 0 ? 'pnl-positive' : 'pnl-negative'}">${fmt.usd(Math.abs(n))}${n >= 0 ? ' ▲' : ' ▼'}</span>`; }
};

// ─── Chart instances ───
let platformChart, tierChart;

function getChartColors() {
    return {
        text: '#8B93A7',
        grid: 'rgba(255,255,255,0.07)',
        bg: 'rgba(255,255,255,0.045)'
    };
}

function getAvatarBg(id) {
    const gradients = [
        'linear-gradient(135deg,#1652F0,#4c8dff)',
        'linear-gradient(135deg,#12D48A,#0a8f5f)',
        'linear-gradient(135deg,#7c5cff,#4c8dff)',
        'linear-gradient(135deg,#ff9d4c,#ff6b8a)',
        'linear-gradient(135deg,#F6465D,#ff6b8a)'
    ];
    return gradients[id % gradients.length];
}

function getTraitClass(trait) {
    if (!trait) return 'tier-unknown';
    const lower = trait.toLowerCase();
    if (lower.includes('winrate') || lower.includes('wins') || lower.includes('farming') || lower.includes('insider')) return 'trait-winrate';
    if (lower.includes('volume') || lower.includes('amounts') || lower.includes('sizes') || lower.includes('stake')) return 'trait-volume';
    if (lower.includes('whale') || lower.includes('scalper') || lower.includes('martingale')) return 'trait-whales';
    if (lower.includes('sentiment') || lower.includes('cadence') || lower.includes('24_7') || lower.includes('copyable') || lower.includes('wallet')) return 'trait-sentiment';
    return 'tier-unknown';
}

const TraitMetadata = {
    "sub_second_cadence": {
        name: "Sub-Second Cadence",
        desc: "Executes trades in sub-second intervals, highly indicative of algorithmic execution."
    },
    "always_on_24_7": {
        name: "24/7 Activity",
        desc: "Trades at all hours of the day and night with very short gaps, indicating bot operation."
    },
    "uniform_sizes": {
        name: "Uniform Sizes",
        desc: "Executes trades with identical position sizes, suggesting a systematic layout."
    },
    "round_amounts": {
        name: "Round Amounts",
        desc: "Executes trades with round amounts (e.g. 100, 500, 1000 USD), common for manual traders."
    },
    "uses_split_merge": {
        name: "Split & Merge",
        desc: "Splits large positions into smaller orders or merges multiple positions to optimize slippage."
    },
    "both_sides_same_market": {
        name: "Two-Sided Market Maker",
        desc: "Buys and sells in the same market, extracting spreads or resolving inventory."
    },
    "resolution_farming": {
        name: "Resolution Farmer",
        desc: "Buys options at high probabilities (e.g., >93%) to collect predictable small returns."
    },
    "longshot_buyer": {
        name: "Longshot Buyer",
        desc: "Buys options at low probabilities (e.g., <10%), seeking rare high-payoff events."
    },
    "scalper": {
        name: "Scalper",
        desc: "Holds positions for very short periods (typically <1 hour) to lock in quick profits."
    },
    "holds_to_resolution": {
        name: "Holds to Resolution",
        desc: "Keeps positions open until the market officially resolves, avoiding early exits."
    },
    "fresh_wallet": {
        name: "Fresh Wallet",
        desc: "Wallet was created recently (less than 30 days of active history)."
    },
    "stable_stake_fraction": {
        name: "Stable Stake",
        desc: "Risk per trade is a highly stable fraction of estimated total bankroll."
    },
    "possible_insider": {
        name: "Possible Insider",
        desc: "Consistently wins low-probability bets with very low volume, indicating asymmetric information."
    },
    "thin_margin_wins": {
        name: "Thin Margin Winner",
        desc: "Consistently resolves trades with small margins of win (low ROI)."
    },
    "high_payoff_wins": {
        name: "High Payoff Winner",
        desc: "Consistently resolves trades with high margins of win (high ROI)."
    },
    "sells_at_loss": {
        name: "Sells at Loss",
        desc: "Uses strict stop-loss rules, selling options at a loss when the market goes against them."
    },
    "days_active": {
        name: "Days Active",
        desc: "The number of days this wallet has been active on-chain."
    },
    "trades_last_30_days": {
        name: "Trades Last 30d",
        desc: "The total number of trades executed in the last 30 days."
    },
    "martingale_pattern": {
        name: "Martingale Trader",
        desc: "Increases bet size after losses to attempt to recoup losses, typical of high-risk strategies."
    },
    "not_copyable_hf": {
        name: "Uncopyable (High Freq)",
        desc: "Trades at frequencies too high to replicate manually or with standard copytrading setups."
    }
};

function getTraitDisplayName(traitKey) {
    const meta = TraitMetadata[traitKey];
    return meta ? meta.name : traitKey.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
}

function getTraitDescription(traitKey) {
    const meta = TraitMetadata[traitKey];
    return meta ? meta.desc : 'No description available.';
}

// ─── Dashboard Load ───
async function loadDashboard() {
    const data = await api('/api/dashboard');
    if (!data) {
        document.getElementById('metricTraders').textContent = '0';
        document.getElementById('metricActive').textContent = '0';
        document.getElementById('metricTrades').textContent = '0';
        document.getElementById('metricVolume').textContent = '$0';
        return;
    }

    // Metrics
    document.getElementById('metricTraders').textContent = fmt.num(data.totalTraders);
    document.getElementById('metricActive').textContent = fmt.num(data.activeTraders24h);
    document.getElementById('metricTrades').textContent = fmt.num(data.totalTrades);
    document.getElementById('metricVolume').textContent = fmt.usd(data.totalVolume24h);

    // Alert badge
    const badge = document.getElementById('alertBadge');
    if (data.unreadAlerts > 0) { badge.textContent = data.unreadAlerts; badge.style.display = 'inline'; }
    else { badge.style.display = 'none'; }

    // Top Traders table
    const tbody = document.getElementById('topTradersBody');
    tbody.innerHTML = data.topTraders.map((t, i) => `
        <tr>
              <td onclick="viewTrader(${t.id})" style="cursor:pointer">${i + 1}</td>
              <td><strong><a href="#" onclick="viewTrader(${t.id}); return false;" style="color:var(--primary);text-decoration:none;">${t.displayName}</a></strong></td>
              <td onclick="viewTrader(${t.id})" style="cursor:pointer">${t.platform}</td>
              <td onclick="viewTrader(${t.id})" style="cursor:pointer"><strong>${Number(t.combinedScore).toFixed(1)}</strong></td>
              <td onclick="viewTrader(${t.id})" style="cursor:pointer">${fmt.pct(t.winRate)}</td>
              <td onclick="viewTrader(${t.id})" style="cursor:pointer">${fmt.pnl(t.totalPnl)}</td>
            <td onclick="viewTrader(${t.id})" style="cursor:pointer">${t.trades30d} | ${t.totalTrades}</td>
        </tr>
    `).join('');

    // Recent Trades table
    const rBody = document.getElementById('recentTradesBody');
    rBody.innerHTML = data.recentTrades.map(t => `
        <tr onclick="viewTrader(${t.traderId})">
            <td>${fmt.time(t.executedAt)}</td>
            <td><strong>${t.traderName}</strong></td>
            <td>${fmt.side(t.side)}</td>
            <td class="num-col">${Number(t.price).toFixed(2)}</td>
            <td class="num-col">${fmt.num(t.size)}</td>
        </tr>
    `).join('');

    const headerVol = document.getElementById('headerVolumeBadge');
    if (headerVol) {
        headerVol.textContent = `24H VOL: ${fmt.usd(data.volume24h)}`;
    }

    // Platform Chart
    const cc = getChartColors();
    const pLabels = Object.keys(data.platformBreakdown.traderCounts);
    const pData = Object.values(data.platformBreakdown.traderCounts);

    if (platformChart) platformChart.destroy();
    platformChart = new Chart(document.getElementById('platformChart'), {
        type: 'doughnut',
        data: { labels: pLabels.length ? pLabels : ['Keine Daten'], datasets: [{ data: pData.length ? pData : [1],
            backgroundColor: ['#1652F0', '#12D48A', '#7c5cff', '#ff9d4c', '#F6465D', '#5b9dff'],
            borderWidth: 0 }] },
        options: { responsive: true, maintainAspectRatio: false, cutout: '70%',
            plugins: { legend: { position: 'bottom', labels: { color: cc.text, padding: 16, font: { family: "'Manrope'", size: 12 } } } } }
    });

    // Tier chart
    const tierData = data.topTraders.reduce((acc, t) => { acc[t.tier] = (acc[t.tier] || 0) + 1; return acc; }, {});
    if (tierChart) tierChart.destroy();
    const tLabels = Object.keys(tierData).length ? Object.keys(tierData) : ['Keine Daten'];
    const tData = Object.values(tierData).length ? Object.values(tierData) : [1];
    tierChart = new Chart(document.getElementById('tierChart'), {
        type: 'bar',
        data: { labels: tLabels, datasets: [{ label: 'Trader', data: tData,
            backgroundColor: '#1652F0', borderRadius: 6, barThickness: 32 }] },
        options: { responsive: true, maintainAspectRatio: false,
            scales: { x: { grid: { display: false }, ticks: { color: cc.text, font: { family: "'Manrope'" } } },
                       y: { grid: { color: cc.grid }, ticks: { color: cc.text, font: { family: "'Manrope'" } } } },
            plugins: { legend: { display: false } } }
    });
}

function setTraderSort(field) {
    if (currentSort === field) {
        sortDirection *= -1;
    } else {
        currentSort = field;
        sortDirection = -1; // Default to descending
    }
    
    // Update tradersSort dropdown if it exists to match
    const sortSelect = document.getElementById('tradersSort');
    if (sortSelect) sortSelect.value = currentSort;
    
    loadTraders();
}

async function loadTraders() {
    let url = '/api/traders?skip=0&take=100';
    if (currentPlatform !== 'All') url += `&platform=${currentPlatform}`;
    
    const hcCheckbox = document.getElementById('chk-highly-copyable');
    if (hcCheckbox && hcCheckbox.checked) {
        url += `&highlyCopyable=true`;
    }

    const filterTrait = document.getElementById('filterTrait');
    if (filterTrait && filterTrait.value) {
        url += `&trait=${encodeURIComponent(filterTrait.value)}`;
    }

    let data = await api(url);
    const tbody = document.getElementById('allTradersBody');
    if (!data || !data.length) { tbody.innerHTML = '<tr><td colspan="11"><div class="empty-state"><p>No traders tracked yet.</p></div></td></tr>'; return; }

    const sortSelect = document.getElementById('tradersSort');
    if (sortSelect && sortSelect.value !== currentSort) currentSort = sortSelect.value;
    
    const filterWinrateMin = parseFloat(document.getElementById('filterWinrateMin')?.value);
    const filterCopyabilityMin = parseFloat(document.getElementById('filterCopyabilityMin')?.value);

    // Filtering
    if (!isNaN(filterWinrateMin)) data = data.filter(t => t.winRate >= filterWinrateMin);
    if (!isNaN(filterCopyabilityMin)) data = data.filter(t => t.copytradingCopyabilityScore >= filterCopyabilityMin);

    // Sorting
    data.sort((a, b) => {
        let valA, valB;
        if (currentSort === 'score') { valA = a.combinedScore; valB = b.combinedScore; }
        else if (currentSort === 'quality') { valA = a.copytradingQualityScore || 0; valB = b.copytradingQualityScore || 0; }
        else if (currentSort === 'copyability') { valA = a.copytradingCopyabilityScore || 0; valB = b.copytradingCopyabilityScore || 0; }
        else if (currentSort === 'winrate') { valA = a.winRate; valB = b.winRate; }
        else if (currentSort === 'pnl') { valA = a.totalPnl; valB = b.totalPnl; }
        else if (currentSort === 'trades') { valA = a.trades30d; valB = b.trades30d; }
        else if (currentSort === 'name') { return a.displayName.localeCompare(b.displayName) * sortDirection; }
        else if (currentSort === 'platform') { return a.platform.localeCompare(b.platform) * sortDirection; }
        else { valA = a.combinedScore; valB = b.combinedScore; }
        
        return (valA < valB ? -1 : valA > valB ? 1 : 0) * sortDirection;
    });

    tbody.innerHTML = data.map((t, i) => `
        <tr onclick="viewTrader(${t.id})">
            <td>${i + 1}</td>
            <td><strong>${t.displayName}</strong>${t.isSuspectedBot ? ' 🤖' : ''}</td>
            <td>${t.platform}</td>
            <td class="num-col"><strong>${Number(t.combinedScore).toFixed(1)}</strong></td>
            <td class="num-col">${Number(t.copytradingQualityScore || 0).toFixed(1)}</td>
            <td class="num-col">${Number(t.copytradingCopyabilityScore || 0).toFixed(1)}</td>
            <td class="num-col">${fmt.pct(t.winRate)}</td>
            <td class="num-col">${fmt.pnl(t.totalPnl)}</td>
            <td>${t.trades30d} | ${t.totalTrades}</td>
            <td>
                ${t.strategy || '—'}
                ${t.traits ? '<div style="display:flex; flex-wrap:wrap; gap:4px; margin-top:4px;">' + t.traits.map(tr => {
                    const rawKey = tr.trait || tr;
                    const name = getTraitDisplayName(rawKey);
                    const desc = getTraitDescription(rawKey);
                    return `<span class="tier-badge ${getTraitClass(rawKey)}" title="${desc}">${name}</span>`;
                }).join('') + '</div>' : ''}
            </td>
        </tr>
    `).join('');
}

async function loadWatchlist() {
    const tbody = document.getElementById('watchlistBody');
    const data = await api(`/api/watchlist`);
    if (!Array.isArray(data)) {
        // api() returns null on HTTP errors — show it instead of a silently empty page.
        tbody.innerHTML = '<tr><td colspan="8">⚠ Failed to load watchlist (see browser console / server log).</td></tr>';
        return;
    }

    tbody.innerHTML = data.map(w => `
        <tr>
            <td class="trader-name">
                <div class="avatar">${(w.displayName || '?').substring(0, 2).toUpperCase()}</div>
                <div>
                    <strong>${w.displayName || '?'}</strong><br>
                    <span style="font-size:12px; color:var(--text-secondary)">${(w.platformUserId || '').substring(0, 8)}...</span>
                </div>
            </td>
            <td>${w.platform ?? 'Unknown'}</td>
            <td>${Number(w.copytradingScore || 0).toFixed(1)}</td>
            <td>${fmt.pct(w.winRate)}</td>
            <td class="${w.totalPnl >= 0 ? 'text-green' : 'text-red'}">${fmt.pnl(w.totalPnl)}</td>
            <td>${w.label || ''}</td>
            <td>${new Date(w.createdAt).toLocaleDateString()}</td>
            <td>
                <button class="btn-sm" onclick="viewTrader(${w.traderId})">View</button>
                <button class="btn-sm btn-outline-danger" onclick="removeFromWatchlist(${w.traderId})">Remove</button>
            </td>
        </tr>
    `).join('') || '<tr><td colspan="8">Your watchlist is empty.</td></tr>';
}

async function removeFromWatchlist(id) {
    if (confirm('Remove this trader from watchlist?')) {
        await api(`/api/traders/${id}/watchlist`, { method: 'DELETE' });
        loadWatchlist();
    }
}

// ─── Alerts Page ───
async function loadAlerts() {
    const data = await api('/api/alerts?count=50');
    const el = document.getElementById('alertsList');
    if (!data || !data.length) { el.innerHTML = '<div class="empty-state"><p>No alerts yet.</p></div>'; return; }
    el.innerHTML = data.map(a => `
        <div class="alert-item ${a.isRead ? '' : 'alert-unread'}">
            <div class="alert-icon alert-severity-${a.severity}">🔔</div>
            <div class="alert-content">
                <div class="alert-title">${a.title}</div>
                <div class="alert-message">${a.message}</div>
            </div>
            <div class="alert-time">${fmt.time(a.createdAt)}</div>
        </div>
    `).join('');
}

// ─── Markets Page ───
async function loadMarkets() {
    let url = '/api/markets?skip=0&take=100';
    if (currentPlatform !== 'All') url += `&platform=${currentPlatform}`;
    
    const catSelect = document.getElementById('marketsCategory');
    if (catSelect && catSelect.value !== 'All') {
        url += `&category=${catSelect.value}`;
    }
    
    const searchInput = document.getElementById('marketsSearchInput');
    if (searchInput && searchInput.value) {
        url += `&query=${encodeURIComponent(searchInput.value)}`;
    }
    
    let data = await api(url);
    const tbody = document.getElementById('allMarketsBody');
    if (!data || !data.length) { tbody.innerHTML = '<tr><td colspan="6"><div class="empty-state"><p>No markets found.</p></div></td></tr>'; return; }

    // Sorting
    if (currentSort === 'score') data.sort((a, b) => b.volume - a.volume);
    else if (currentSort === 'name') data.sort((a, b) => (a.endDate || '').localeCompare(b.endDate || ''));
    else if (currentSort === 'pnl') data.sort((a, b) => b.liquidity - a.liquidity);

    tbody.innerHTML = data.map(m => `
        <tr onclick="viewMarket(${m.id})" style="cursor:pointer">
            <td>${m.platform}</td>
            <td title="${m.question}"><strong>${m.question.length > 60 ? m.question.substring(0, 60) + '...' : m.question}</strong></td>
            <td>${fmt.usd(m.volume)}</td>
            <td>${fmt.usd(m.liquidity)}</td>
            <td>${m.endDate ? new Date(m.endDate).toLocaleDateString() : '—'}</td>
            <td>${m.isResolved ? '<span class="side-sell">Resolved</span>' : '<span class="side-buy">Active</span>'}</td>
        </tr>
    `).join('');
}

async function viewTrader(id) {
    window.currentTraderId = id;
    navigateTo('trader-detail');
    const t = await api(`/api/traders/${id}`);
    if (!t) return;

    // Reset tabs to default (Analytics)
    switchTraderTab('td-tab-analytics');

    document.getElementById('td-name').textContent = `Trader-Profil: ${t.displayName}`;
    document.getElementById('td-displayName').textContent = t.displayName;
    document.getElementById('td-platform').textContent = t.platform;
    document.getElementById('td-platformId').textContent = t.platformUserId;
    document.getElementById('td-tier').innerHTML = fmt.tier(t.tier);
    document.getElementById('td-strategy').textContent = t.strategy;

    const avatarEl = document.getElementById('td-avatar');
    if (avatarEl) {
        avatarEl.textContent = t.displayName ? t.displayName[0].toUpperCase() : 'P';
        avatarEl.style.background = getAvatarBg(id);
    }
    
    const traitsContainer = document.getElementById('td-traits-container');
    const traitsEl = document.getElementById('td-traits');
    if (t.traits && t.traits.length > 0) {
        traitsContainer.style.display = 'block';
        traitsEl.innerHTML = t.traits.map(tr => {
            const rawKey = tr.trait || tr;
            const name = getTraitDisplayName(rawKey);
            const desc = getTraitDescription(rawKey);
            const scoreVal = tr.value !== undefined ? ` (Value: ${Number(tr.value).toFixed(4)})` : '';
            return `<span class="tier-badge ${getTraitClass(rawKey)}" title="${desc}${scoreVal}">${name}</span>`;
        }).join('');
    } else {
        traitsContainer.style.display = 'none';
        traitsEl.innerHTML = '';
    }
    document.getElementById('td-winrate').innerHTML = fmt.pct(t.winRate);
    document.getElementById('td-winrate30d').innerHTML = fmt.pct(t.winRate30d);
    document.getElementById('td-pnl').innerHTML = fmt.pnl(t.totalPnl);
    document.getElementById('td-pnl30d').innerHTML = fmt.pnl(t.pnL30d);
    document.getElementById('td-trades').textContent = fmt.num(t.totalTrades);
    document.getElementById('td-bankroll').textContent = fmt.usd(t.estimatedBankroll);
    document.getElementById('td-score').textContent = Number(t.combinedScore).toFixed(1);
    document.getElementById('td-quality-score').textContent = Number(t.copytradingQualityScore || 0).toFixed(1);
    document.getElementById('td-copyability-score').textContent = Number(t.copytradingCopyabilityScore || 0).toFixed(1);
    
    const winRateDec = t.winRate / 100;
    const medianWin = t.medianWinReturnPct || 0;
    const medianLoss = t.medianLossReturnPct || 0;
    const expectancy = (winRateDec * medianWin) - ((1 - winRateDec) * Math.abs(medianLoss));
    
    document.getElementById('td-median-win-loss').innerHTML = `<span class="side-buy">+${Number(medianWin).toFixed(1)}%</span> / <span class="side-sell">${Number(medianLoss).toFixed(1)}%</span>`;
    document.getElementById('td-profit-factor').textContent = t.profitFactor ? Number(t.profitFactor).toFixed(2) : '—';
    document.getElementById('td-expectancy').innerHTML = expectancy > 0 ? `<span class="side-buy">+${expectancy.toFixed(1)}%</span>` : `<span class="side-sell">${expectancy.toFixed(1)}%</span>`;
    document.getElementById('td-ai-summary').textContent = t.aiStrategySummary || 'Bisher keine KI-Strategieanalyse durchgeführt.';
    
    const syncBtn = document.getElementById('btn-sync-trader');
    if (syncBtn) {
        syncBtn.onclick = () => queueHistorySync(id);
    }

    const deepSyncBtn = document.getElementById('btn-deep-resync-trader');
    if (deepSyncBtn) {
        deepSyncBtn.onclick = () => {
            if (confirm("Bist du sicher? Dies löscht alle komprimierten Trades, setzt die Positionen zurück und lädt den gesamten Verlauf neu.")) {
                queueDeepResync(id);
            }
        };
    }

    const analyzeBtn = document.getElementById('btn-analyze-trader');
    if (analyzeBtn) {
        analyzeBtn.onclick = () => queueTraderAnalysis(id);
    }

    const aiBtn = document.getElementById('btn-ai-analysis');
    if (window.capabilities && !window.capabilities.canControl) {
        aiBtn.style.display = 'none';
    } else {
        aiBtn.style.display = 'inline-block';
        aiBtn.onclick = () => triggerAiAnalysis(id, true);
    }

    const wlBtn = document.getElementById('btn-toggle-watchlist');
    wlBtn.textContent = t.isOnWatchlist ? 'Watchlist (Remove)' : 'Watchlist (Add)';
    wlBtn.onclick = async () => {
        const method = t.isOnWatchlist ? 'DELETE' : 'POST';
        await api(`/api/traders/${id}/watchlist`, { method });
        viewTrader(id); // Reload to update UI
    };

    const openBtn = document.getElementById('btn-open-platform');
    if (t.platform === 'Polymarket') {
        openBtn.style.display = 'inline-block';
        openBtn.textContent = 'Open on Polymarket';
        openBtn.onclick = () => window.open(`https://polymarket.com/profile/${t.platformUserId}`, '_blank');
    } else {
        openBtn.style.display = 'none';
    }

    const catBody = document.getElementById('td-categoryBody');
    if (t.categoryPerformances && t.categoryPerformances.length > 0) {
        catBody.innerHTML = t.categoryPerformances.map(c => `
            <tr>
                <td>${c.category}</td>
                <td>${c.subcategory || '-'}</td>
                <td>${fmt.pct(c.winRate)}</td>
                <td>${fmt.pnl(c.totalPnL)}</td>
                <td>${fmt.usd(c.totalVolume)}</td>
                <td class="${(c.avgReturnPct || 0) >= 0 ? 'pnl-positive' : 'pnl-negative'}">${(c.avgReturnPct || 0).toFixed(1)}%</td>
                <td>${fmt.num(c.totalTrades)}</td>
            </tr>
        `).join('');
    } else {
        catBody.innerHTML = '<tr><td colspan="7" style="text-align:center; padding:16px; color:var(--text-muted)">No category data available</td></tr>';
    }

    const tbody = document.getElementById('td-tradesBody');
    tbody.innerHTML = t.recentTrades.map(tr => `
        <tr>
            <td>${fmt.time(tr.executedAt)}</td>
              <td onclick="${tr.dbMarketId ? `viewMarket(${tr.dbMarketId})` : `''`}" style="cursor:${tr.dbMarketId ? 'pointer' : 'default'}; color:${tr.dbMarketId ? 'var(--primary)' : 'inherit'}" title="Market ID: ${tr.marketId}">
                  ${tr.marketName || (tr.marketId.length > 20 ? tr.marketId.substring(0,20)+'...' : tr.marketId)}
              </td>
            <td>${fmt.side(tr.side)}</td>
            <td>${Number(tr.price).toFixed(2)}</td>
            <td>${fmt.num(tr.size)}</td>
            <td>${fmt.usd(tr.amount)}</td>
        </tr>
    `).join('');

    loadTraderPositions(id);
}

async function loadTraderPositions(id) {
    const tbody = document.getElementById('td-positionsBody');
    tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;">Loading...</td></tr>';
    
    try {
        const data = await api(`/api/traders/${id}/positions`);
        if (!data || !data.length) {
            tbody.innerHTML = '<tr><td colspan="6"><div class="empty-state"><p>No active positions.</p></div></td></tr>';
            return;
        }

        tbody.innerHTML = data.map(p => `
            <tr>
                <td title="Market ID: ${p.marketId}">
                    <div style="font-weight:600">${p.marketName || p.marketId}</div>
                    <div style="font-size:0.85em; color:var(--text-muted)">${p.category || 'Other'} &middot; ${p.outcomeToken || 'Unknown'}</div>
                </td>
                <td>${fmt.num(p.sharesHeld)}</td>
                <td>${Number(p.avgCost).toFixed(2)}</td>
                <td>${Number(p.currentPrice).toFixed(2)}</td>
                <td>${fmt.pnl(p.realizedPnl)}</td>
                <td>${fmt.pnl(p.unrealizedPnl)}</td>
            </tr>
        `).join('');
    } catch (e) {
        tbody.innerHTML = '<tr><td colspan="6" style="text-align:center; color:var(--pnl-negative);">Error loading positions.</td></tr>';
    }
}

function switchTraderTab(tabId) {
    // Hide all tabs
    document.querySelectorAll('.tab-content').forEach(el => el.style.display = 'none');
    // Deactivate all buttons
    document.querySelectorAll('.btn-tab').forEach(el => el.classList.remove('active'));
    // Show active tab
    document.getElementById(tabId).style.display = 'block';
    // Activate clicked button
    const btn = document.querySelector(`.btn-tab[data-tab="${tabId}"]`);
    if(btn) btn.classList.add('active');
}

async function viewMarket(id) {
    navigateTo('market-detail');
    const m = await api(`/api/markets/${id}`);
    if (!m) return;

    document.getElementById('md-question').textContent = m.question;
    document.getElementById('md-platform').textContent = m.platform;
    document.getElementById('md-category').textContent = m.category;
    document.getElementById('md-ends').textContent = m.endDate ? new Date(m.endDate).toLocaleDateString() : 'Never';
    document.getElementById('md-volume').textContent = fmt.usd(m.volume);
    document.getElementById('md-liquidity').textContent = fmt.usd(m.liquidity);
    document.getElementById('md-status').textContent = m.isResolved ? 'Resolved' : 'Active';
    document.getElementById('md-bot-activity').textContent = m.botActivityScore ? Number(m.botActivityScore).toFixed(1) : '0';
    document.getElementById('md-unique-traders').textContent = fmt.num(m.uniqueTradersCount);
    document.getElementById('md-avg-trade-size').textContent = fmt.usd(m.averageTradeSize);

    const imgContainer = document.getElementById('md-image');
    if (m.imageUrl) imgContainer.innerHTML = `<img src="${m.imageUrl}" alt="Market" style="width:100%; border-radius:8px; margin-bottom:16px;">`;
    else imgContainer.innerHTML = '';

    const oList = document.getElementById('md-outcomes');
    oList.innerHTML = m.outcomes.map(o => `
        <div class="outcome-row">
            <span>${o.name}</span>
            <strong>${(o.price * 100).toFixed(1)}¢</strong>
            <div class="progress-bar"><div class="progress-fill" style="width:${o.price * 100}%"></div></div>
        </div>
    `).join('');

    const tbody = document.getElementById('md-tradesBody');
    tbody.innerHTML = m.recentTrades.map(tr => `
        <tr>
            <td>${fmt.time(tr.executedAt)}</td>
            <td onclick="viewTrader(${tr.traderId})" style="cursor:pointer; color:var(--primary)">${tr.traderName}</td>
            <td>${fmt.side(tr.side)}</td>
            <td>${Number(tr.price).toFixed(2)}</td>
            <td>${fmt.num(tr.size)}</td>
            <td>${fmt.usd(tr.amount)}</td>
        </tr>
    `).join('');
}

async function viewMarketByQuestion(question) {
    // Legacy helper: search markets by question text
    const data = await api(`/api/search?q=${encodeURIComponent(question)}`);
    if (data && data.markets.length > 0) {
        viewMarket(data.markets[0].id);
    }
}

function viewMarketById(dbMarketId) {
    if (dbMarketId) viewMarket(dbMarketId);
}

// ─── Initial Load ───
loadDashboard();

// Auto-refresh every 30 seconds
setInterval(() => {
    const activePage = document.querySelector('.page.active');
    if (activePage?.id === 'page-dashboard') loadDashboard();
}, 30000);

async function triggerAiAnalysis(id, manual) {
    const btn = document.getElementById('btn-ai-analysis');
    const oldText = btn.textContent;
    btn.textContent = 'Analyzing...';
    btn.disabled = true;
    try {
        const res = await api(`/api/traders/${id}/ai-analysis?manual=${manual}`, { method: 'POST' });
        if (res && res.summary) {
            document.getElementById('td-ai-summary').textContent = res.summary;
        } else {
            alert('Analysis failed or returned empty.');
        }
    } catch (e) {
        console.error(e);
        alert('Analysis error: ' + e);
    } finally {
        btn.textContent = oldText;
        btn.disabled = false;
    }
}

// ─── Jobs Page ───
async function loadJobs() {
    const data = await api('/api/jobs');
    const tbody = document.getElementById('jobsBody');
    if (!data || !data.length) {
        tbody.innerHTML = '<tr><td colspan="9"><div class="empty-state"><p>No jobs found.</p></div></td></tr>';
        return;
    }

    tbody.innerHTML = data.map(j => `
        <tr>
            <td>${j.id}</td>
            <td><strong>${j.jobType}</strong></td>
            <td>${j.status === 'Completed' ? '<span style="color:var(--pnl-positive)">Completed</span>' : j.status === 'Failed' ? '<span style="color:var(--pnl-negative)">Failed</span>' : j.status}</td>
            <td><a href="#" onclick="viewTrader(${j.traderId}); return false;" style="color:var(--primary);text-decoration:none;">${j.traderName || j.traderId}</a></td>
            <td>${fmt.time(j.createdAt)}</td>
            <td>${j.startedAt ? fmt.time(j.startedAt) : '—'}</td>
            <td>${j.completedAt ? fmt.time(j.completedAt) : '—'}</td>
            <td style="color:var(--pnl-negative); max-width:200px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${j.errorMessage || ''}">${j.errorMessage || '—'}</td>
            <td><button class="btn-sm" onclick="viewTrader(${j.traderId})">View Trader</button></td>
        </tr>
    `).join('');
}

async function queueHistorySync(id) {
    const res = await fetch(`/api/jobs/sync/${id}`, { method: 'POST' });
    if (res.ok) {
        alert('History Sync job queued successfully.');
    } else {
        alert('Failed to queue history sync.');
    }
}

async function queueDeepResync(id) {
    const res = await fetch(`/api/jobs/deep-resync/${id}`, { method: 'POST' });
    if (res.ok) {
        alert('Deep Resync job queued successfully.');
    } else {
        alert('Failed to queue deep resync.');
    }
}

async function queueTraderAnalysis(id) {
    const res = await fetch(`/api/jobs/analyze/${id}`, { method: 'POST' });
    if (res.ok) {
        alert('Trader Analysis job queued successfully.');
    } else {
        alert('Failed to queue trader analysis.');
    }
}

async function queueBacklogAnalysis() {
    const res = await fetch(`/api/jobs/analyze-backlog?take=50`, { method: 'POST' });
    if (res.ok) {
        const data = await res.json();
        alert(`Successfully queued ${data.queued} traders for analysis from backlog.`);
        loadJobs();
    } else {
        alert('Failed to queue backlog analysis.');
    }
}

// Initialize Dashboard
document.addEventListener('DOMContentLoaded', async () => {
    // Load capabilities first
    try {
        const caps = await api('/api/capabilities');
        if (caps) {
            window.capabilities = caps;
            if (!caps.canControl) {
                // Hide control-mode guarded elements
                const jobsNav = document.getElementById('nav-jobs-container');
                if (jobsNav) jobsNav.style.display = 'none';
                
                const addTraderPanel = document.getElementById('control-add-trader-panel');
                if (addTraderPanel) addTraderPanel.style.display = 'none';

                const traderActions = document.getElementById('control-trader-actions');
                if (traderActions) traderActions.style.display = 'none';
            }
        }
    } catch (e) {
        console.error('Failed to load capabilities', e);
    }

    // Load traits for filter
    try {
        const traits = await api('/api/traders/traits');
        if (traits && Array.isArray(traits)) {
            const filterTrait = document.getElementById('filterTrait');
            if (filterTrait) {
                traits.forEach(t => {
                    const opt = document.createElement('option');
                    opt.value = t;
                    opt.textContent = getTraitDisplayName(t);
                    filterTrait.appendChild(opt);
                });
            }
        }
    } catch (e) {
        console.error('Failed to load traits', e);
    }
    loadDashboard();
});
