{% extends "base.html" %}
{% block title %}JA4 SOC — IP {{ ip }}{% endblock %}
{% block page_title %}
    <a href="/detections" class="text-gray-500 hover:text-gray-300 text-xs">&larr; Retour</a>
    <span class="mx-2 text-gray-700">/</span>
    Investigation IP : <span class="text-brand-500 font-mono">{{ ip }}</span>
{% endblock %}
{% block header_actions %}
    <div class="flex gap-2">
        <select id="cls-select" class="px-2 py-1 bg-gray-900 border border-gray-800 rounded text-xs text-gray-300">
            <option value="bot">🤖 Bot</option><option value="legitimate">✅ Légitime</option><option value="suspicious">⚠️ Suspect</option>
        </select>
        <input type="text" id="cls-comment" placeholder="Commentaire…" class="px-2 py-1 bg-gray-900 border border-gray-800 rounded text-xs text-gray-300 w-40 focus:border-brand-500 focus:outline-none">
        <button id="cls-btn" class="px-3 py-1 bg-brand-600 text-white rounded text-xs font-medium hover:bg-brand-500">Classifier</button>
        <span id="cls-result" class="text-xs self-center"></span>
    </div>
{% endblock %}
{% block content %}
<div class="space-y-4">
    <!-- KPIs -->
    <div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3">
        <div class="kpi-card"><div class="text-[11px] text-gray-500 mb-1">Détections</div><div class="text-xl font-bold text-red-400" id="ip-det-count">—</div></div>
        <div class="kpi-card"><div class="text-[11px] text-gray-500 mb-1">Pire score</div><div class="text-xl font-bold" id="ip-worst-score">—</div></div>
        <div class="kpi-card"><div class="text-[11px] text-gray-500 mb-1">Récurrence</div><div class="text-xl font-bold text-yellow-400" id="ip-recurrence">—</div></div>
        <div class="kpi-card"><div class="text-[11px] text-gray-500 mb-1">Requêtes HTTP</div><div class="text-xl font-bold text-gray-200" id="ip-http-count">—</div></div>
        <div class="kpi-card"><div class="text-[11px] text-gray-500 mb-1">Scores ML</div><div class="text-xl font-bold text-brand-500" id="ip-score-count">—</div></div>
        <div class="kpi-card"><div class="text-[11px] text-gray-500 mb-1">ASN / Pays</div><div class="text-sm font-medium" id="ip-asn-info">—</div></div>
    </div>

    <!-- Radar + Score timeline -->
    <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
        <div class="section-card">
            <div class="section-header"><span class="section-title">Profil comportemental
                <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                    <h4>Radar comportemental</h4>
                    <p>Compare cette IP aux profils moyens ISP (vert) et datacenter/bot (rouge). Les axes sont normalisés 0→1.</p>
                    <p><strong>Interprétation :</strong> Un profil proche du rouge indique un comportement bot. hit_velocity élevé + fuzzing élevé = scraping agressif.</p>
                    <p class="doc-source">Source : view_ai_features_1h</p>
                </div></span>
            </span></div>
            <div class="section-body"><div id="radar-chart" style="height:300px"></div></div>
        </div>
        <div class="section-card">
            <div class="section-header"><span class="section-title">Scores ML dans le temps
                <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                    <h4>Historique des scores</h4>
                    <p>Évolution du score ML normalisé sur les derniers cycles. Un score stable élevé = bot persistant. Un pic soudain = changement de comportement.</p>
                    <p class="doc-source">Source : ml_all_scores</p>
                </div></span>
            </span></div>
            <div class="section-body"><div id="score-chart" style="height:300px"></div></div>
        </div>
    </div>

    <!-- Detections -->
    <div class="section-card overflow-hidden">
        <div class="section-header"><span class="section-title">Détections
            <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                <h4>Historique des détections</h4>
                <p>Chaque ligne = une session classée comme anormale. Le score combiné utilise l'ensemble triple-voix (EIF + AE + XGBoost).</p>
                <p class="doc-source">Source : ml_detected_anomalies</p>
            </div></span>
        </span></div>
        <div class="overflow-x-auto" style="max-height:35vh; overflow-y:auto">
            <table class="data-table"><thead><tr>
                <th>Date</th><th>Score</th><th>Raw</th><th>AE</th><th>XGB</th><th>Threat</th><th>JA4</th><th>Host</th><th>Hits</th><th>Campagne</th>
            </tr></thead><tbody id="det-body"></tbody></table>
        </div>
    </div>

    <!-- AI Features grid -->
    <div class="section-card overflow-hidden" id="features-section" style="display:none">
        <div class="section-header"><span class="section-title">Features AI (dernière fenêtre)
            <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                <h4>Features ML détaillées</h4>
                <p>72 features extraites : velocity, fuzzing, entropie, ratios, métriques TLS, etc. Valeurs élevées en rouge indiquent un comportement suspect.</p>
                <p class="doc-source">Source : view_ai_features_1h</p>
            </div></span>
        </span></div>
        <div class="p-4 grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-2 text-sm" id="features-grid"></div>
    </div>

    <!-- HTTP Logs -->
    <div class="section-card overflow-hidden">
        <div class="section-header"><span class="section-title">Requêtes HTTP récentes (100 max)
            <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                <h4>Logs HTTP bruts</h4>
                <p>Dernières requêtes de cette IP. Cherchez des patterns : scraping séquentiel, POST répétés, paths suspects, absence de referer.</p>
                <p class="doc-source">Source : http_logs</p>
            </div></span>
        </span></div>
        <div class="overflow-x-auto" style="max-height:35vh; overflow-y:auto">
            <table class="data-table"><thead><tr>
                <th>Time</th><th>Method</th><th>Host</th><th>Path</th><th>HTTP</th><th>User-Agent</th><th>JA4</th>
            </tr></thead><tbody id="http-body"></tbody></table>
        </div>
    </div>

    <!-- Resource cascade -->
    <div class="section-card overflow-hidden" id="cascade-section" style="display:none">
        <div class="section-header"><span class="section-title">Cascade de ressources
            <span class="relative inline-block"><button onclick="docToggle(this)" class="doc-btn">ⓘ</button><div class="doc-panel">
                <h4>Analyse de cascade</h4>
                <p>Détection de navigateurs headless : un vrai navigateur charge une page HTML puis ses sous-ressources (CSS, JS, images) avec un délai croissant. Un bot ne charge souvent que la page principale.</p>
                <p><strong>Indicateurs :</strong> page_count=1 + max_sub=0 = bot probable. avg_sub_delay très bas = headless rapide.</p>
                <p class="doc-source">Source : view_resource_cascade_1h</p>
            </div></span>
        </span></div>
        <div class="overflow-x-auto" style="max-height:25vh; overflow-y:auto">
            <table class="data-table"><thead><tr>
                <th>Fenêtre</th><th>Host</th><th>Pages</th><th>Sub-resources max</th><th>Délai moyen (ms)</th><th>Écart-type (ms)</th>
            </tr></thead><tbody id="cascade-body"></tbody></table>
        </div>
    </div>
</div>
{% endblock %}
{% block scripts %}
<script>
const IP = {{ ip | tojson }};
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 loadIP() {
    try {
        const [d, radar, cascade] = await Promise.all([
            fetch(`/api/ip/${encodeURIComponent(IP)}`).then(r=>r.json()),
            fetch(`/api/ip/${encodeURIComponent(IP)}/radar`).then(r=>r.json()),
            fetch(`/api/cascade/${encodeURIComponent(IP)}`).then(r=>r.json()),
        ]);

        // KPIs
        document.getElementById('ip-det-count').textContent = d.detections?.length ?? 0;
        document.getElementById('ip-http-count').textContent = d.http_logs?.length ?? 0;
        document.getElementById('ip-score-count').textContent = d.scores?.length ?? 0;
        if (d.recurrence?.length) {
            document.getElementById('ip-recurrence').textContent = d.recurrence[0].recurrence || 0;
            document.getElementById('ip-worst-score').innerHTML = fmtScore(d.recurrence[0].worst_score);
        }
        // ASN info from first detection or score
        const firstRow = d.detections?.[0] || d.scores?.[0] || {};
        if (firstRow.asn_org || firstRow.country_code) {
            document.getElementById('ip-asn-info').innerHTML =
                (firstRow.asn_org ? fmtASN(firstRow.asn_org) : '') +
                (firstRow.country_code ? ' ' + fmtCountry(firstRow.country_code) : '');
        }

        // Radar
        if (radar.features?.length && Object.keys(radar.ip_values).length) {
            const labels = radar.features.map(f => f.replace(/_/g,' '));
            const ipVals = radar.features.map(f => radar.ip_values[f] ?? 0);
            const humanVals = radar.features.map(f => radar.human_baseline[f] ?? 0);
            const botVals = radar.features.map(f => radar.bot_baseline[f] ?? 0);
            const maxVals = radar.features.map((_,i) => Math.max(ipVals[i], humanVals[i], botVals[i], 0.001));
            const norm = arr => arr.map((v,i) => +(v/maxVals[i]).toFixed(3));
            const ch = initChart('radar-chart');
            ch.setOption(ecBase({
                tooltip: ecTooltip({}),
                legend: {data:['Cette IP','ISP moyen','Bot moyen'], bottom:0, textStyle:{color:EC_TEXT,fontSize:10}},
                radar: {
                    indicator: labels.map(() => ({max:1})),
                    shape:'polygon', radius:'65%',
                    splitArea:{areaStyle:{color:['rgba(99,102,241,0.02)','rgba(99,102,241,0.04)']}},
                    splitLine:{lineStyle:{color:EC_GRID}}, axisLine:{lineStyle:{color:EC_GRID}},
                    axisName:{color:EC_TEXT,fontSize:9,formatter:(_,i)=>labels[i.dimensionIndex]||''},
                },
                series: [{type:'radar', data: [
                    {value:norm(ipVals), name:'Cette IP', lineStyle:{color:'#f97316',width:2}, areaStyle:{color:'rgba(249,115,22,0.15)'}, itemStyle:{color:'#f97316'}},
                    {value:norm(humanVals), name:'ISP moyen', lineStyle:{color:'#22c55e',width:1,type:'dashed'}, areaStyle:{color:'rgba(34,197,94,0.05)'}, itemStyle:{color:'#22c55e'}},
                    {value:norm(botVals), name:'Bot moyen', lineStyle:{color:'#ef4444',width:1,type:'dashed'}, areaStyle:{color:'rgba(239,68,68,0.05)'}, itemStyle:{color:'#ef4444'}},
                ]}]
            }));
        }

        // Score timeline
        if (d.scores?.length) {
            const scores = [...d.scores].reverse();
            const ch = initChart('score-chart');
            ch.setOption(ecBase({
                tooltip: ecTooltip({trigger:'axis'}),
                grid: ecGrid(),
                xAxis: {type:'category', data:scores.map(s=>(s.detected_at||'').substring(11,16)), axisLine:{lineStyle:{color:EC_GRID}}, axisLabel:{color:EC_TEXT,fontSize:10}},
                yAxis: {type:'value', min:0, max:1, splitLine:{lineStyle:{color:EC_GRID,type:'dashed'}}, axisLabel:{color:EC_TEXT}},
                series: [{
                    type:'line', data:scores.map(s=>s.anomaly_score), smooth:true,
                    areaStyle:{color:new echarts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:'rgba(99,102,241,0.3)'},{offset:1,color:'rgba(99,102,241,0.02)'}])},
                    lineStyle:{color:'#6366f1',width:2}, itemStyle:{color:'#6366f1'}, symbol:'circle', symbolSize:4,
                }]
            }));
        }

        // Detections table
        document.getElementById('det-body').innerHTML = (d.detections||[]).map(row => `<tr>
            <td class="text-[11px] whitespace-nowrap text-gray-400">${(row.detected_at||'').substring(0,16)}</td>
            <td>${fmtScore(row.anomaly_score)}</td>
            <td class="font-mono text-[11px] text-gray-400">${parseFloat(row.raw_anomaly_score||0).toFixed(4)}</td>
            <td class="font-mono text-[11px] text-gray-400">${parseFloat(row.ae_recon_error||0).toFixed(4)}</td>
            <td class="font-mono text-[11px] text-gray-400">${parseFloat(row.xgb_prob||0).toFixed(4)}</td>
            <td>${threatBadge(row.threat_level)}</td>
            <td>${fmtJA4(row.ja4)}</td>
            <td class="text-xs max-w-[100px] truncate">${escapeHtml(row.host||'')}</td>
            <td class="font-mono text-xs">${row.hits||0}</td>
            <td class="text-[11px] text-purple-400">${row.campaign_id && row.campaign_id!=='0' ? '#'+escapeHtml(String(row.campaign_id).substring(0,8)) : ''}</td>
        </tr>`).join('') || '<tr><td colspan="10" class="text-center text-gray-500 py-4">Aucune détection</td></tr>';

        // AI Features
        if (d.ai_features?.length) {
            const f = d.ai_features[0];
            const skip = new Set(['src_ip','window_start','ja4','host','bot_name','src_ip_str']);
            document.getElementById('features-grid').innerHTML = Object.entries(f).filter(([k])=>!skip.has(k)).map(([k,v]) => {
                let val = typeof v === 'number' ? v.toFixed(4) : v;
                let color = 'text-gray-200';
                if (typeof v === 'number' && v > 0.7) color = 'text-red-400';
                else if (typeof v === 'number' && v > 0.4) color = 'text-orange-400';
                let display = `<span class="${color} font-mono text-xs">${val}</span>`;
                if (k === 'asn_org' && v) display = fmtASN(v);
                else if (k === 'country_code' && v) display = fmtCountry(v);
                else if (k === 'asn_label' && v) display = fmtLabel(v);
                return `<div class="bg-gray-800/50 rounded p-2"><div class="text-[9px] text-gray-500 truncate">${escapeHtml(k)}</div>${display}</div>`;
            }).join('');
            document.getElementById('features-section').style.display = '';
        }

        // HTTP logs
        const sc = s => s>=500?'text-red-400':s>=400?'text-orange-400':s>=300?'text-yellow-400':'text-green-400';
        document.getElementById('http-body').innerHTML = (d.http_logs||[]).map(row => `<tr>
            <td class="text-[11px] whitespace-nowrap text-gray-400">${row.time||''}</td>
            <td><span class="font-mono text-xs ${row.method==='POST'?'text-orange-400':'text-gray-300'}">${escapeHtml(row.method||'')}</span></td>
            <td class="text-xs max-w-[100px] truncate">${escapeHtml(row.host||'')}</td>
            <td class="text-xs max-w-[200px] truncate font-mono" title="${escapeHtml(row.path||'')}">${escapeHtml(row.path||'')}</td>
            <td class="font-mono text-[11px] text-gray-400">${escapeHtml(row.http_version||'')}</td>
            <td class="text-xs max-w-[180px] truncate text-gray-400">${escapeHtml(row.header_user_agent||'')}</td>
            <td class="font-mono text-[11px]">${escapeHtml(row.ja4||'')}</td>
        </tr>`).join('') || '<tr><td colspan="7" class="text-center text-gray-500 py-4">Aucun log</td></tr>';

        // Cascade
        const cascadeRows = cascade.data || [];
        if (cascadeRows.length) {
            document.getElementById('cascade-section').style.display = '';
            document.getElementById('cascade-body').innerHTML = cascadeRows.map(row => `<tr>
                <td class="text-[11px] whitespace-nowrap text-gray-400">${(row.window_start||'').substring(0,16)}</td>
                <td class="text-xs">${escapeHtml(row.host||'')}</td>
                <td class="font-mono text-xs">${row.page_count||0}</td>
                <td class="font-mono text-xs">${row.max_sub_resources||0}</td>
                <td class="font-mono text-xs ${(row.avg_sub_delay_ms||0)<50?'text-red-400':'text-gray-300'}">${(row.avg_sub_delay_ms||0).toFixed(0)}</td>
                <td class="font-mono text-xs">${(row.stddev_sub_delay_ms||0).toFixed(0)}</td>
            </tr>`).join('');
        }

    } catch(e) { console.error(e); }
}

document.getElementById('cls-btn').onclick = async () => {
    try {
        const r = await fetch('/api/classify', {method:'POST', headers:{'Content-Type':'application/json'},
            body:JSON.stringify({src_ip:IP, classification:document.getElementById('cls-select').value, comment:document.getElementById('cls-comment').value})});
        const d = await r.json();
        document.getElementById('cls-result').innerHTML = r.ok
            ? `<span class="text-green-400">✓ ${escapeHtml(d.classification)}</span>`
            : `<span class="text-red-400">✗ ${escapeHtml(d.detail||'erreur')}</span>`;
    } catch(e) { document.getElementById('cls-result').innerHTML = `<span class="text-red-400">✗ erreur</span>`; }
};

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