//! Live interop: receive and verify a real Python `rnsd`'s announces over TCP.
//!
//! Ignored by default (CI has no Python RNS). To run it:
//!
//! ```sh
//! python3 -m venv .venv && .venv/bin/pip install rns
//! PLEXUS_RNS_PYTHON=$PWD/.venv/bin/python \
//!   cargo test -p plexus-iface --test interop_announce -- --ignored --nocapture
//! ```
//!
//! It starts `crates/plexus-vectors/interop/announce_server.py` (an RNS node
//! announcing over a `TCPServerInterface`), connects our `TcpClient`, drives the
//! frames through a real `Gateway`, and asserts the announce verifies and
//! surfaces with the destination the Python side printed.

use std::io::BufRead;
use std::process::{Command, Stdio};
use std::time::Duration;

use plexus_gateway::identity::Identity;
use plexus_gateway::{Gateway, GatewayCmd, GatewayConfig, GatewayEvent, InterfaceId};
use plexus_iface::{Interface, TcpClient};

fn hex_decode(s: &str) -> Vec<u8> {
    (0..s.len())
        .step_by(2)
        .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
        .collect()
}

#[tokio::test]
#[ignore = "requires a Python RNS reference; set PLEXUS_RNS_PYTHON and run with --ignored"]
async fn receives_live_announce_from_python_rnsd() {
    let Ok(python) = std::env::var("PLEXUS_RNS_PYTHON") else {
        eprintln!("PLEXUS_RNS_PYTHON unset; skipping live interop test");
        return;
    };
    let script = concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/../plexus-vectors/interop/announce_server.py"
    );

    // Grab a free port, then let the Python server bind it.
    let port = std::net::TcpListener::bind("127.0.0.1:0")
        .unwrap()
        .local_addr()
        .unwrap()
        .port();

    let mut child = Command::new(&python)
        .arg(script)
        .arg(port.to_string())
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .spawn()
        .expect("spawn python rnsd announce server");

    // The server prints `DEST <hex>` once it is up and listening.
    let mut reader = std::io::BufReader::new(child.stdout.take().unwrap());
    let mut line = String::new();
    reader.read_line(&mut line).expect("read DEST line");
    let expected = hex_decode(
        line.trim()
            .strip_prefix("DEST ")
            .expect("DEST line from server"),
    );
    assert_eq!(expected.len(), 16, "destination hash is 16 bytes");

    let client = Box::new(TcpClient::new(InterfaceId(1), format!("127.0.0.1:{port}")));
    let mut handle = client.start().await.expect("connect to python rnsd");
    let mut gateway = Gateway::new(
        Identity::from_bytes(&[5u8; 64]).unwrap(),
        GatewayConfig::default(),
    );

    let found = tokio::time::timeout(Duration::from_secs(20), async {
        while let Some(rx) = handle.rx.recv().await {
            let events = gateway.step(GatewayCmd::SubmitInbound {
                from_iface: InterfaceId(1),
                bytes: rx.bytes,
            });
            for ev in events {
                if let GatewayEvent::AnnounceReceived {
                    destination,
                    app_data,
                    ..
                } = ev
                {
                    if destination.as_bytes()[..] == expected[..] {
                        assert_eq!(&app_data[..], b"interop");
                        return true;
                    }
                }
            }
        }
        false
    })
    .await
    .unwrap_or(false);

    handle.cancel.cancel();
    let _ = child.kill();
    let _ = child.wait();

    assert!(
        found,
        "did not receive/verify the expected announce from Python rnsd"
    );
}
