use std::collections::HashMap;

use serde::de::Unexpected;
use serde::{de, Deserialize, Deserializer};
use serde_json::{Number, Value};

use crate::models::{FavIcon, FavIconRequest};

/// Deserialize bool from Number with custom value mapping
pub fn bool_from_number<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
    D: Deserializer<'de>,
{
    match Number::deserialize(deserializer)?.as_i64().unwrap() {
        0 => Ok(false),
        1 => Ok(true),
        other => Err(de::Error::invalid_value(
            Unexpected::Str(&other.to_string()),
            &"0 or 1",
        )),
    }
}

/// Deserialize Number or String to u64
pub fn to_u64<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
    Ok(match Value::deserialize(deserializer)? {
        Value::String(s) => s.parse().map_err(de::Error::custom)?,
        Value::Number(num) => num
            .as_u64()
            .ok_or_else(|| de::Error::custom("Invalid ID"))?,
        _ => return Err(de::Error::custom("wrong type")),
    })
}

/// Deserialize Number or String to i64
pub fn to_i64<'de, D: Deserializer<'de>>(deserializer: D) -> Result<i64, D::Error> {
    Ok(match Value::deserialize(deserializer)? {
        Value::String(s) => s.parse().map_err(de::Error::custom)?,
        Value::Number(num) => num
            .as_i64()
            .ok_or_else(|| de::Error::custom("Invalid ID"))?,
        _ => return Err(de::Error::custom("wrong type")),
    })
}

/// Deserialize Vec<ID> from String
pub fn vec_ids_from_string<'de, D>(deserializer: D) -> Result<Vec<u64>, D::Error>
where
    D: Deserializer<'de>,
{
    let str_sequence = String::deserialize(deserializer)?;
    if str_sequence.is_empty() {
        return Ok(Vec::new());
    }
    Ok(str_sequence
        .split(',')
        .map(|id| id.parse::<u64>().unwrap())
        .collect())
}

pub fn to_favicon_hashmap<'de, D>(deserializer: D) -> Result<HashMap<u64, FavIcon>, D::Error>
where
    D: Deserializer<'de>,
{
    let mut map = HashMap::new();
    for item in Vec::<FavIconRequest>::deserialize(deserializer)? {
        let tmp: Vec<&str> = item.data.split(';').collect();
        map.insert(
            item.id,
            FavIcon {
                mime_type: tmp[0].to_string(),
                data: tmp[1].to_string(),
            },
        );
    }
    Ok(map)
}
