#![allow(
    clippy::arithmetic_side_effects,
    reason = "tests are allowed have arithmetic_side_effects"
)]

use chrono::{DateTime, NaiveDateTime, TimeZone as _, Utc};

use crate::test::ApproxEq;

impl ApproxEq for DateTime<Utc> {
    /// Use a tolerance of seconds.
    type Tolerance = i64;

    fn default_tolerance() -> Self::Tolerance {
        2
    }

    fn approx_eq(&self, other: &Self) -> bool {
        self.approx_eq_tolerance(other, Self::default_tolerance())
    }

    fn approx_eq_tolerance(&self, other: &Self, tolerance: i64) -> bool {
        let diff = *self - *other;
        diff.num_seconds().abs() <= tolerance
    }
}

/// Deserialize an OCPI date as string into a `DateTime<Utc>`.
pub fn deser_to_utc<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::Deserialize as _;

    let date_string = String::deserialize(deserializer)?;

    // First try parsing with a timezone, if that doesn't work try to parse without
    let err = match date_string.parse::<DateTime<Utc>>() {
        Ok(date) => return Ok(date),
        Err(err) => err,
    };

    if let Ok(date) = date_string.parse::<NaiveDateTime>() {
        Ok(Utc.from_utc_datetime(&date))
    } else {
        Err(serde::de::Error::custom(err))
    }
}
