//! Request Message
//!
//! All messages that clients can send to relayers are abstracted in this mod
use crate::{client, common::SerializeDeserialize, types};
use serde::{
    de::{self, Deserializer},
    ser::{self, SerializeSeq, Serializer},
    Deserialize,
};
use std::collections::VecDeque;

custom_derive! {
#[derive(Debug, Clone, EnumFromInner)]
    /// Request Messages
    ///
    /// All requests from clients to relayer are abstracted in this struct
    pub enum Request {
        /// Close a subscription
        Close(client::Close),
        /// An Event from a client.  This is when a client wants to publish an
        /// event. The event must be signed.
        Event(client::Event),
        /// Creates a subscription. Client tells the relayer which kind of events
        /// they'd like to hear about
        Request(client::Subscribe),
    }
}

impl Request {
    /// Returns the current message as a Request, if possible
    pub fn as_request(&self) -> Option<&client::Subscribe> {
        match self {
            Self::Request(x) => Some(x),
            _ => None,
        }
    }

    /// Returns the current message as a close if possible
    pub fn as_close(&self) -> Option<&client::Close> {
        match self {
            Self::Close(t) => Some(t),
            _ => None,
        }
    }

    /// Returns the current message as an event from the client, if possible
    pub fn as_event(&self) -> Option<&types::Event> {
        match self {
            Self::Event(event) => Some(&**event),
            _ => None,
        }
    }
}

impl From<types::Event> for Request {
    fn from(event: types::Event) -> Self {
        Self::Event(event.into())
    }
}

impl ser::Serialize for Request {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let values = match self {
            Self::Close(t) => t.serialize(),
            Self::Event(t) => t.serialize(),
            Self::Request(t) => t.serialize(),
        }
        .map_err(ser::Error::custom)?;

        let mut seq = serializer.serialize_seq(Some(values.len()))?;
        for value in values.iter() {
            seq.serialize_element(value)?;
        }
        seq.end()
    }
}

impl<'de> de::Deserialize<'de> for Request {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let mut array: VecDeque<serde_json::Value> = Deserialize::deserialize(deserializer)?;
        if array.is_empty() {
            return Err(de::Error::custom(
                "Invalid array length, expecting at least one",
            ));
        }

        let tag = array
            .remove(0)
            .ok_or_else(|| de::Error::custom("Invalid array length, expecting at least one"))?;

        let tag = tag
            .as_str()
            .ok_or_else(|| de::Error::custom("Invalid type for element 0 of the array"))?;

        match tag {
            "EVENT" => Ok(client::Event::deserialize(array)
                .map_err(de::Error::custom)?
                .into()),
            "CLOSE" => Ok(client::Close::deserialize(array)
                .map_err(de::Error::custom)?
                .into()),
            "REQ" => Ok(client::Subscribe::deserialize(array)
                .map_err(de::Error::custom)?
                .into()),
            tag => Err(de::Error::custom(format!("{} is not a support tag", tag))),
        }
    }
}

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

    #[test]
    fn unsupported() {
        let json = "[\"CLOSEX\", \"foo\"]";
        let message: Result<Request, _> = serde_json::from_str(json);
        assert!(message.is_err());
    }

    #[test]
    fn close() {
        let json = "[\"CLOSE\", \"foo\"]";
        let message: Request = serde_json::from_str(json).expect("valid message");
        let subscription_id = message.as_close().expect("valid subscription_id");

        assert_eq!("foo".to_owned(), subscription_id.to_string());
    }
}
