use std::time::{Duration, SystemTime};

#[derive(
    Clone,
    Copy,
    serde::Serialize,
    serde::Deserialize,
    Debug,
    derive_more::From,
    derive_more::Mul,
    derive_more::Div,
    derive_more::MulAssign,
    derive_more::DivAssign,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    derive_more::Add,
    derive_more::Sub,
    derive_more::AddAssign,
    derive_more::SubAssign,
    Hash,
)]
#[serde(transparent)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct TimestampSec(pub u32);

#[derive(
    Clone,
    Copy,
    serde::Serialize,
    serde::Deserialize,
    Debug,
    derive_more::From,
    derive_more::Mul,
    derive_more::Div,
    derive_more::MulAssign,
    derive_more::DivAssign,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    derive_more::Add,
    derive_more::Sub,
    derive_more::AddAssign,
    derive_more::SubAssign,
    Hash,
)]
#[serde(transparent)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct TimestampMs(pub u64);

#[derive(
    Clone,
    Copy,
    serde::Serialize,
    serde::Deserialize,
    Debug,
    derive_more::From,
    derive_more::Mul,
    derive_more::Div,
    derive_more::MulAssign,
    derive_more::DivAssign,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    derive_more::Add,
    derive_more::Sub,
    derive_more::AddAssign,
    derive_more::SubAssign,
    Hash,
)]
#[serde(transparent)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct TimestampMicros(pub u64);

impl TimestampSec {
    pub const ZERO: Self = Self(0);
    pub const ONE_SEC: Self = Self(1);
    pub const ONE_MINUTE: Self = Self(60);
    pub const ONE_HOUR: Self = Self(60 * 60);
    pub const ONE_DAY: Self = Self(60 * 60 * 24);
    pub const MAX: Self = Self(u32::MAX);

    pub fn now() -> Self {
        Self(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() as u32)
    }
    pub const fn sec(&self) -> u32 {
        self.0
    }
    pub const fn ms(&self) -> u64 {
        self.sec() as u64 * 1000
    }

    pub fn elapsed(&self) -> Option<Self> {
        Self::now().0.checked_sub(self.0).map(Self)
    }
}
impl TimestampMs {
    pub const ZERO: Self = Self(0);
    pub const ONE_MS: Self = Self(1);
    pub const ONE_SEC: Self = Self(1000);
    pub const ONE_MINUTE: Self = Self(1000 * 60);
    pub const ONE_HOUR: Self = Self(1000 * 60 * 60);
    pub const ONE_DAY: Self = Self(1000 * 60 * 60 * 24);
    pub const MAX: Self = Self(u64::MAX);

    pub fn now() -> Self {
        Self(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis() as u64)
    }
    pub const fn sec(&self) -> u32 {
        (self.ms() / 1000) as u32
    }
    pub const fn ms(&self) -> u64 {
        self.0
    }

    pub fn elapsed(&self) -> Option<Self> {
        Self::now().0.checked_sub(self.0).map(Self)
    }
}
impl TimestampMicros {
    pub const ZERO: Self = Self(0);
    pub const ONE_MICROS: Self = Self(1);
    pub const ONE_MS: Self = Self(1000);
    pub const ONE_SEC: Self = Self(1000 * 1000);
    pub const ONE_MINUTE: Self = Self(1000 * 1000 * 60);
    pub const ONE_HOUR: Self = Self(1000 * 1000 * 60 * 60);
    pub const ONE_DAY: Self = Self(1000 * 1000 * 60 * 60 * 24);
    pub const MAX: Self = Self(u64::MAX);

    pub fn now() -> Self {
        Self(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_micros() as u64)
    }
    pub const fn sec(&self) -> u32 {
        (self.ms() / 1000) as u32
    }
    pub const fn ms(&self) -> u64 {
        self.0 / 1000
    }
    pub const fn micros(&self) -> u64 {
        self.0
    }

    pub fn elapsed(&self) -> Option<Self> {
        Self::now().0.checked_sub(self.0).map(Self)
    }
}

impl From<SystemTime> for TimestampMs {
    fn from(value: SystemTime) -> Self {
        Self(value.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis() as u64)
    }
}
impl From<SystemTime> for TimestampSec {
    fn from(value: SystemTime) -> Self {
        Self(value.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() as u32)
    }
}
impl From<SystemTime> for TimestampMicros {
    fn from(value: SystemTime) -> Self {
        Self(value.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_micros() as u64)
    }
}

impl From<TimestampMs> for SystemTime {
    fn from(value: TimestampMs) -> Self {
        SystemTime::UNIX_EPOCH + Duration::from_millis(value.0)
    }
}
impl From<TimestampSec> for SystemTime {
    fn from(value: TimestampSec) -> Self {
        SystemTime::UNIX_EPOCH + Duration::from_secs(value.0 as u64)
    }
}
impl From<TimestampMicros> for SystemTime {
    fn from(value: TimestampMicros) -> Self {
        SystemTime::UNIX_EPOCH + Duration::from_micros(value.0)
    }
}
