{% extends "base.html" %}
{% block title %}Net Mapper — AUTARCH{% endblock %}
{% block content %}
<div class="page-header">
    <h1>Network Topology Mapper</h1>
    <p class="text-muted">Host discovery, service enumeration, topology visualization</p>
</div>

<div class="tabs">
    <button class="tab active" onclick="switchTab('discover')">Discover</button>
    <button class="tab" onclick="switchTab('map')">Map</button>
    <button class="tab" onclick="switchTab('scans')">Saved Scans</button>
</div>

<!-- Discover -->
<div id="tab-discover" class="tab-content active">
    <div class="card" style="max-width:700px">
        <h3>Host Discovery</h3>
        <div style="display:flex;gap:0.5rem;align-items:end">
            <div class="form-group" style="flex:1;margin:0">
                <label>Target (CIDR, range, or single IP)</label>
                <input type="text" id="disc-target" class="form-control" placeholder="192.168.1.0/24">
            </div>
            <select id="disc-method" class="form-control" style="width:130px">
                <option value="auto">Auto</option>
                <option value="nmap">Nmap</option>
                <option value="icmp">ICMP/TCP</option>
            </select>
            <button class="btn btn-primary" id="disc-btn" onclick="startDiscover()">Scan</button>
        </div>
        <div id="disc-status" style="margin-top:0.5rem"></div>
    </div>

    <div id="disc-results" style="margin-top:1rem">
        <div id="disc-empty" class="card" style="text-align:center;color:var(--text-muted)">Run a discovery scan to find hosts</div>
        <div id="disc-hosts" style="display:none">
            <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.5rem">
                <h3>Discovered Hosts (<span id="host-count">0</span>)</h3>
                <div style="display:flex;gap:0.5rem">
                    <button class="btn btn-sm" onclick="showTopology()">View Map</button>
                    <button class="btn btn-sm" onclick="saveScan()">Save Scan</button>
                </div>
            </div>
            <table class="data-table">
                <thead><tr><th>IP</th><th>Hostname</th><th>MAC</th><th>OS</th><th>Ports</th><th>Action</th></tr></thead>
                <tbody id="hosts-body"></tbody>
            </table>
        </div>
    </div>
</div>

<!-- Map -->
<div id="tab-map" class="tab-content" style="display:none">
    <div class="card">
        <h3>Network Topology</h3>
        <div id="topology-canvas" style="width:100%;height:500px;background:#0a0a12;border-radius:var(--radius);position:relative;overflow:hidden">
            <svg id="topo-svg" width="100%" height="100%"></svg>
        </div>
        <div id="topo-info" style="margin-top:0.5rem;font-size:0.8rem;color:var(--text-muted)"></div>
    </div>
</div>

<!-- Saved Scans -->
<div id="tab-scans" class="tab-content" style="display:none">
    <h3>Saved Scans</h3>
    <div id="scans-list"></div>
    <div class="card" style="margin-top:1rem">
        <h4>Compare Scans</h4>
        <div style="display:flex;gap:0.5rem;align-items:end">
            <select id="diff-s1" class="form-control" style="flex:1"></select>
            <span>vs</span>
            <select id="diff-s2" class="form-control" style="flex:1"></select>
            <button class="btn btn-primary" onclick="diffScans()">Compare</button>
        </div>
        <div id="diff-results" style="margin-top:0.5rem"></div>
    </div>
</div>

<style>
.node-circle{cursor:pointer;transition:r 0.2s}
.node-circle:hover{r:12}
.spinner-inline{display:inline-block;width:14px;height:14px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin 0.8s linear infinite;vertical-align:middle;margin-right:6px}
@keyframes spin{to{transform:rotate(360deg)}}
</style>

<script>
let currentHosts=[];
let discPoll=null;

function switchTab(name){
    document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',['discover','map','scans'][i]===name));
    document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
    document.getElementById('tab-'+name).style.display='block';
    if(name==='scans') loadScans();
}

function startDiscover(){
    const target=document.getElementById('disc-target').value.trim();
    if(!target) return;
    document.getElementById('disc-btn').disabled=true;
    document.getElementById('disc-status').innerHTML='<div class="spinner-inline"></div> Discovering hosts...';
    fetch('/net-mapper/discover',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({target,method:document.getElementById('disc-method').value})})
    .then(r=>r.json()).then(d=>{
        if(!d.ok){showDiscError(d.error);return}
        if(discPoll) clearInterval(discPoll);
        discPoll=setInterval(()=>{
            fetch('/net-mapper/discover/'+d.job_id).then(r=>r.json()).then(s=>{
                if(!s.done) return;
                clearInterval(discPoll);discPoll=null;
                document.getElementById('disc-btn').disabled=false;
                document.getElementById('disc-status').innerHTML='';
                currentHosts=s.hosts||[];
                renderHosts(currentHosts);
            });
        },2000);
    }).catch(e=>showDiscError(e.message));
}

function showDiscError(msg){
    document.getElementById('disc-btn').disabled=false;
    document.getElementById('disc-status').innerHTML='<span style="color:var(--danger)">'+esc(msg)+'</span>';
}

function renderHosts(hosts){
    document.getElementById('disc-empty').style.display='none';
    document.getElementById('disc-hosts').style.display='';
    document.getElementById('host-count').textContent=hosts.length;
    const body=document.getElementById('hosts-body');
    body.innerHTML=hosts.map(h=>`<tr>
        <td><strong>${esc(h.ip)}</strong></td>
        <td>${esc(h.hostname||'—')}</td>
        <td style="font-size:0.8rem">${esc(h.mac||'—')}</td>
        <td style="font-size:0.8rem">${esc(h.os_guess||'—')}</td>
        <td>${(h.ports||[]).length}</td>
        <td><button class="btn btn-sm" onclick="scanHost('${h.ip}')">Detail Scan</button></td>
    </tr>`).join('');
}

function scanHost(ip){
    fetch('/net-mapper/scan-host',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({ip})})
    .then(r=>r.json()).then(d=>{
        if(!d.ok){alert(d.error);return}
        const h=d.host;
        let msg=`${h.ip} — ${h.os_guess||'unknown OS'}\n\nOpen Ports:\n`;
        (h.ports||[]).forEach(p=>{msg+=`  ${p.port}/${p.protocol} ${p.service||''} ${p.version||''}\n`});
        alert(msg);
        // Update in current hosts
        const idx=currentHosts.findIndex(x=>x.ip===ip);
        if(idx>=0) currentHosts[idx]=h;
    });
}

function showTopology(){
    if(!currentHosts.length) return;
    fetch('/net-mapper/topology',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({hosts:currentHosts})})
    .then(r=>r.json()).then(d=>{
        if(!d.ok) return;
        renderTopology(d);
        switchTab('map');
    });
}

function renderTopology(data){
    const svg=document.getElementById('topo-svg');
    const w=svg.clientWidth||800,h=svg.clientHeight||500;
    svg.innerHTML='';
    const nodes=data.nodes.filter(n=>n.type!=='subnet');
    const colors={host:'#6366f1',web:'#22c55e',server:'#f59e0b',windows:'#3b82f6',subnet:'#444'};

    // Simple force-directed-ish layout
    nodes.forEach((n,i)=>{
        const angle=(i/nodes.length)*Math.PI*2;
        const radius=Math.min(w,h)*0.35;
        n.x=w/2+Math.cos(angle)*radius;
        n.y=h/2+Math.sin(angle)*radius;
    });

    // Edges
    data.edges.forEach(e=>{
        const from=nodes.find(n=>n.id===e.from);
        const to=nodes.find(n=>n.id===e.to);
        if(from&&to){
            const line=document.createElementNS('http://www.w3.org/2000/svg','line');
            line.setAttribute('x1',from.x);line.setAttribute('y1',from.y);
            line.setAttribute('x2',to.x);line.setAttribute('y2',to.y);
            line.setAttribute('stroke','#333');line.setAttribute('stroke-width','1');
            svg.appendChild(line);
        }
    });

    // Nodes
    nodes.forEach(n=>{
        const g=document.createElementNS('http://www.w3.org/2000/svg','g');
        const circle=document.createElementNS('http://www.w3.org/2000/svg','circle');
        circle.setAttribute('cx',n.x);circle.setAttribute('cy',n.y);
        circle.setAttribute('r','8');circle.setAttribute('fill',colors[n.type]||'#6366f1');
        circle.classList.add('node-circle');
        const text=document.createElementNS('http://www.w3.org/2000/svg','text');
        text.setAttribute('x',n.x);text.setAttribute('y',n.y+20);
        text.setAttribute('text-anchor','middle');text.setAttribute('fill','#888');
        text.setAttribute('font-size','10');text.textContent=n.label||n.ip;
        g.appendChild(circle);g.appendChild(text);svg.appendChild(g);
    });

    document.getElementById('topo-info').textContent=`${nodes.length} hosts across ${data.subnets.length} subnet(s)`;
}

function saveScan(){
    if(!currentHosts.length) return;
    const name=prompt('Scan name:');
    if(!name) return;
    fetch('/net-mapper/scans',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({name,hosts:currentHosts})})
    .then(r=>r.json()).then(d=>{if(d.ok) alert('Scan saved')});
}

function loadScans(){
    fetch('/net-mapper/scans').then(r=>r.json()).then(d=>{
        const scans=d.scans||[];
        const list=document.getElementById('scans-list');
        const s1=document.getElementById('diff-s1'),s2=document.getElementById('diff-s2');
        s1.innerHTML='';s2.innerHTML='';
        if(!scans.length){list.innerHTML='<div class="card" style="color:var(--text-muted)">No saved scans</div>';return}
        list.innerHTML=scans.map(s=>`<div class="card" style="margin-bottom:0.5rem;cursor:pointer" onclick="loadSavedScan('${esc(s.file)}')">
            <div style="display:flex;justify-content:space-between"><div><strong>${esc(s.name)}</strong> — ${s.host_count} hosts</div>
            <span style="font-size:0.8rem;color:var(--text-muted)">${(s.timestamp||'').slice(0,19)}</span></div></div>`).join('');
        scans.forEach(s=>{
            s1.innerHTML+=`<option value="${esc(s.file)}">${esc(s.name)} (${s.host_count})</option>`;
            s2.innerHTML+=`<option value="${esc(s.file)}">${esc(s.name)} (${s.host_count})</option>`;
        });
    });
}

function loadSavedScan(file){
    fetch('/net-mapper/scans/'+encodeURIComponent(file)).then(r=>r.json()).then(d=>{
        if(!d.ok) return;
        currentHosts=d.scan.hosts||[];
        renderHosts(currentHosts);
        switchTab('discover');
    });
}

function diffScans(){
    const s1=document.getElementById('diff-s1').value;
    const s2=document.getElementById('diff-s2').value;
    if(!s1||!s2) return;
    fetch('/net-mapper/diff',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({scan1:s1,scan2:s2})})
    .then(r=>r.json()).then(d=>{
        if(!d.ok){document.getElementById('diff-results').innerHTML=esc(d.error);return}
        let html=`<div style="margin-top:0.5rem;font-size:0.85rem">`;
        html+=`<div style="color:#22c55e"><strong>+ New hosts (${d.new_hosts.length}):</strong> ${d.new_hosts.join(', ')||'none'}</div>`;
        html+=`<div style="color:var(--danger)"><strong>- Removed (${d.removed_hosts.length}):</strong> ${d.removed_hosts.join(', ')||'none'}</div>`;
        html+=`<div style="color:var(--text-muted)">Unchanged: ${d.unchanged_hosts.length}</div></div>`;
        document.getElementById('diff-results').innerHTML=html;
    });
}

function esc(s){return s?String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'):''}
</script>
{% endblock %}
