//! End-to-end networking validation for the KVM backend (TSI).
//!
//!   * EGRESS  — guest resolves a hostname (DNS over TSI) and HTTP-GETs it; the
//!     AF_TSI guest kernel tunnels the connect()/sendto() to the host muxer.
//!   * INGRESS — guest runs an HTTP server; the host `expose_tcp`'s it and GETs
//!     it back through the vsock mux.
//!
//! Requires a TSI-enabled guest kernel (CONFIG_TSI=y + af-tsi patches); point
//! the bake at one via SUPERMACHINE_X86_KERNEL_PATH.
//!
//! Usage: kvm_net [dest_dir]   (default /tmp/net-vm)

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn main() {
    use std::io::{Read, Write};
    use std::time::Duration;

    let dest = std::env::args()
        .nth(1)
        .unwrap_or_else(|| "/tmp/net-vm".to_string());

    eprintln!("=== bake alpine + start (TSI kernel) ===");
    let image = supermachine::Image::bake_kvm_auto("alpine", &dest).expect("bake_kvm_auto");
    let vm = image
        .start(&supermachine::VmConfig::new())
        .expect("Image::start");
    std::thread::sleep(Duration::from_millis(6000));

    // ---- EGRESS (with DNS) ----
    eprintln!("=== EGRESS: wget http://example.com/ (DNS over TSI) ===");
    let e = vm
        .exec_builder()
        .argv([
            "/bin/sh",
            "-c",
            "wget -q -T 15 -O- http://example.com/ 2>&1 | grep -o '<title>[^<]*</title>' | head -1; echo RC=$?",
        ])
        .output()
        .expect("exec wget");
    eprintln!(
        "EGRESS success={} out={:?}",
        e.success(),
        String::from_utf8_lossy(&e.stdout).trim_end()
    );

    // ---- INGRESS (expose_tcp) ----
    eprintln!("=== INGRESS: start guest httpd :8080, expose to host ===");
    // Detached guest server: a shell loop serving a sentinel page with busybox
    // nc (httpd applet isn't guaranteed; nc -l is). Each connection gets one
    // HTTP/1.0 response then nc exits; the loop re-listens.
    let _srv = vm
        .exec([
            "/bin/sh",
            "-c",
            "while :; do printf 'HTTP/1.0 200 OK\\r\\nContent-Length: 13\\r\\n\\r\\nsm-ingress-ok' \
             | /bin/busybox nc -l -p 8080; done",
        ])
        .expect("spawn server");
    std::thread::sleep(Duration::from_millis(1500)); // let it bind

    // Debug: what's listening on 8080 in the guest?
    let dbg = vm
        .exec_builder()
        .argv([
            "/bin/sh",
            "-c",
            "echo --listen--; /bin/busybox netstat -ltn 2>/dev/null | grep -E '8080|Proto'",
        ])
        .output()
        .expect("exec dbg");
    eprintln!(
        "[guest] {}",
        String::from_utf8_lossy(&dbg.stdout).trim_end()
    );

    let fwd = vm.expose_tcp(0, 8080).expect("expose_tcp");
    let addr = fwd.local_addr();
    eprintln!("host forwarder on {addr} -> guest :8080");

    let mut got = String::new();
    match std::net::TcpStream::connect(addr) {
        Ok(mut s) => {
            s.set_read_timeout(Some(Duration::from_secs(8))).ok();
            let _ = s.write_all(b"GET / HTTP/1.0\r\nHost: x\r\n\r\n");
            let _ = s.read_to_string(&mut got);
        }
        Err(err) => eprintln!("INGRESS connect error: {err}"),
    }
    let ingress_ok = got.contains("sm-ingress-ok");
    eprintln!(
        "INGRESS ok={ingress_ok} response_tail={:?}",
        got.lines().last().unwrap_or("")
    );

    drop(fwd);
    vm.stop().expect("stop");
    eprintln!("=== done ===");
}

#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
fn main() {
    eprintln!("kvm_net is Linux/x86_64 only");
}
