use crate::constants::WorldID;
use crate::CharacterID;

use chrono::Duration;
use serde::{Deserialize, Serialize, Serializer};
use serde_json::json;

pub fn serialize_optional_bool<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    match *value {
        None => serializer.serialize_none(),
        Some(value) => match value {
            true => serializer.serialize_str("true"),
            false => serializer.serialize_str("false"),
        },
    }
}

pub fn serialize_all_subscription<S>(serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    json!(["all"]).serialize(serializer)
}

pub fn serialize_char_ids_subscription<S>(
    value: &[CharacterID],
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    let mut ids = Vec::with_capacity(value.len());
    for id in value.iter() {
        ids.push(id.to_string());
    }

    serializer.collect_seq(ids.iter())
}

pub fn serialize_world_ids_subscription<S>(
    value: &[WorldID],
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    let mut ids = Vec::with_capacity(value.len());
    for id in value.iter() {
        ids.push((*id as u8).to_string());
    }

    serializer.collect_seq(ids.iter())
}

pub fn deserialize_from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
    T: std::str::FromStr,
    T::Err: std::fmt::Display,
    D: serde::Deserializer<'de>,
{
    String::deserialize(deserializer)?
        .parse()
        .map_err(serde::de::Error::custom)
}

pub fn deserialize_duration_from_str<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let duration = String::deserialize(deserializer)?
        .parse::<i64>()
        .map_err(serde::de::Error::custom)?;

    Ok(Duration::seconds(duration))
}

pub fn serialize_duration<S>(duration: &Duration, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    s.serialize_i64(duration.num_seconds())
}

pub fn de_bool_from_str_int<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let string = String::deserialize(deserializer)?;
    let int_value = string.parse::<u8>().map_err(|_| {
        serde::de::Error::invalid_type(serde::de::Unexpected::Str(&string), &"Not an int")
    })?;

    match int_value {
        0 => Ok(false),
        1 => Ok(true),
        other => Err(serde::de::Error::invalid_value(
            serde::de::Unexpected::Unsigned(other as u64),
            &"zero or one",
        )),
    }
}
