use crate::command::Command;
use chrono::serde::ts_nanoseconds;
use chrono::{DateTime, TimeDelta, Utc};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use std::time::Instant;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ValidPriorityCommand<T>
where
    T: Clone + Debug,
{
    pub inner: T,
    #[serde(with = "ts_nanoseconds")]
    pub valid_until: DateTime<Utc>,
    pub priority: u8,
}

impl<T: Clone + Debug + Command + Serialize + DeserializeOwned> Command
    for ValidPriorityCommand<T>
{
}

impl<T> ValidPriorityCommand<T>
where
    T: Clone + Debug,
{
    /// Get the valid until time as an Instant
    ///
    /// # Panics
    /// While this theoretically could panic, there are checks to prevent this.
    pub fn get_valid_until_instant(&self) -> Instant {
        let delta = self.valid_until.signed_duration_since(Utc::now());
        let now = Instant::now();
        if delta >= TimeDelta::zero() {
            // Unwrap is safe because we checked that it is not negative
            now + delta.to_std().unwrap()
        } else {
            // Unwrap is safe because we converted the negative to a positive
            now.checked_sub((-delta).to_std().unwrap()).unwrap_or(now)
        }
    }
}

impl<T: Clone + Debug + Command + Serialize + DeserializeOwned> Deref for ValidPriorityCommand<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<T: Clone + Debug + Command + Serialize + DeserializeOwned> DerefMut
    for ValidPriorityCommand<T>
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}
