use crate::{IndexMap, Span, Type};
use alloc::string::{String, ToString};
use id_arena::{Arena, Id};
use semver::Version;
use serde::ser::{SerializeMap, SerializeSeq, Serializer};
use serde::{Deserialize, Serialize, de::Error};

pub fn serialize_none<S>(serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serializer.serialize_none()
}

pub fn serialize_arena<T, S>(arena: &Arena<T>, serializer: S) -> Result<S::Ok, S::Error>
where
    T: Serialize,
    S: Serializer,
{
    let mut seq = serializer.serialize_seq(Some(arena.len()))?;
    for (_, item) in arena.iter() {
        seq.serialize_element(&item)?;
    }
    seq.end()
}

pub fn serialize_id<T, S>(id: &Id<T>, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serializer.serialize_u64(id.index() as u64)
}

/// Variant-level serialize_with for struct variants that have an id and a
/// skipped span, preserving the old newtype serialization format.
pub fn serialize_id_ignore_span<T, S>(
    id: &Id<T>,
    _span: &Span,
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serialize_id(id, serializer)
}

pub fn serialize_optional_id<T, S>(id: &Option<Id<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    match id {
        Some(id) => serialize_id(&id, serializer),
        None => serializer.serialize_none(),
    }
}

pub fn serialize_id_map<K, T, S>(map: &IndexMap<K, Id<T>>, serializer: S) -> Result<S::Ok, S::Error>
where
    K: Serialize,
    S: Serializer,
{
    let mut s = serializer.serialize_map(Some(map.len()))?;
    for (key, id) in map.iter() {
        s.serialize_entry(key, &(id.index() as u64))?;
    }
    s.end()
}

impl Serialize for Type {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Type::Bool => serializer.serialize_str("bool"),
            Type::U8 => serializer.serialize_str("u8"),
            Type::U16 => serializer.serialize_str("u16"),
            Type::U32 => serializer.serialize_str("u32"),
            Type::U64 => serializer.serialize_str("u64"),
            Type::S8 => serializer.serialize_str("s8"),
            Type::S16 => serializer.serialize_str("s16"),
            Type::S32 => serializer.serialize_str("s32"),
            Type::S64 => serializer.serialize_str("s64"),
            Type::F32 => serializer.serialize_str("f32"),
            Type::F64 => serializer.serialize_str("f64"),
            Type::Char => serializer.serialize_str("char"),
            Type::String => serializer.serialize_str("string"),
            Type::ErrorContext => serializer.serialize_str("error-context"),
            Type::Id(type_id) => serializer.serialize_u64(type_id.index() as u64),
        }
    }
}

pub fn serialize_version<S>(version: &Version, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    version.to_string().serialize(serializer)
}

pub fn deserialize_version<'de, D>(deserializer: D) -> Result<Version, D::Error>
where
    D: serde::de::Deserializer<'de>,
{
    let version: String = String::deserialize(deserializer)?;
    version.parse().map_err(|e| D::Error::custom(e))
}

pub fn serialize_optional_version<S>(
    version: &Option<Version>,
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    version
        .as_ref()
        .map(|s| s.to_string())
        .serialize(serializer)
}

pub fn deserialize_optional_version<'de, D>(deserializer: D) -> Result<Option<Version>, D::Error>
where
    D: serde::de::Deserializer<'de>,
{
    match <Option<String>>::deserialize(deserializer)? {
        Some(version) => Ok(Some(version.parse().map_err(|e| D::Error::custom(e))?)),
        None => Ok(None),
    }
}
