/// Solo mode helpers: spawn nats-server child process and wait for readiness
#[cfg(feature = "nats")]
pub mod inner {
    use anyhow::{Context, Result};
    use tokio::net::TcpStream;
    use tokio::process::Command;
    use tokio::time::{timeout, Duration, Instant};
    use tracing::info;

    /// Spawn `nats-server` as a child process with JetStream enabled.
    ///
    /// The returned `Child` holds the process alive; drop it to kill the
    /// server. Uses `kill_on_drop(true)` so the process is cleaned up when
    /// `Child` is dropped.
    pub async fn spawn_nats_server(data_dir: &str) -> Result<tokio::process::Child> {
        let nats_store_dir = format!("{}/nats", data_dir);
        std::fs::create_dir_all(&nats_store_dir)
            .context("Failed to create NATS storage directory")?;

        let child = Command::new("nats-server")
            .args(["-js", "-sd", &nats_store_dir, "-p", "4222"])
            .kill_on_drop(true)
            .spawn()
            .context("Failed to spawn nats-server — ensure nats-server is in PATH")?;

        wait_for_nats(4222).await?;
        info!("✓ NATS server (solo mode) ready on port 4222");
        Ok(child)
    }

    /// Attempt TCP connect to 127.0.0.1:{port} in a loop until ready or
    /// timeout.
    async fn wait_for_nats(port: u16) -> Result<()> {
        let addr = format!("127.0.0.1:{}", port);
        let deadline = Instant::now() + Duration::from_secs(10);
        loop {
            if Instant::now() > deadline {
                return Err(anyhow::anyhow!(
                    "NATS server did not become ready within 10 seconds on port {}",
                    port
                ));
            }
            if timeout(Duration::from_millis(200), TcpStream::connect(&addr))
                .await
                .is_ok()
            {
                return Ok(());
            }
            tokio::time::sleep(Duration::from_millis(100)).await;
        }
    }
}
