use core::{
    ffi::{c_char, c_int, c_long, c_void},
    time::Duration,
};

use yggdrasil_abi::time::MICROSECONDS_IN_SECOND;
use yggdrasil_rt::time::SystemTime;

use super::sys_types::{suseconds_t, time_t};

#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[repr(C)]
pub struct timeval {
    pub tv_sec: time_t,
    pub tv_usec: suseconds_t,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[repr(C)]
pub struct timespec {
    pub tv_sec: time_t,
    pub tv_nsec: c_long,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[repr(C)]
pub struct itimerval {
    pub it_interval: timeval,
    pub it_value: timeval,
}

pub type __ygg_timeval_t = timeval;
pub type __ygg_timespec_t = timespec;

pub const ITIMER_REAL: c_int = 0;
pub const ITIMER_VIRTUAL: c_int = 1;
pub const ITIMER_PROF: c_int = 2;

#[no_mangle]
unsafe extern "C" fn getitimer(_timer: c_int, _value: *mut itimerval) -> c_int {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn gettimeofday(_time: *mut timeval, _d: *mut c_void) -> c_int {
    todo!()
}

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

#[no_mangle]
unsafe extern "C" fn utimes(_path: *const c_char, _times: *const timeval) -> c_int {
    todo!()
}

impl timespec {
    pub const fn zero() -> Self {
        Self {
            tv_sec: time_t(0),
            tv_nsec: 0,
        }
    }
}

impl timeval {
    pub const fn zero() -> Self {
        Self {
            tv_sec: time_t(0),
            tv_usec: 0,
        }
    }

    pub fn to_duration_opt(&self) -> Option<Duration> {
        if self.tv_sec.0 == 0 && self.tv_usec == 0 {
            None
        } else {
            let seconds =
                (self.tv_sec.0 as u64).saturating_add(self.tv_usec / MICROSECONDS_IN_SECOND);
            let nanoseconds = (self.tv_usec % MICROSECONDS_IN_SECOND) * 1000;
            Some(Duration::new(seconds, nanoseconds as u32))
        }
    }
}

impl From<__ygg_timespec_t> for Duration {
    fn from(value: __ygg_timespec_t) -> Self {
        Self::new(
            value.tv_sec.0.try_into().unwrap(),
            value.tv_nsec.try_into().unwrap(),
        )
    }
}

impl From<SystemTime> for __ygg_timespec_t {
    fn from(value: SystemTime) -> Self {
        Self {
            tv_sec: time_t(value.seconds() as i64),
            tv_nsec: value.subsec_nanos() as i64,
        }
    }
}

impl From<Duration> for __ygg_timespec_t {
    fn from(value: Duration) -> Self {
        Self {
            tv_sec: time_t(value.as_secs().try_into().unwrap()),
            tv_nsec: value.subsec_nanos().try_into().unwrap(),
        }
    }
}

impl From<Duration> for timeval {
    fn from(value: Duration) -> Self {
        Self {
            tv_sec: time_t(value.as_secs().try_into().unwrap()),
            tv_usec: value.subsec_micros() as _,
        }
    }
}
