use core::{ffi::c_int, ptr::NonNull, sync::atomic::Ordering};

use yggdrasil_rt::process::MutexOperation;

use crate::{
    error::{self, CIntZeroResult, CResult, EResult},
    headers::{
        errno::Errno,
        pthread::{
            PTHREAD_MUTEX_NORMAL, PTHREAD_MUTEX_STALLED, PTHREAD_PRIO_NONE, PTHREAD_PROCESS_PRIVATE,
        },
        sys_time::__ygg_timespec_t,
        sys_types::{pthread_mutex_t, pthread_mutexattr_t},
    },
    thread::Thread,
    util::PointerExt,
};

use super::{PTHREAD_MUTEX_DEFAULT, PTHREAD_MUTEX_RECURSIVE};

impl pthread_mutex_t {
    const LOCKED: u32 = 1;

    fn try_lock(&self) -> EResult<bool> {
        match self.__mode {
            PTHREAD_MUTEX_NORMAL => EResult::Ok(
                self.__state
                    .compare_exchange(0, Self::LOCKED, Ordering::Acquire, Ordering::Relaxed)
                    .is_ok(),
            ),
            PTHREAD_MUTEX_RECURSIVE => {
                let mut thread_id = Thread::this().expect("pthread_self() failed");
                if thread_id == 0 {
                    thread_id = u32::MAX;
                }

                match self.__state.compare_exchange(
                    0,
                    thread_id,
                    Ordering::Acquire,
                    Ordering::Relaxed,
                ) {
                    // 0 -> thread_id
                    Ok(_) => {
                        self.__recursion.fetch_add(1, Ordering::Relaxed);
                        EResult::Ok(true)
                    }
                    Err(t) if t == thread_id => {
                        self.__recursion.fetch_add(1, Ordering::Relaxed);
                        EResult::Ok(true)
                    }
                    // Other thread owns the lock
                    Err(_) => EResult::Ok(false),
                }
            }
            _ => EResult::Err(Errno::EINVAL),
        }
    }

    fn try_lock_err(&self) -> EResult<()> {
        if self.try_lock()? {
            EResult::Ok(())
        } else {
            EResult::Err(Errno::EBUSY)
        }
    }

    pub fn lock(&self) -> EResult<()> {
        loop {
            if self.try_lock()? {
                return EResult::Ok(());
            }

            self.wait()?;
        }
    }

    fn wait(&self) -> EResult<()> {
        unsafe {
            yggdrasil_rt::sys::mutex(&self.__state, &MutexOperation::WaitUntilEqual(0, None))
        }?;
        EResult::Ok(())
    }

    pub unsafe fn release(&self) -> EResult<()> {
        match self.__mode {
            PTHREAD_MUTEX_NORMAL => {
                if self.__state.swap(0, Ordering::Release) == Self::LOCKED {
                    yggdrasil_rt::sys::mutex(&self.__state, &MutexOperation::Wake(1)).ok();
                    // TODO maybe yield for a fairer mutex?
                }
                EResult::Ok(())
            }
            PTHREAD_MUTEX_RECURSIVE => {
                if self.__recursion.fetch_sub(1, Ordering::Relaxed) == 1 {
                    self.__state.store(0, Ordering::Release);
                    yggdrasil_rt::sys::mutex(&self.__state, &MutexOperation::Wake(1)).ok();
                }
                EResult::Ok(())
            }
            _ => EResult::Err(Errno::EINVAL),
        }
    }
}

impl Default for pthread_mutexattr_t {
    fn default() -> Self {
        Self {
            __mode: PTHREAD_MUTEX_DEFAULT,
        }
    }
}

#[no_mangle]
unsafe extern "C" fn pthread_mutex_consistent(_mutex: *mut pthread_mutex_t) -> c_int {
    log::error!("TODO: pthread_mutex_consistent()");
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutex_destroy(_mutex: *mut pthread_mutex_t) -> c_int {
    log::debug!("TODO: pthread_mutex_destroy()");
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutex_getprioceiling(
    _mutex: *const pthread_mutex_t,
    maxprio: *mut c_int,
) -> c_int {
    log::error!("TODO: pthread_mutex_getprioceiling()");
    if let Some(maxprio) = NonNull::new(maxprio) {
        maxprio.write(10);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutex_init(
    mutex: *mut pthread_mutex_t,
    attr: *const pthread_mutexattr_t,
) -> CIntZeroResult {
    let mutex = mutex.ensure_mut();
    match attr.as_ref() {
        Some(attr) => {
            // TODO validate mode
            mutex.__mode = attr.__mode;
        }
        None => {
            mutex.__mode = PTHREAD_MUTEX_NORMAL;
        }
    }
    mutex.__state.store(0, Ordering::Release);
    mutex.__recursion.store(0, Ordering::Release);
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_mutex_lock(mutex: *mut pthread_mutex_t) -> CIntZeroResult {
    let mutex = mutex.ensure_mut();
    mutex.lock()?;
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_mutex_setprioceiling(
    _mutex: *mut pthread_mutex_t,
    new: c_int,
    old: *mut c_int,
) -> c_int {
    log::error!("TODO: pthread_mutex_setprioceiling()");
    if let Some(old) = NonNull::new(old) {
        old.write(new);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutex_timedlock(
    _mutex: *mut pthread_mutex_t,
    _timeout: *const __ygg_timespec_t,
) -> c_int {
    todo!("pthread_mutex_timedlock()")
}

#[no_mangle]
unsafe extern "C" fn pthread_mutex_trylock(mutex: *mut pthread_mutex_t) -> CIntZeroResult {
    let mutex = mutex.ensure_mut();
    mutex.try_lock_err()?;
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_mutex_unlock(mutex: *mut pthread_mutex_t) -> CIntZeroResult {
    let mutex = mutex.ensure_mut();
    mutex.release()?;
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_destroy(_attr: *mut pthread_mutexattr_t) -> c_int {
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_getprioceiling(
    _attr: *const pthread_mutexattr_t,
    maxprio: *mut c_int,
) -> c_int {
    log::error!("TODO: pthread_mutexattr_getprioceiling()");
    if let Some(maxprio) = NonNull::new(maxprio) {
        maxprio.write(10);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_getprotocol(
    _attr: *const pthread_mutexattr_t,
    protocol: *mut c_int,
) -> c_int {
    log::error!("TODO: pthread_mutexattr_getprotocol()");
    if let Some(protocol) = NonNull::new(protocol) {
        protocol.write(PTHREAD_PRIO_NONE);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_getpshared(
    _attr: *const pthread_mutexattr_t,
    shared: *mut c_int,
) -> c_int {
    if let Some(shared) = NonNull::new(shared) {
        shared.write(PTHREAD_PROCESS_PRIVATE);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_getrobust(
    _attr: *const pthread_mutexattr_t,
    robust: *mut c_int,
) -> c_int {
    log::error!("TODO: pthread_mutexattr_getrobust()");
    if let Some(robust) = NonNull::new(robust) {
        robust.write(PTHREAD_MUTEX_STALLED);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_gettype(
    attr: *const pthread_mutexattr_t,
    ty: *mut c_int,
) -> c_int {
    let attr = attr.ensure();
    if let Some(ty) = NonNull::new(ty) {
        ty.write(attr.__mode);
    }
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_init(attr: *mut pthread_mutexattr_t) -> c_int {
    let attr = attr.ensure_mut();
    *attr = pthread_mutexattr_t::default();
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_setprioceiling(
    _attr: *mut pthread_mutexattr_t,
    _maxprio: c_int,
) -> c_int {
    log::error!("TODO: pthread_mutexattr_setprioceiling()");
    0
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_setprotocol(
    _attr: *mut pthread_mutexattr_t,
    protocol: c_int,
) -> CIntZeroResult {
    if protocol == PTHREAD_PRIO_NONE {
        CIntZeroResult::SUCCESS
    } else {
        log::error!("TODO: pthread_mutexattr_setprotocol()");
        error::set_errno(Errno::EINVAL);
        CIntZeroResult::ERROR
    }
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_setpshared(
    _attr: *mut pthread_mutexattr_t,
    shared: c_int,
) -> CIntZeroResult {
    if shared == PTHREAD_PROCESS_PRIVATE {
        CIntZeroResult::SUCCESS
    } else {
        log::error!("TODO: pthread_mutexattr_setpshared()");
        error::set_errno(Errno::EINVAL);
        CIntZeroResult::ERROR
    }
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_setrobust(
    _attr: *mut pthread_mutexattr_t,
    robust: c_int,
) -> CIntZeroResult {
    if robust == PTHREAD_MUTEX_STALLED {
        CIntZeroResult::SUCCESS
    } else {
        log::error!("TODO: pthread_mutexattr_setrobust()");
        error::set_errno(Errno::EINVAL);
        CIntZeroResult::ERROR
    }
}

#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_settype(
    attr: *mut pthread_mutexattr_t,
    ty: c_int,
) -> CIntZeroResult {
    let attr = attr.ensure_mut();
    match ty {
        PTHREAD_MUTEX_NORMAL | PTHREAD_MUTEX_RECURSIVE => {
            attr.__mode = ty;
            CIntZeroResult::SUCCESS
        }
        _ => {
            error::set_errno(Errno::EINVAL);
            CIntZeroResult::ERROR
        }
    }
}
