use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};

use asupersync::runtime::RuntimeHandle;

/// Spawn a background task that listens for Ctrl+C.
///
/// First press: cancels `signal` and prints an interrupt message.
/// Second press: force-exits with code 130.
pub fn install_ctrl_c_handler(handle: &RuntimeHandle, signal: ShutdownSignal) {
    handle.spawn(async move {
        let _ = asupersync::signal::ctrl_c().await;
        eprintln!("\nInterrupted, finishing current batch... (Ctrl+C again to force quit)");
        signal.cancel();
        let _ = asupersync::signal::ctrl_c().await;
        std::process::exit(130);
    });
}

/// A cooperative cancellation token for graceful shutdown.
///
/// Clone-able and cheaply checkable from any thread or async task.
/// When `cancel()` is called (typically from a Ctrl+C signal handler),
/// all clones observe the cancellation via `is_cancelled()`.
#[derive(Clone)]
pub struct ShutdownSignal {
    cancelled: Arc<AtomicBool>,
}

impl ShutdownSignal {
    pub fn new() -> Self {
        Self {
            cancelled: Arc::new(AtomicBool::new(false)),
        }
    }

    pub fn cancel(&self) {
        self.cancelled.store(true, Ordering::Release);
    }

    pub fn is_cancelled(&self) -> bool {
        self.cancelled.load(Ordering::Acquire)
    }
}

impl Default for ShutdownSignal {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn signal_starts_uncancelled() {
        let signal = ShutdownSignal::new();
        assert!(!signal.is_cancelled());
    }

    #[test]
    fn cancel_sets_flag() {
        let signal = ShutdownSignal::new();
        signal.cancel();
        assert!(signal.is_cancelled());
    }

    #[test]
    fn clone_propagates_cancellation() {
        let signal = ShutdownSignal::new();
        let clone = signal.clone();
        signal.cancel();
        assert!(
            clone.is_cancelled(),
            "clone should see cancellation from original"
        );
    }
}
