use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::path::Path;

use serde_derive::Deserialize;

#[derive(Debug, Deserialize)]
pub struct Account {
    #[serde(deserialize_with = "deserialize_jid")]
    pub jid: xmpp_parsers::Jid,
    pub password: String,
    #[serde(default, deserialize_with = "deserialize_jid_map")]
    pub chatrooms: HashMap<String, xmpp_parsers::Jid>,
    pub ping: Option<u64>,
}

#[derive(Debug, Deserialize)]
pub struct Config {
    pub account: Account,
    pub http: std::net::SocketAddr,
}

impl Config {
    pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        use std::io::Read;

        let mut f = File::open(path)?;
        let mut buffer = String::new();
        f.read_to_string(&mut buffer)?;
        Ok(toml::from_str(&buffer).unwrap())
    }
}

fn deserialize_jid<'de, D>(deserializer: D) -> Result<xmpp_parsers::Jid, D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::Deserialize;
    let s = String::deserialize(deserializer)?;
    std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom)
}

fn deserialize_jid_map<'de, D>(
    deserializer: D,
) -> Result<HashMap<String, xmpp_parsers::Jid>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::Deserialize;
    let s = HashMap::<String, String>::deserialize(deserializer)?;

    let size = s.len();
    s.into_iter()
        .map(|(k, v)| (k, std::str::FromStr::from_str(&v)))
        .take_while(|(_k, r)| r.is_ok())
        .fold(Ok(HashMap::with_capacity(size)), |res, (k, r)| match res {
            Ok(mut res) => match r {
                Ok(v) => {
                    res.insert(k, v);
                    Ok(res)
                }
                Err(e) => Err(e),
            },
            Err(e) => Err(e),
        })
        .map_err(serde::de::Error::custom)
}