{% extends "base.html" %}
{% block title %}JA4 SOC — Overview{% endblock %}
{% block page_title %}
    Centre de commande
    <span class="relative inline-block ml-1"><button onclick="docToggle(this)" class="doc-btn" aria-label="Aide">ⓘ</button><div class="doc-panel">
        <h4>Centre de commande SOC</h4>
        <p>Vue d'ensemble temps réel de la posture de sécurité. Les KPI montrent les dernières 24h. La timeline et les alertes se rafraîchissent toutes les 60s.</p>
        <p><strong>Workflow :</strong> Identifiez les pics → cliquez sur une alerte → investiguez l'IP → classifiez.</p>
        <p class="doc-source">Sources : ml_detected_anomalies, ml_all_scores, http_logs</p>
    </div></span>
{% endblock %}
{% block content %}
<div class="space-y-4">
    <!-- ═══ KPI Row ═══ -->
    <div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3" id="kpi-grid">
        <div class="kpi-card">
            <div class="flex items-center gap-2 mb-1"><span class="w-2 h-2 rounded-full bg-red-500"></span><span class="text-[11px] text-gray-500">Détections 24h</span></div>
            <div class="text-2xl font-bold text-red-400" id="kpi-detections">—</div>
            <div class="text-[10px] text-gray-600 mt-1" id="kpi-det-trend"></div>
        </div>
        <div class="kpi-card">
            <div class="flex items-center gap-2 mb-1"><span class="w-2 h-2 rounded-full bg-orange-500"></span><span class="text-[11px] text-gray-500">Critical + High</span></div>
            <div class="text-2xl font-bold text-orange-400" id="kpi-critical">—</div>
        </div>
        <div class="kpi-card">
            <div class="flex items-center gap-2 mb-1"><span class="w-2 h-2 rounded-full bg-indigo-400"></span><span class="text-[11px] text-gray-500">Sessions ML</span></div>
            <div class="text-2xl font-bold text-brand-500" id="kpi-scored">—</div>
        </div>
        <div class="kpi-card">
            <div class="flex items-center gap-2 mb-1"><span class="w-2 h-2 rounded-full bg-yellow-400"></span><span class="text-[11px] text-gray-500">IPs uniques</span></div>
            <div class="text-2xl font-bold text-yellow-400" id="kpi-ips">—</div>
        </div>
        <div class="kpi-card">
            <div class="flex items-center gap-2 mb-1"><span class="w-2 h-2 rounded-full bg-green-400"></span><span class="text-[11px] text-gray-500">Navigateurs légit.</span></div>
            <div class="text-2xl font-bold text-green-400" id="kpi-browsers">—</div>
        </div>
        <div class="kpi-card">
            <div class="flex items-center gap-2 mb-1"><span class="w-2 h-2 rounded-full bg-blue-400"></span><span class="text-[11px] text-gray-500">Bots connus</span></div>
            <div class="text-2xl font-bold text-blue-400" id="kpi-knownbots">—</div>
        </div>
    </div>

    <!-- ═══ Row 2: Timeline + Live alerts ═══ -->
    <div class="grid grid-cols-1 xl:grid-cols-3 gap-4">
        <!-- Timeline (2/3) -->
        <div class="xl:col-span-2 section-card">
            <div class="section-header">
                <span class="section-title">
                    <svg class="w-4 h-4 text-brand-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"/></svg>
                    Timeline détections (24h)
                    <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                        <h4>Timeline des détections</h4>
                        <p>Courbes empilées par niveau de menace. Un pic soudain indique une attaque en cours ou un nouveau pattern détecté.</p>
                        <p><strong>Action :</strong> Cliquez sur un pic pour filtrer les détections de cette heure.</p>
                        <p class="doc-source">Source : ml_detected_anomalies GROUP BY hour, threat_level</p>
                    </div></span>
                </span>
            </div>
            <div class="section-body"><div id="chart-timeline" style="height:280px"></div></div>
        </div>
        <!-- Live alerts (1/3) -->
        <div class="section-card flex flex-col">
            <div class="section-header">
                <span class="section-title">
                    <svg class="w-4 h-4 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/></svg>
                    Alertes récentes
                    <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                        <h4>Flux d'alertes temps réel</h4>
                        <p>Dernières détections HIGH/CRITICAL/KNOWN_BOT. Cliquez sur une IP pour démarrer l'investigation.</p>
                        <p class="doc-source">Source : ml_detected_anomalies ORDER BY detected_at DESC</p>
                    </div></span>
                </span>
            </div>
            <div class="flex-1 overflow-y-auto" style="max-height:280px" id="alerts-feed">
                <div class="px-4 py-8 text-center text-gray-500 text-xs">Chargement…</div>
            </div>
        </div>
    </div>

    <!-- ═══ Row 3: Threat pie + Browsers + Top IPs ═══ -->
    <div class="grid grid-cols-1 lg:grid-cols-4 gap-4">
        <div class="section-card">
            <div class="section-header">
                <span class="section-title">Threat Levels
                    <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                        <h4>Distribution des menaces</h4>
                        <p>Répartition des sessions par niveau : CRITICAL (score &gt;0.70), HIGH (&gt;0.40), MEDIUM (&gt;0.10), NORMAL, LEGITIMATE_BROWSER, KNOWN_BOT.</p>
                        <p><strong>Action :</strong> Cliquez sur un segment pour filtrer les détections.</p>
                        <p class="doc-source">Source : ml_all_scores.threat_level</p>
                    </div></span>
                </span>
            </div>
            <div class="section-body"><div id="chart-threats" style="height:220px"></div></div>
        </div>
        <div class="section-card">
            <div class="section-header">
                <span class="section-title">Navigateurs
                    <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                        <h4>Familles de navigateurs</h4>
                        <p>Identification via dictionnaire JA4 → browser_family. Les navigateurs légitimes sont exemptés du scoring ML.</p>
                        <p class="doc-source">Source : ml_all_scores.browser_family</p>
                    </div></span>
                </span>
            </div>
            <div class="section-body"><div id="chart-browsers" style="height:220px"></div></div>
        </div>
        <div class="lg:col-span-2 section-card">
            <div class="section-header">
                <span class="section-title">Top 10 IPs suspectes
                    <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                        <h4>IPs les plus détectées</h4>
                        <p>IPs avec le plus grand nombre de détections sur 24h. Le score indique le pire score observé.</p>
                        <p><strong>Action :</strong> Cliquez sur une IP pour l'investiguer en profondeur.</p>
                        <p class="doc-source">Source : ml_detected_anomalies GROUP BY src_ip</p>
                    </div></span>
                </span>
            </div>
            <div class="section-body p-0">
                <div class="overflow-x-auto">
                    <table class="data-table">
                        <thead><tr><th>IP</th><th>Dét.</th><th>Score</th><th>Threat</th><th>ASN</th><th>Pays</th></tr></thead>
                        <tbody id="top-ips-body"></tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <!-- ═══ Row 4: ASN + Geo + Campaigns ═══ -->
    <div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
        <div class="section-card">
            <div class="section-header">
                <span class="section-title">Top ASN
                    <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                        <h4>Systèmes autonomes</h4>
                        <p>ASN les plus représentés. Couleur : <span class="text-green-400">ISP</span> (résidentiel), <span class="text-red-400">Datacenter</span>, <span class="text-orange-400">Hosting</span>.</p>
                        <p><strong>Action :</strong> Cliquez pour voir les détails réseau.</p>
                        <p class="doc-source">Source : view_ai_features_1h.asn_org</p>
                    </div></span>
                </span>
            </div>
            <div class="section-body"><div id="chart-asn" style="height:260px"></div></div>
        </div>
        <div class="section-card">
            <div class="section-header">
                <span class="section-title">Géographie
                    <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                        <h4>Répartition géographique</h4>
                        <p>Treemap par pays et type d'ASN. La taille indique le nombre de sessions.</p>
                        <p class="doc-source">Source : view_ai_features_1h.country_code</p>
                    </div></span>
                </span>
            </div>
            <div class="section-body"><div id="chart-geo" style="height:260px"></div></div>
        </div>
        <div class="section-card">
            <div class="section-header">
                <span class="section-title">
                    <svg class="w-4 h-4 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
                    Campagnes bots
                    <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                        <h4>Campagnes détectées (HDBSCAN)</h4>
                        <p>Groupes d'IPs présentant des patterns comportementaux similaires, identifiés par clustering HDBSCAN sur les features ML.</p>
                        <p><strong>Action :</strong> Une campagne multi-IP indique une attaque coordonnée (botnet, scraping distribué).</p>
                        <p class="doc-source">Source : ml_detected_anomalies.campaign_id</p>
                    </div></span>
                </span>
            </div>
            <div class="section-body p-0">
                <div class="overflow-y-auto" style="max-height:260px" id="campaigns-list">
                    <div class="px-4 py-8 text-center text-gray-500 text-xs">Chargement…</div>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}
{% block scripts %}
<script>
const THREAT_COLORS = {CRITICAL:'#ef4444',HIGH:'#f97316',MEDIUM:'#eab308',LOW:'#22c55e',NORMAL:'#6b7280',KNOWN_BOT:'#3b82f6',ANUBIS_DENY:'#dc2626',LEGITIMATE_BROWSER:'#22c55e'};
const LABEL_COLORS = {isp:'#22c55e',datacenter:'#ef4444',hosting:'#f97316',cdn:'#06b6d4',unknown:'#6b7280'};

let charts = {};
function initChart(id) {
    const el = document.getElementById(id);
    if (!el) return null;
    if (charts[id]) charts[id].dispose();
    charts[id] = echarts.init(el);
    return charts[id];
}

async function loadOverview() {
    try {
        const [ov, geo, fp, alerts, tl, campaigns] = await Promise.all([
            fetch('/api/overview').then(r=>r.json()),
            fetch('/api/geo').then(r=>r.json()),
            fetch('/api/fingerprints').then(r=>r.json()),
            fetch('/api/alerts?limit=20').then(r=>r.json()),
            fetch('/api/timeline-detail').then(r=>r.json()),
            fetch('/api/campaigns').then(r=>r.json()),
        ]);

        // ── KPIs ──
        const threatMap = {};
        (ov.threat_distribution||[]).forEach(t => { threatMap[t.threat_level] = t.cnt; });

        document.getElementById('kpi-detections').textContent = fmtNum(ov.detections_24h);
        document.getElementById('kpi-critical').textContent = fmtNum((threatMap.CRITICAL||0)+(threatMap.HIGH||0));
        document.getElementById('kpi-scored').textContent = fmtNum(ov.scored_24h);
        document.getElementById('kpi-ips').textContent = fmtNum(ov.unique_ips);
        document.getElementById('kpi-browsers').textContent = fmtNum(ov.legitimate_browsers);
        document.getElementById('kpi-knownbots').textContent = fmtNum(threatMap.KNOWN_BOT||0);

        // ── Stacked timeline ──
        const timeline = tl.timeline || [];
        if (timeline.length) {
            const ch = initChart('chart-timeline');
            const levels = ['CRITICAL','HIGH','MEDIUM','KNOWN_BOT'];
            const hours = timeline.map(t => t.hour?.substring(11,16)||'');
            ch.setOption(ecBase({
                tooltip: ecTooltip({trigger:'axis'}),
                legend: {data:levels, textStyle:{color:EC_TEXT,fontSize:10}, top:0, right:0, itemWidth:12, itemHeight:8},
                grid: ecGrid({top:30}),
                xAxis: {type:'category', data:hours, axisLine:{lineStyle:{color:EC_GRID}}, axisLabel:{color:EC_TEXT,fontSize:10}},
                yAxis: {type:'value', splitLine:{lineStyle:{color:EC_GRID,type:'dashed'}}, axisLabel:{color:EC_TEXT}},
                series: levels.map(lvl => ({
                    name:lvl, type:'bar', stack:'total',
                    data: timeline.map(t => t[lvl]||0),
                    itemStyle:{color:THREAT_COLORS[lvl]},
                    emphasis:{focus:'series'},
                }))
            }));
        }

        // ── Live alerts feed ──
        const feed = document.getElementById('alerts-feed');
        const alertRows = alerts.alerts || [];
        if (alertRows.length) {
            feed.innerHTML = alertRows.map(a => {
                const ip = String(a.src_ip||'').replace('::ffff:','');
                const color = a.threat_level === 'CRITICAL' ? 'border-l-red-500' :
                              a.threat_level === 'HIGH' ? 'border-l-orange-500' :
                              a.threat_level === 'KNOWN_BOT' ? 'border-l-blue-500' : 'border-l-gray-700';
                return `<a href="/ip/${encodeURIComponent(ip)}" class="block px-4 py-2.5 border-b border-l-2 ${color} border-gray-800/50 hover:bg-gray-800/40 transition-colors">
                    <div class="flex items-center gap-2">
                        ${threatBadge(a.threat_level)}
                        <span class="font-mono text-xs text-brand-400">${escapeHtml(ip)}</span>
                        <span class="text-[10px] text-gray-500 ml-auto">${fmtAgo(a.detected_at)}</span>
                    </div>
                    <div class="text-[11px] text-gray-400 mt-0.5 truncate">
                        ${a.bot_name ? '<span class="text-cyan-400">'+escapeHtml(a.bot_name)+'</span> · ' : ''}
                        ${escapeHtml(a.host||'')} · ${a.hits||0} hits · score ${parseFloat(a.anomaly_score||0).toFixed(3)}
                    </div>
                </a>`;
            }).join('');
        } else {
            feed.innerHTML = '<div class="px-4 py-8 text-center text-gray-500 text-xs">Aucune alerte récente</div>';
        }

        // ── Threats donut ──
        if (ov.threat_distribution?.length) {
            const ch = initChart('chart-threats');
            ch.setOption(ecBase({
                tooltip: ecTooltip({trigger:'item', formatter:'{b}: {c} ({d}%)'}),
                series: [{
                    type:'pie', radius:['50%','80%'], center:['50%','55%'],
                    label:{show:false},
                    data: ov.threat_distribution.map(t=>({
                        name:t.threat_level, value:t.cnt,
                        itemStyle:{color:THREAT_COLORS[t.threat_level]||'#6b7280'}
                    })),
                    emphasis:{label:{show:true,color:'#fff',fontSize:12,fontWeight:'bold'}},
                }]
            }));
            ch.on('click', p => { if(p.name) window.location.href='/detections?threat_level='+encodeURIComponent(p.name); });
        }

        // ── Browser donut ──
        if (ov.browser_stats?.length) {
            const ch = initChart('chart-browsers');
            ch.setOption(ecBase({
                tooltip: ecTooltip({trigger:'item', formatter:'{b}: {c} ({d}%)'}),
                series: [{
                    type:'pie', radius:['50%','80%'], center:['50%','55%'],
                    label:{show:false},
                    data: ov.browser_stats.map((b,i)=>({
                        name:b.browser_family, value:b.cnt,
                        itemStyle:{color:EC_COLORS[i%EC_COLORS.length]}
                    })),
                    emphasis:{label:{show:true,color:'#fff',fontSize:11}},
                }]
            }));
        }

        // ── ASN horizontal bar ──
        if (geo.asns?.length) {
            const top = geo.asns.slice(0,10);
            const ch = initChart('chart-asn');
            ch.setOption(ecBase({
                tooltip: ecTooltip({trigger:'axis',axisPointer:{type:'shadow'}}),
                grid: {left:130,right:30,top:5,bottom:5},
                yAxis: {type:'category', data:top.map(a=>a.asn_org).reverse(), axisLine:{show:false}, axisLabel:{color:EC_TEXT,fontSize:10,width:120,overflow:'truncate'}},
                xAxis: {type:'value', show:false},
                series: [{
                    type:'bar', data:top.map(a=>({value:a.sessions,itemStyle:{color:LABEL_COLORS[a.asn_label]||'#6b7280'}})).reverse(),
                    barWidth:'60%', label:{show:true,position:'right',color:EC_TEXT,fontSize:10},
                }]
            }));
            ch.on('click', p => { if(p.name) window.location.href='/network?asn_org='+encodeURIComponent(p.name); });
        }

        // ── Country treemap ──
        if (geo.countries?.length) {
            const byCountry = {};
            geo.countries.forEach(c => {
                if (!byCountry[c.country_code]) byCountry[c.country_code] = {name:c.country_code, value:0, children:[]};
                byCountry[c.country_code].value += c.sessions;
                byCountry[c.country_code].children.push({name:c.asn_label, value:c.sessions});
            });
            const ch = initChart('chart-geo');
            ch.setOption(ecBase({
                tooltip: ecTooltip({formatter:i=>`${i.name}: ${(i.value||0).toLocaleString()} sessions`}),
                series:[{type:'treemap', data:Object.values(byCountry).sort((a,b)=>b.value-a.value).slice(0,20),
                    width:'100%',height:'100%',
                    label:{show:true,fontSize:12,color:'#fff',fontWeight:'bold'},
                    itemStyle:{borderColor:'#111827',borderWidth:2,gapWidth:1},
                    levels:[{itemStyle:{borderColor:'#1f2937',borderWidth:3}},{colorSaturation:[0.3,0.7]}],
                    color:EC_COLORS,
                }]
            }));
            ch.on('click', p => { if(p.data?.name?.length<=3) window.location.href='/detections?country_code='+encodeURIComponent(p.data.name); });
        }

        // ── Top IPs table ──
        document.getElementById('top-ips-body').innerHTML = (ov.top_ips||[]).map(ip => `<tr onclick="window.location='/ip/${encodeURIComponent(String(ip.src_ip).replace('::ffff:',''))}'">
            <td>${fmtIP(ip.src_ip)}</td><td class="font-mono">${ip.cnt}</td><td>${fmtScore(ip.worst_score)}</td>
            <td>${threatBadge(ip.threat_level)}</td><td class="text-xs">${fmtASN(ip.asn_org)}</td><td>${fmtCountry(ip.country_code)}</td>
        </tr>`).join('') || '<tr><td colspan="6" class="text-center py-4 text-gray-500">Aucune donnée</td></tr>';

        // ── Campaigns list ──
        const campEl = document.getElementById('campaigns-list');
        const camps = campaigns.campaigns || [];
        if (camps.length) {
            campEl.innerHTML = camps.map(c => `<div class="px-4 py-2.5 border-b border-gray-800/50 hover:bg-gray-800/40 transition-colors cursor-pointer" onclick="window.location='/campaigns'">
                <div class="flex items-center gap-2 mb-1">
                    <span class="text-xs font-mono text-purple-400">Camp. #${escapeHtml(String(c.campaign_id).substring(0,8))}</span>
                    <span class="badge badge-high">${c.members} membres</span>
                    <span class="text-[10px] text-gray-500 ml-auto">${c.unique_ips} IPs</span>
                </div>
                <div class="text-[11px] text-gray-400">
                    Score max: ${fmtScore(c.max_score)} · ASN: ${(c.asn_list||[]).map(a=>escapeHtml(a)).join(', ')||'—'}
                </div>
            </div>`).join('');
        } else {
            campEl.innerHTML = '<div class="px-4 py-8 text-center text-gray-500 text-xs">Aucune campagne active</div>';
        }

    } catch(e) { console.error('Overview load error:', e); }
}
loadOverview();
setInterval(loadOverview, 60000);
</script>
{% endblock %}
