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

use serde::{Deserialize, Serialize, de::Deserializer};
use thiserror::Error as ThisError;
use tokio::time::sleep;

use crate::status::{Satisfying, Status};

// TODO: wait for https://github.com/rust-lang/rust/issues/35121
// TODO: drop this and use the std::never::Never type instead
#[derive(Debug, ThisError)]
pub(crate) enum Error {
    #[allow(dead_code)]
    #[error("never")]
    Never,
}
impl PartialEq for Error {
    fn eq(&self, other: &Error) -> bool {
        format!("{self:?}") == format!("{other:?}")
    }
}

#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, rename_all = "lowercase", tag = "type")]
pub(crate) struct Fake {
    #[serde(deserialize_with = "crate::convert::into_option_duration_ms")]
    pub sleep_ms: Option<Duration>,
    #[serde(deserialize_with = "into_option_status")]
    pub status: Option<Status>,
}
impl Default for Fake {
    fn default() -> Self {
        Self {
            status: Some(Status::Satisfying(Satisfying::default())),
            sleep_ms: Some(Duration::from_millis(0)),
        }
    }
}
impl fmt::Display for Fake {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?} {:?}", &self.sleep_ms, &self.status)
    }
}
impl Fake {
    pub async fn execute(&self) -> Result {
        sleep(self.sleep_ms.unwrap_or_else(|| Duration::from_millis(0))).await;
        Ok(self
            .status
            .clone()
            .unwrap_or(Status::Satisfying(Satisfying::default())))
    }
}

pub(crate) type Result = std::result::Result<Status, Error>;

pub(crate) fn into_option_status<'de, D>(
    deserializer: D,
) -> std::result::Result<Option<Status>, D::Error>
where
    D: Deserializer<'de>,
{
    match Status::deserialize(deserializer) {
        Ok(s) => Ok(Some(s)),
        Err(e) => Err(e),
    }
}
