//! Auth message
//!
//! This message is being used to authenticate the client to the relayer, it
//! works by giving a random challenge to the client, that must be returned
//! signed by the client
use crate::common::SerializeDeserialize;
use rand::{distributions::Alphanumeric, Rng};
use serde_json::Value;
use std::{collections::VecDeque, ops::Deref};

/// This is how a relayer sends an authentication challenge to the client. The
/// challenge must be returned signed by the client
#[derive(Clone, Debug)]
pub struct Auth(pub String);

impl Default for Auth {
    fn default() -> Self {
        Self(
            rand::thread_rng()
                .sample_iter(&Alphanumeric)
                .take(64)
                .map(char::from)
                .collect(),
        )
    }
}

impl Deref for Auth {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl SerializeDeserialize for Auth {
    fn get_tag() -> &'static str {
        "AUTH"
    }

    fn serialize(&self) -> Result<Vec<Value>, String> {
        Ok(vec![
            Value::String(Self::get_tag().to_owned()),
            Value::String((*self.0).to_owned()),
        ])
    }

    fn deserialize(args: VecDeque<Value>) -> Result<Self, String> {
        if args.is_empty() {
            return Err("Invalid length".to_owned());
        }
        let challenge = args[0]
            .as_str()
            .ok_or_else(|| "Invalid message, expected string".to_owned())?;
        Ok(Auth(challenge.to_owned()))
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::Response;

    #[test]
    fn parse() {
        let json = r#"["AUTH", "test"]"#;
        let message: Response = serde_json::from_str(json).expect("message");
        assert!(message.as_auth().is_some());
    }

    #[test]
    fn serialize() {
        let auth = Auth("test".to_owned());
        let m: Response = auth.into();
        assert_eq!(
            r#"["AUTH","test"]"#,
            serde_json::to_string(&m).expect("valid json")
        );
    }
}
