use anyhow::Result;
use tokio_stream::StreamExt;

pub(super) struct Signals;

impl Signals {
	pub(super) fn start() -> Result<()> {
		#[cfg(unix)]
		use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP};

		#[cfg(unix)]
		let mut stream = signal_hook_tokio::Signals::new([
			// Interrupt signals (Ctrl-C, Ctrl-\)
			SIGINT, SIGQUIT, //
			// Hangup signal (Terminal closed)
			SIGHUP, //
			// Termination signal (kill)
			SIGTERM, //
			// Job control signals (Ctrl-Z, fg/bg)
			SIGTSTP, SIGCONT,
		])?;
		#[cfg(windows)]
		let mut stream = tokio_stream::empty();

		tokio::spawn(async move {
			while let Some(n) = stream.next().await {
				if !Self::handle(n).await {
					break;
				}
			}
		});
		Ok(())
	}

	#[cfg(unix)]
	async fn handle(n: libc::c_int) -> bool {
		use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGSTOP, SIGTERM, SIGTSTP};
		use yazi_macro::error;
		use yazi_term::YIELD_TO_SUBPROCESS;

		match n {
			SIGINT => { /* ignored */ }
			SIGQUIT | SIGHUP | SIGTERM => {
				yazi_proxy::AppProxy::quit(Default::default());
				return false;
			}
			SIGTSTP => {
				yazi_scheduler::AppProxy::stop().await;
				if unsafe { libc::kill(0, SIGSTOP) } != 0 {
					error!("Failed to stop the process:\n{}", std::io::Error::last_os_error());
					yazi_proxy::AppProxy::quit(Default::default());
				}
			}
			SIGCONT if YIELD_TO_SUBPROCESS.try_acquire().is_ok() => {
				yazi_scheduler::AppProxy::resume().await;
			}
			_ => {}
		}
		true
	}

	#[cfg(windows)]
	async fn handle(_: ()) -> bool { unreachable!() }
}
