use chrono::{DateTime, NaiveDate, Utc};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StatusResponse {
    Ok,
    Error,
}

impl StatusResponse {
    pub fn as_str(&self) -> &str {
        match self {
            StatusResponse::Ok => "ok",
            StatusResponse::Error => "error",
        }
    }
}

impl Serialize for StatusResponse {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.as_str().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for StatusResponse {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        match s.to_lowercase().as_str() {
            "ok" => Ok(StatusResponse::Ok),
            "error" => Ok(StatusResponse::Error),
            _ => Err(serde::de::Error::custom(format!("Invalid status: {}", s))),
        }
    }
}

impl Display for StatusResponse {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateDecisionListEntryResponse {
    pub status: StatusResponse,
    pub uri: String,
    pub created_at: DateTime<Utc>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub expires_at: Option<NaiveDate>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_status_response_serialization() {
        assert_eq!(
            serde_json::to_value(StatusResponse::Ok).unwrap(),
            json!("ok")
        );
        assert_eq!(
            serde_json::to_value(StatusResponse::Error).unwrap(),
            json!("error")
        );
    }

    #[test]
    fn test_status_response_deserialization() {
        let ok_variants = vec!["ok", "OK", "Ok", "oK"];
        for variant in ok_variants {
            let status: StatusResponse =
                serde_json::from_value(json!(variant)).expect("Should deserialize ok");
            assert_eq!(status, StatusResponse::Ok);
        }

        let error_variants = vec!["error", "ERROR", "Error"];
        for variant in error_variants {
            let status: StatusResponse =
                serde_json::from_value(json!(variant)).expect("Should deserialize error");
            assert_eq!(status, StatusResponse::Error);
        }
    }
}
