{% extends "base.html" %}
{% block page_title %}Flottes JA4×ASN — §5.2{% endblock %}
{% block content %}
<div class="p-4 lg:p-6 space-y-4 max-w-[1920px] mx-auto">

    <!-- ═══ Header KPIs ═══ -->
    <div class="flex flex-wrap items-center gap-4 mb-2">
        <h1 class="text-xl font-bold text-white flex items-center gap-2">
            <svg class="w-6 h-6 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
            Flottes JA4×ASN
        </h1>
        <div class="ml-auto flex items-center gap-3">
            <div class="text-center px-3">
                <div class="text-2xl font-bold text-cyan-400" id="kpi-total">—</div>
                <div class="text-[10px] text-gray-500 uppercase tracking-wider">Flottes</div>
            </div>
            <div class="text-center px-3 border-l border-gray-700">
                <div class="text-2xl font-bold text-red-400" id="kpi-ips">—</div>
                <div class="text-[10px] text-gray-500 uppercase tracking-wider">IPs impliquées</div>
            </div>
            <div class="text-center px-3 border-l border-gray-700">
                <div class="text-2xl font-bold text-amber-400" id="kpi-maxscore">—</div>
                <div class="text-[10px] text-gray-500 uppercase tracking-wider">Score max</div>
            </div>
        </div>
    </div>

    <!-- ═══ Doc banner ═══ -->
    <div class="bg-gray-900/50 border border-gray-800 rounded-lg px-4 py-3 text-xs text-gray-400 leading-relaxed">
        <strong class="text-cyan-300">Détection de flottes §5.2</strong> — Analyse du graphe bipartite
        <strong>G = (JA4 ∪ ASN, E)</strong> pour identifier les flottes de bots coordonnées qui
        font tourner leurs fingerprints JA4 et ASN. Les communautés suspectes sont détectées via
        l'algorithme Louvain (ou composantes connexes en fallback).
        <br><strong>fleet_score = taille × densité / log₂(n_asn + 2).</strong>
        Un score ≥ 2.0 indique une communauté coordonnée. Chaque ligne représente une communauté
        avec les fingerprints JA4 et ASNs impliqués.
        <br><strong>Action SOC :</strong> Bloquer les ASNs ou les JA4 d'une flotte peut neutraliser
        des milliers de bots en une seule règle.
    </div>

    <!-- ═══ Table des flottes détectées ═══ -->
    <div class="section-card overflow-hidden">
        <div class="section-header">
            <span class="section-title">
                <svg class="w-4 h-4 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064"/></svg>
                Communautés suspectes (7 jours)
                <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                    <h4>Flottes JA4×ASN</h4>
                    <p>Chaque ligne = une communauté du graphe bipartite. Les flottes avec un
                    <strong>fleet_score</strong> élevé sont les plus coordonnées.</p>
                    <p><strong>ja4_set :</strong> Fingerprints TLS utilisés par la flotte.</p>
                    <p><strong>asn_set :</strong> Réseaux (ASN) de la flotte.</p>
                    <p><strong>n_ips :</strong> Nombre d'IPs distinctes dans la communauté.</p>
                    <p class="doc-source">Source : fleet_detections (7j)</p>
                </div></span>
            </span>
            <span id="load-status" class="text-[10px] text-gray-500">Chargement…</span>
        </div>
        <div class="overflow-x-auto">
            <table class="data-table">
                <thead>
                    <tr>
                        <th>Détecté le</th>
                        <th>Comm.</th>
                        <th>Score</th>
                        <th>IPs</th>
                        <th>JA4 impliqués</th>
                        <th>ASNs impliqués</th>
                        <th>Échantillon IPs</th>
                    </tr>
                </thead>
                <tbody id="fleet-body">
                    <tr><td colspan="7" class="text-center text-gray-500 py-8">Chargement…</td></tr>
                </tbody>
            </table>
        </div>
    </div>

    <!-- ═══ Graphique score distribution ═══ -->
    <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
        <div class="section-card">
            <div class="section-header">
                <span class="section-title">
                    <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/></svg>
                    Distribution des fleet_scores
                </span>
            </div>
            <div class="section-body">
                <div id="score-chart" 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"><circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="12" cy="7" r="1"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l3-5 3 5"/></svg>
                    IPs par flotte
                </span>
            </div>
            <div class="section-body">
                <div id="ips-chart" style="height:260px"></div>
            </div>
        </div>
    </div>
</div>

<script>
/* ════════════════════════════════════════════════════════════════════════════
 * Page Flottes JA4×ASN — chargement et rendu
 * ════════════════════════════════════════════════════════════════════════════ */

function fmtTs(ts) {
    if (!ts) return '—';
    return new Date(ts).toLocaleString('fr-FR', {dateStyle:'short', timeStyle:'short'});
}

function scoreColor(score) {
    if (score >= 5) return 'badge-critical';
    if (score >= 3) return 'badge-high';
    return 'badge-medium';
}

function renderTags(arr, maxShow = 3) {
    if (!arr || arr.length === 0) return '<span class="text-gray-600">—</span>';
    const shown = arr.slice(0, maxShow);
    const rest  = arr.length - shown.length;
    const tags  = shown.map(v =>
        `<span class="inline-block bg-gray-800 text-gray-300 text-[10px] px-1.5 py-0.5 rounded mr-1 mb-1 font-mono">${v}</span>`
    ).join('');
    const more  = rest > 0 ? `<span class="text-gray-500 text-[10px]">+${rest}</span>` : '';
    return tags + more;
}

async function loadFleet() {
    try {
        const res  = await fetch('/api/fleet');
        const data = await res.json();
        const fleets = data.fleets || [];

        // KPIs
        const totalIps = fleets.reduce((s, f) => s + (f.n_ips || 0), 0);
        const maxScore  = fleets.length ? Math.max(...fleets.map(f => f.fleet_score || 0)) : 0;
        document.getElementById('kpi-total').textContent    = fleets.length;
        document.getElementById('kpi-ips').textContent      = totalIps.toLocaleString('fr-FR');
        document.getElementById('kpi-maxscore').textContent = maxScore.toFixed(2);
        document.getElementById('load-status').textContent  =
            fleets.length ? `${fleets.length} communauté(s)` : 'Aucune flotte détectée';

        // Table
        const tbody = document.getElementById('fleet-body');
        if (fleets.length === 0) {
            tbody.innerHTML = '<tr><td colspan="7" class="text-center text-gray-500 py-8">Aucune flotte détectée sur les 7 derniers jours</td></tr>';
        } else {
            tbody.innerHTML = fleets.map(f => `
                <tr>
                    <td class="text-gray-400 text-xs">${fmtTs(f.detected_at)}</td>
                    <td class="font-mono text-xs text-gray-300">#${f.community_id}</td>
                    <td><span class="badge ${scoreColor(f.fleet_score)}">${(f.fleet_score || 0).toFixed(3)}</span></td>
                    <td class="font-bold text-white">${(f.n_ips || 0).toLocaleString('fr-FR')}</td>
                    <td class="max-w-xs">${renderTags(f.ja4_set, 2)}</td>
                    <td class="max-w-xs">${renderTags(f.asn_set, 3)}</td>
                    <td class="max-w-xs">${renderTags(f.ip_sample, 3)}</td>
                </tr>
            `).join('');
        }

        // Graphique distribution des scores
        renderScoreChart(fleets);
        renderIpsChart(fleets);

    } catch (err) {
        console.error('Erreur chargement flottes :', err);
        document.getElementById('load-status').textContent = 'Erreur de chargement';
        document.getElementById('fleet-body').innerHTML =
            '<tr><td colspan="7" class="text-center text-red-500 py-8">Erreur de chargement</td></tr>';
    }
}

function renderScoreChart(fleets) {
    const el = document.getElementById('score-chart');
    if (!el || !fleets.length) return;
    const chart = echarts.init(el, 'dark');
    // Histogramme des fleet_scores
    const scores = fleets.map(f => parseFloat((f.fleet_score || 0).toFixed(2)));
    chart.setOption({
        backgroundColor: 'transparent',
        tooltip: { trigger: 'axis' },
        xAxis: { type:'category', data: fleets.map((f,i) => `#${f.community_id}`),
                 axisLabel:{color:'#6b7280', fontSize:10, rotate:45} },
        yAxis: { type:'value', name:'Fleet Score', nameTextStyle:{color:'#6b7280',fontSize:10},
                 axisLabel:{color:'#6b7280', fontSize:10} },
        series: [{
            type: 'bar',
            data: scores.map(s => ({
                value: s,
                itemStyle: { color: s >= 5 ? '#ef4444' : s >= 3 ? '#f97316' : '#eab308' }
            })),
            barMaxWidth: 40,
        }],
    });
    window.addEventListener('resize', () => chart.resize());
}

function renderIpsChart(fleets) {
    const el = document.getElementById('ips-chart');
    if (!el || !fleets.length) return;
    const chart = echarts.init(el, 'dark');
    chart.setOption({
        backgroundColor: 'transparent',
        tooltip: { trigger: 'item', formatter: '{b}: {c} IPs' },
        series: [{
            type: 'pie',
            radius: ['40%', '70%'],
            data: fleets.slice(0, 15).map(f => ({
                name: `#${f.community_id}`,
                value: f.n_ips || 0,
            })),
            label: { color: '#9ca3af', fontSize: 10 },
            emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.5)' } },
        }],
    });
    window.addEventListener('resize', () => chart.resize());
}

loadFleet();
</script>
{% endblock %}
