use core::ffi::c_int;

use yggdrasil_rt::{
    process::{
        signal::{self, SignalHandler},
        ExitCode, Signal,
    },
    sync::rwlock::RwLock,
};

use crate::{
    error::EResult,
    headers::{
        errno::Errno,
        signal::{sig_handler_t, signum_to_int, SIGNAL_MAX},
    },
};

const SIGNAL_STACK_SIZE: usize = 4096 * 8;

static SIGNAL_TABLE: RwLock<[Option<unsafe extern "C" fn(c_int)>; SIGNAL_MAX as usize]> =
    RwLock::new([None; SIGNAL_MAX as usize]);

#[no_mangle]
unsafe extern "C" fn __sig_ignore(_signum: c_int) {
    unreachable!()
}

#[no_mangle]
unsafe extern "C" fn __sig_terminate(_signum: c_int) {
    unreachable!()
}

fn signal_handler(signal: Signal) {
    let signum = signum_to_int(signal);
    if let Some(entry) = SIGNAL_TABLE.read()[signum as usize] {
        unsafe { entry(signum) };
    } else {
        let pid = unsafe { yggdrasil_rt::sys::get_pid() }.into_raw();
        log::error!("{pid}: terminated by signal {signal:?}");
        unsafe { yggdrasil_rt::sys::exit_process(ExitCode::BySignal(Ok(signal))) };
    }
}

pub unsafe fn set(sig: c_int, handler: sig_handler_t) -> EResult<sig_handler_t> {
    if !(0..SIGNAL_MAX).contains(&sig) {
        return EResult::Err(Errno::EINVAL);
    }

    let mut table = SIGNAL_TABLE.write();
    // let signal = int_to_signum(sig)?;
    let address = handler as usize;
    let handler = match handler {
        // Transform special cases into Rust signal handlers instead
        _ if address == __sig_terminate as usize => todo!(),
        _ if address == __sig_ignore as usize => todo!(),
        // This is safe: handler essentially has the same type, just in a wrapper
        _ => handler,
    };
    let old = table[sig as usize].replace(handler);

    let old = match old {
        Some(old) => old,
        None => __sig_terminate as sig_handler_t,
    };
    EResult::Ok(old)
}

pub fn init(main: bool) {
    // TODO reset any non-default handlers possibly set by whatever libc might've been entered from
    if main {
        signal::setup_signal_full(SIGNAL_STACK_SIZE)
            .expect("Couldn't setup signal handler for the main thread");
    } else {
        signal::setup_signal_stack(SIGNAL_STACK_SIZE).expect("Couldn't setup thread signal stack");
    }

    signal::set_all_handlers(SignalHandler::Rust(signal_handler));
}
