// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::sync::{Arc, atomic::{AtomicU8, Ordering}};
use std::process;

use tokio::signal;

pub struct CtrlCHandler(Arc<AtomicU8>);

impl CtrlCHandler {
	pub fn new() -> CtrlCHandler {
		CtrlCHandler(Arc::new(AtomicU8::new(0)))
	}

	pub async fn listen(&self) {
		self.listen_with(|| ()).await;
	}

	pub async fn listen_with(&self, mut f: impl FnMut() + Send) {
		loop {
			if let Err(err) = signal::ctrl_c().await {
				log::warn!("failed waiting for Ctrl+C: {err}");
				return;
			}

			let prev = self.0.fetch_add(1, Ordering::AcqRel);
			if prev >= 3 {
				log::info!("Forcefully exiting");
				process::exit(1);
			} else {
				log::info!("Ctrl+C intercepted, repeat {} more time(s) to forcefully exit", 3 - prev);
				f();
			}
		}
	}

	#[cfg(feature = "client")]
	pub fn should_exit(&self) -> bool {
		self.0.load(Ordering::Acquire) > 0
	}
}

impl Clone for CtrlCHandler {
	fn clone(&self) -> CtrlCHandler {
		CtrlCHandler(Arc::clone(&self.0))
	}
}
