use core::{
    ffi::{c_char, c_int, c_long, c_void, CStr},
    ptr::NonNull,
};

use yggdrasil_rt::process::{signal as rt, Signal};

use crate::{
    error::{self, CIntZeroResult, CResult, EResult},
    headers::errno::Errno,
    signal,
    util::PointerExt,
};

use super::{
    sys_time::__ygg_timespec_t,
    sys_types::{pid_t, uid_t},
};

pub type sig_handler_t = unsafe extern "C" fn(c_int);

pub type sigset_t = u64;

#[repr(C)]
pub struct __stack {
    pub ss_sp: *mut c_void,
    pub ss_flags: c_int,
    pub ss_size: usize,
}

pub type stack_t = __stack;

#[repr(C)]
pub struct sigevent {
    pub sigev_notify: c_int,
    pub sigev_signo: c_int,
    pub sigev_value: sigval,
    pub sigev_notify_function: unsafe extern "C" fn(sigval),
    // TODO pthread_attr_t *
    pub sigev_notify_attributes: *mut c_void,
}

pub type __ygg_sigevent_t = sigevent;

#[repr(C)]
pub struct sigaction {
    pub sa_handler: unsafe extern "C" fn(c_int),
    pub sa_mask: sigset_t,
    pub sa_flags: c_int,
    pub sa_sigaction: unsafe extern "C" fn(c_int, *mut siginfo_t, *mut c_void),
}

#[repr(C)]
pub struct siginfo {
    pub si_signo: c_int,
    pub si_code: c_int,
    pub si_errno: c_int,
    pub si_pid: pid_t,
    pub si_uid: uid_t,
    pub si_addr: *mut c_void,
    pub si_status: c_int,
    pub si_band: c_long,
    pub si_value: sigval,
}

pub type siginfo_t = siginfo;

#[repr(C)]
pub union sigval {
    pub sival_int: c_int,
    pub sival_ptr: *mut c_void,
}

// TODO sig_atomic_t

// SIG_DFL, SIG_ERR, SIG_HOLD, SIG_IGN are in <bits/signal.h>

extern "C" {
    fn __sig_terminate(_: c_int);
    fn __sig_ignore(_: c_int);
}

pub const SIGHUP: c_int = 1;
pub const SIGINT: c_int = 2;
pub const SIGQUIT: c_int = 3;
pub const SIGILL: c_int = 4;
pub const SIGTRAP: c_int = 5;
pub const SIGABRT: c_int = 6;
pub const SIGBUS: c_int = 7;
pub const SIGFPE: c_int = 8;
pub const SIGKILL: c_int = 9;
pub const SIGUSR1: c_int = 10;
pub const SIGSEGV: c_int = 11;
pub const SIGUSR2: c_int = 12;
pub const SIGPIPE: c_int = 13;
pub const SIGALRM: c_int = 14;
pub const SIGTERM: c_int = 15;
pub const SIGCHLD: c_int = 17;
pub const SIGCONT: c_int = 18;
pub const SIGSTOP: c_int = 19;
pub const SIGTSTP: c_int = 20;
pub const SIGTTIN: c_int = 21;
pub const SIGTTOU: c_int = 22;
pub const SIGURG: c_int = 23;
pub const SIGXCPU: c_int = 24;
pub const SIGXFSZ: c_int = 25;
pub const SIGVTALRM: c_int = 26;
pub const SIGPROF: c_int = 27;
pub const SIGWINCH: c_int = 28;
pub const SIGPOLL: c_int = 29;
pub const SIGPWR: c_int = 30;
pub const SIGSYS: c_int = 31;
pub const SIGNAL_MAX: c_int = SIGSYS + 1;

pub const SIGEV_NONE: c_int = 0;
pub const SIGEV_SIGNAL: c_int = 1;
pub const SIGEV_THREAD: c_int = 2;

pub const SIG_BLOCK: c_int = 1;
pub const SIG_UNBLOCK: c_int = 2;
pub const SIG_SETMASK: c_int = 3;

pub const SA_NOCLDSTOP: c_int = 1 << 0;
pub const SA_ONSTACK: c_int = 1 << 2;
pub const SA_RESETHAND: c_int = 1 << 3;
pub const SA_RESTART: c_int = 1 << 4;
pub const SA_SIGINFO: c_int = 1 << 5;
pub const SA_NOCLDWAIT: c_int = 1 << 6;
pub const SA_NODEFER: c_int = 1 << 7;

pub const SS_ONSTACK: c_int = 1 << 8;
pub const SS_DISABLE: c_int = 1 << 9;

pub const MINSIGSTKSZ: usize = 2 * 4096;
pub const SIGSTKSZ: usize = 8 * 4096;

pub fn int_to_signum(value: c_int) -> EResult<Signal> {
    match value {
        SIGABRT => EResult::Ok(Signal::Aborted),
        SIGSEGV => EResult::Ok(Signal::MemoryAccessViolation),
        SIGINT => EResult::Ok(Signal::Interrupted),
        SIGILL => EResult::Ok(Signal::InvalidInstruction),
        SIGKILL => EResult::Ok(Signal::Killed),
        SIGTERM => EResult::Ok(Signal::Terminated),
        _ => EResult::Err(Errno::EINVAL),
    }
}

pub fn signum_to_int(value: Signal) -> c_int {
    match value {
        Signal::Aborted => SIGABRT,
        Signal::MemoryAccessViolation => SIGSEGV,
        Signal::Interrupted => SIGINT,
        Signal::InvalidInstruction => SIGILL,
        Signal::Killed => SIGKILL,
        Signal::Terminated => SIGTERM,
        // Never issued/handled
        Signal::Debug => unreachable!(),
    }
}

pub fn signal_string(value: c_int) -> Option<&'static CStr> {
    match value {
        SIGHUP => Some(c"Hangup"),
        SIGINT => Some(c"Interrupted"),
        SIGQUIT => Some(c"Quit"),
        SIGILL => Some(c"Illegal instruction"),
        SIGTRAP => Some(c"Trap/breakpoint"),
        SIGABRT => Some(c"Aborted"),
        SIGBUS => Some(c"Bus error"),
        SIGFPE => Some(c"Floating-point exception"),
        SIGKILL => Some(c"Killed"),
        SIGUSR1 => Some(c"User signal 1"),
        SIGSEGV => Some(c"Segmentation fault"),
        SIGUSR2 => Some(c"User signal 2"),
        SIGPIPE => Some(c"Broken pipe"),
        SIGALRM => Some(c"Timer alarm"),
        SIGTERM => Some(c"Terminated"),
        SIGCHLD => Some(c"Child quit"),
        SIGCONT => Some(c"Continued"),
        SIGSTOP => Some(c"Stopped"),
        SIGTSTP => Some(c"Terminal stopped"),
        SIGTTIN => Some(c"Background process terminal input"),
        SIGTTOU => Some(c"Background process terminal output"),
        SIGURG => Some(c"Urgent"),
        SIGXCPU => Some(c"CPU time limit exceeded"),
        SIGXFSZ => Some(c"File size limit exceeded"),
        SIGVTALRM => Some(c"Virtual alarm"),
        SIGPROF => Some(c"Profiling timer expired"),
        SIGWINCH => Some(c"Window size changed"),
        SIGPOLL => Some(c"Pollable event"),
        SIGPWR => Some(c"Power failure"),
        SIGSYS => Some(c"Bad system call"),
        _ => None,
    }
}

#[no_mangle]
unsafe extern "C" fn kill(_pid: pid_t, _signum: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn killpg(_pgid: pid_t, _signum: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn psiginfo(_info: *const siginfo_t, _m: *const c_char) {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn psignal(_signum: c_int, _m: *const c_char) {
    todo!()
}

// TODO pthread_t
#[no_mangle]
unsafe extern "C" fn pthread_kill(_pt: *mut c_void, _signum: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn pthread_sigmask(
    _a: c_int,
    _new: *const sigset_t,
    _old: *mut sigset_t,
) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn raise(_signum: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigaction(
    signum: c_int,
    _new: *const sigaction,
    _old: *mut sigaction,
) -> CIntZeroResult {
    log::error!("TODO: sigaction({:?})", signum);
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn sigaddset(mask: *mut sigset_t, signum: c_int) -> CIntZeroResult {
    if signum > 63 || signum <= 0 {
        error::set_errno(Errno::EINVAL);
        return CIntZeroResult::ERROR;
    }
    *mask |= 1 << signum;
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn sigaltstack(new: *const stack_t, old: *mut stack_t) -> CIntZeroResult {
    if let Some(old) = NonNull::new(old) {
        let (base, size) = rt::get_signal_stack();
        old.write(stack_t {
            ss_sp: base as _,
            ss_flags: 0,
            ss_size: size,
        });
    }
    if let Some(new) = new.as_ref() {
        // TODO what do with SS_DISABLE?
        if new.ss_size < MINSIGSTKSZ {
            error::set_errno(Errno::ENOMEM);
            return CIntZeroResult::ERROR;
        }
        rt::set_signal_stack(new.ss_sp.addr(), new.ss_size);
    }
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn sigdelset(_mask: *mut sigset_t, _signum: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigemptyset(mask: *mut sigset_t) -> CIntZeroResult {
    let mask = mask.ensure_mut();
    *mask = 0;
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn sigfillset(_mask: *mut sigset_t) -> c_int {
    0
}

#[no_mangle]
unsafe extern "C" fn sighold(_signum: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigignore(_signum: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn siginterrupt(_signum: c_int, _b: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigismember(mask: *const sigset_t, signum: c_int) -> c_int {
    if signum > 63 || signum <= 0 {
        error::set_errno(Errno::EINVAL);
        return -1;
    }
    let mask = *mask.ensure();
    if mask & (1 << signum) != 0 {
        1
    } else {
        0
    }
}

#[no_mangle]
unsafe extern "C" fn signal(signum: c_int, handler: sig_handler_t) -> sig_handler_t {
    // Validate handler
    let address = handler as usize;
    // NULL or SIG_ERR
    if address == 0 || address == 1 {
        log::error!("libc: signal() was passed an invalid handler");
        error::set_errno(Errno::EINVAL);
        // SIG_ERR
        return core::mem::transmute(1usize);
    }

    match signal::set(signum, handler) {
        EResult::Ok(handler) => handler,
        EResult::Err(err) => {
            error::set_errno(err);
            // SIG_ERR
            core::mem::transmute(1usize)
        }
    }
}

#[no_mangle]
unsafe extern "C" fn sigpause(_signum: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigpending(_mask: *mut sigset_t) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigprocmask(
    _signum: c_int,
    _new: *const sigset_t,
    _old: *mut sigset_t,
) -> c_int {
    0
}

#[no_mangle]
unsafe extern "C" fn sigqueue(_pid: pid_t, _signum: c_int, _val: sigval) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigrelse(_signum: c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigset(_signum: c_int, _handler: sig_handler_t) -> sig_handler_t {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigsuspend(_mask: *const sigset_t) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigtimedwait(
    _mask: *const sigset_t,
    _info: *mut siginfo_t,
    _time: *const __ygg_timespec_t,
) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigwait(_mask: *const sigset_t, _signum: *mut c_int) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sigwaitinfo(_mask: *const sigset_t, _info: *mut siginfo_t) -> c_int {
    todo!()
}
