use core::{
    convert::Infallible,
    ffi::c_int,
    ops::{ControlFlow, FromResidual, Try},
    ptr::{null_mut, NonNull},
};

use yggdrasil_rt::io::RawFd;

use crate::headers::{errno::Errno, sys_types::off_t};

macro impl_from_residual($($ty:ty),+) {
    $(
        impl FromResidual<EResult<Infallible>> for $ty {
            fn from_residual(residual: EResult<Infallible>) -> Self {
                let err = residual.unwrap_err();
                crate::error::set_errno(err);
                Self::ERROR
            }
        }
    )+
}

#[thread_local]
#[no_mangle]
#[allow(non_upper_case_globals)]
static mut __errno: Errno = Errno::ESUCCESS;

pub trait ResultExt<T, E> {
    fn e_map_err<F: FnOnce(E) -> Errno>(self, map: F) -> EResult<T>;
}

pub trait OptionExt<T> {
    fn e_ok_or(self, err: Errno) -> EResult<T>;
}

pub trait CResult {
    const ERROR: Self;
    type Inner;
    fn into_inner(self) -> Self::Inner;
}

#[must_use = "EResult must be converted into its output type with proper errno set"]
#[derive(Debug)]
pub enum EResult<T> {
    Ok(T),
    Err(Errno),
}

/// On success: non-null ptr
/// On failure: NULL
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CPtrResult<T>(*mut T);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CIsizeResult(isize);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CIntZeroResult(c_int);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CEofResult(c_int);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CIntCountResult(c_int);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CFdResult(c_int);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct COffsetResult(pub(crate) off_t);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CUsizeResult(usize);

pub trait TryFromExt<T>: Sized {
    fn e_try_from(value: T) -> EResult<Self>;
}

impl<T> EResult<T> {
    // pub fn is_ok(&self) -> bool {
    //     matches!(self, Self::Ok(_))
    // }

    pub fn is_err(&self) -> bool {
        matches!(self, Self::Err(_))
    }

    pub fn ok(self) -> Option<T> {
        match self {
            Self::Ok(res) => Some(res),
            Self::Err(_) => None,
        }
    }

    #[track_caller]
    pub fn expect(self, message: &str) -> T {
        match self {
            Self::Ok(value) => value,
            Self::Err(err) => {
                panic!("expect() failed: {}, errno={:?}", message, err)
            }
        }
    }

    pub fn map<U, F: FnOnce(T) -> U>(self, map: F) -> EResult<U> {
        match self {
            Self::Ok(value) => EResult::Ok(map(value)),
            Self::Err(err) => EResult::Err(err),
        }
    }

    pub fn unwrap_err(self) -> Errno {
        match self {
            Self::Ok(_) => panic!("Expected an error"),
            Self::Err(err) => err,
        }
    }

    pub fn into_result<E, F: FnOnce(Errno) -> E>(
        self,
        map_err: F,
        set_errno: bool,
    ) -> Result<T, E> {
        match self {
            Self::Ok(value) => Ok(value),
            Self::Err(err) => {
                if set_errno {
                    self::set_errno(err);
                }
                Err(map_err(err))
            }
        }
    }
}

impl<T, E> ResultExt<T, E> for Result<T, E> {
    fn e_map_err<F: FnOnce(E) -> Errno>(self, map: F) -> EResult<T> {
        match self {
            Ok(value) => EResult::Ok(value),
            Err(value) => EResult::Err(map(value)),
        }
    }
}

impl<T> OptionExt<T> for Option<T> {
    fn e_ok_or(self, err: Errno) -> EResult<T> {
        match self {
            Some(value) => EResult::Ok(value),
            None => EResult::Err(err),
        }
    }
}

impl<T> Try for EResult<T> {
    type Output = T;
    type Residual = EResult<Infallible>;

    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
        match self {
            Self::Ok(value) => ControlFlow::Continue(value),
            Self::Err(err) => ControlFlow::Break(EResult::Err(err)),
        }
    }

    fn from_output(output: Self::Output) -> Self {
        Self::Ok(output)
    }
}

impl<T> FromResidual<EResult<Infallible>> for EResult<T> {
    fn from_residual(residual: EResult<Infallible>) -> Self {
        let err = residual.unwrap_err();
        Self::Err(err)
    }
}

impl<T, E: Into<Errno>> FromResidual<Result<Infallible, E>> for EResult<T> {
    fn from_residual(residual: Result<Infallible, E>) -> Self {
        let err = residual.unwrap_err();
        Self::Err(err.into())
    }
}

impl<T, E: From<Errno>> FromResidual<EResult<Infallible>> for Result<T, E> {
    fn from_residual(residual: EResult<Infallible>) -> Self {
        let err = residual.unwrap_err();
        Self::Err(err.into())
    }
}

impl<T> CPtrResult<T> {
    pub const fn success(ptr: NonNull<T>) -> Self {
        Self(ptr.as_ptr())
    }
}

impl<T> CResult for CPtrResult<T> {
    const ERROR: Self = Self(null_mut());
    type Inner = *mut T;

    fn into_inner(self) -> Self::Inner {
        self.0
    }
}

impl<T> FromResidual<EResult<Infallible>> for CPtrResult<T> {
    fn from_residual(residual: EResult<Infallible>) -> Self {
        let err = residual.unwrap_err();
        set_errno(err);
        Self::ERROR
    }
}

impl CIsizeResult {
    pub const fn success(value: usize) -> Self {
        Self(value as _)
    }
}

impl CResult for CIsizeResult {
    const ERROR: Self = Self(-1);
    type Inner = isize;

    fn into_inner(self) -> Self::Inner {
        self.0
    }
}

impl CIntZeroResult {
    pub const SUCCESS: Self = Self(0);
}

impl CResult for CIntZeroResult {
    const ERROR: Self = Self(-1);
    type Inner = c_int;

    fn into_inner(self) -> Self::Inner {
        self.0
    }
}

impl CEofResult {
    pub fn success(value: c_int) -> Self {
        Self(value)
    }
}

impl CResult for CEofResult {
    const ERROR: Self = Self(-1);
    type Inner = c_int;

    fn into_inner(self) -> Self::Inner {
        self.0
    }
}

impl CIntCountResult {
    pub fn success(value: c_int) -> Self {
        Self(value)
    }
}

impl CResult for CIntCountResult {
    const ERROR: Self = Self(-1);
    type Inner = c_int;

    fn into_inner(self) -> Self::Inner {
        self.0
    }
}

impl CFdResult {
    pub fn success(value: RawFd) -> Self {
        Self(value.into_raw().try_into().unwrap())
    }
}

impl CResult for CFdResult {
    const ERROR: Self = Self(-1);
    type Inner = c_int;

    fn into_inner(self) -> Self::Inner {
        self.0
    }
}

impl COffsetResult {
    pub fn success(value: u64) -> Self {
        Self(value.try_into().unwrap())
    }
}

impl CResult for COffsetResult {
    const ERROR: Self = Self(-1);
    type Inner = off_t;

    fn into_inner(self) -> Self::Inner {
        self.0
    }
}

impl CUsizeResult {
    pub fn success(value: usize) -> Self {
        Self(value)
    }
}

impl CResult for CUsizeResult {
    const ERROR: Self = Self(0);
    type Inner = usize;

    fn into_inner(self) -> Self::Inner {
        self.0
    }
}

impl_from_residual!(
    CIsizeResult,
    CIntZeroResult,
    CEofResult,
    CIntCountResult,
    CFdResult,
    COffsetResult,
    CUsizeResult
);

pub fn set_errno(value: Errno) {
    unsafe { __errno = value };
}

pub fn get_errno() -> Errno {
    unsafe { __errno }
}
