use dirigent_process::{create_manager, graceful_shutdown_sync};
use std::process::Command;
use std::time::Duration;

/// Build a long-running command that does not require a TTY on any platform.
///
/// On Windows, `timeout /t N /nobreak` fails when stdin is a pipe (no console),
/// so we use `ping -n N 127.0.0.1` which sleeps approximately N-1 seconds with
/// no TTY requirement.
///
/// On Unix, `sleep N` is the idiomatic choice.
#[cfg(windows)]
fn long_sleep_cmd(seconds: u32) -> Command {
    let mut cmd = Command::new("ping");
    cmd.args(["-n", &seconds.to_string(), "127.0.0.1"]);
    cmd
}

#[cfg(unix)]
fn long_sleep_cmd(seconds: u32) -> Command {
    let mut cmd = Command::new("sleep");
    cmd.arg(seconds.to_string());
    cmd
}

#[cfg(all(windows, feature = "tokio"))]
fn long_sleep_async_cmd(seconds: u32) -> tokio::process::Command {
    let mut cmd = tokio::process::Command::new("ping");
    cmd.args(["-n", &seconds.to_string(), "127.0.0.1"]);
    cmd
}

#[cfg(all(unix, feature = "tokio"))]
fn long_sleep_async_cmd(seconds: u32) -> tokio::process::Command {
    let mut cmd = tokio::process::Command::new("sleep");
    cmd.arg(seconds.to_string());
    cmd
}

#[test]
fn test_manager_init() {
    let mgr = create_manager();
    mgr.init().expect("init should succeed");
    // Double init should also succeed (idempotent)
    mgr.init().expect("double init should succeed");
}

#[test]
fn test_create_lifecycle() {
    let mgr = create_manager();
    mgr.init().expect("init failed");
    let _lifecycle = mgr.create_lifecycle();
}

#[test]
fn test_configure_and_spawn() {
    let mgr = create_manager();
    mgr.init().expect("init failed");
    let lifecycle = mgr.create_lifecycle();

    let mut cmd = long_sleep_cmd(30);
    lifecycle.configure_command(&mut cmd);

    let mut child = cmd.spawn().expect("spawn failed");
    let pid = child.id();
    assert!(pid > 0);

    // Register should succeed
    lifecycle.register_child(pid).expect("register failed");

    // Process should still be running
    assert!(child.try_wait().expect("try_wait failed").is_none());

    // Clean up
    let _ = child.kill();
    let _ = child.wait();
}

#[test]
fn test_graceful_shutdown_sync() {
    let mgr = create_manager();
    mgr.init().expect("init failed");
    let lifecycle = mgr.create_lifecycle();

    let mut cmd = long_sleep_cmd(60);
    lifecycle.configure_command(&mut cmd);
    let mut child = cmd.spawn().expect("spawn failed");
    let pid = child.id();
    lifecycle.register_child(pid).expect("register failed");

    // Graceful shutdown with 3s timeout — process won't exit voluntarily,
    // so it should be force-killed after timeout
    let exited_gracefully = graceful_shutdown_sync(
        lifecycle.as_ref(),
        &mut child,
        Duration::from_secs(3),
    );

    // Process should be dead now
    assert!(child.try_wait().expect("try_wait failed").is_some());
    // It was force-killed (ping/sleep don't handle SIGTERM/CTRL_BREAK)
    assert!(!exited_gracefully);
}

#[test]
fn test_send_kill_signal() {
    let mgr = create_manager();
    mgr.init().expect("init failed");
    let lifecycle = mgr.create_lifecycle();

    let mut cmd = long_sleep_cmd(60);
    lifecycle.configure_command(&mut cmd);
    let mut child = cmd.spawn().expect("spawn failed");
    let pid = child.id();
    lifecycle.register_child(pid).expect("register failed");

    // Direct kill signal
    lifecycle.send_kill_signal(pid).expect("kill failed");

    // Wait for process to die
    let status = child.wait().expect("wait failed");
    assert!(!status.success());
}

#[cfg(feature = "tokio")]
#[tokio::test]
async fn test_async_graceful_shutdown() {
    use dirigent_process::graceful_shutdown_async;

    let mgr = create_manager();
    mgr.init().expect("init failed");
    let lifecycle = mgr.create_lifecycle();

    let mut cmd = long_sleep_async_cmd(60);
    lifecycle.configure_async_command(&mut cmd);
    let mut child = cmd.spawn().expect("spawn failed");
    let pid = child.id().expect("no pid");
    lifecycle.register_child(pid).expect("register failed");

    let exited_gracefully = graceful_shutdown_async(
        lifecycle.as_ref(),
        &mut child,
        Duration::from_secs(3),
    )
    .await;

    assert!(child.try_wait().expect("try_wait failed").is_some());
    assert!(!exited_gracefully);
}
