use crate::traits::ProcessLifecycle;
use std::time::Duration;

/// Graceful shutdown: send signal → wait → force kill (sync, blocking).
///
/// Returns `true` if the process exited within the timeout, `false` if force-killed.
pub fn graceful_shutdown_sync(
    lifecycle: &dyn ProcessLifecycle,
    child: &mut std::process::Child,
    timeout: Duration,
) -> bool {
    let pid = child.id();
    if pid == 0 {
        return true;
    }

    if lifecycle.send_shutdown_signal(pid).is_err() {
        return true;
    }

    let start = std::time::Instant::now();
    let poll_interval = Duration::from_millis(50);

    while start.elapsed() < timeout {
        match child.try_wait() {
            Ok(Some(_)) => return true,
            Ok(None) => std::thread::sleep(poll_interval),
            Err(_) => return true,
        }
    }

    tracing::debug!(pid, "Graceful shutdown timed out, force killing");
    let _ = lifecycle.send_kill_signal(pid);
    let _ = child.wait();
    false
}

/// Graceful shutdown: send signal → wait → force kill (async, non-blocking).
///
/// Returns `true` if the process exited within the timeout, `false` if force-killed.
#[cfg(feature = "tokio")]
pub async fn graceful_shutdown_async(
    lifecycle: &dyn ProcessLifecycle,
    child: &mut tokio::process::Child,
    timeout: Duration,
) -> bool {
    let pid = match child.id() {
        Some(0) | None => return true,
        Some(pid) => pid,
    };

    if lifecycle.send_shutdown_signal(pid).is_err() {
        return true;
    }

    match tokio::time::timeout(timeout, child.wait()).await {
        Ok(Ok(_)) => true,
        Ok(Err(_)) => true,
        Err(_) => {
            tracing::debug!(pid, "Graceful shutdown timed out, force killing");
            let _ = lifecycle.send_kill_signal(pid);
            let _ = child.wait().await;
            false
        }
    }
}
