//! # Nostr Protocol Types. Request
//!
//! Used to request events and subscribe to new updates.
use crate::{common::SerializeDeserialize, types};
use serde_json::Value;
use std::collections::VecDeque;

/// Request: used to request events and subscribe to new updates.
///
/// More details at https://github.com/nostr-protocol/nips/blob/master/01.md#communication-between-clients-and-relays
#[derive(Debug, Clone)]
pub struct Subscribe {
    /// The new subscription ID. If not provided, a default one will be created
    /// automatically
    pub subscription_id: types::SubscriptionId,
    /// List of filters to requests events and subscribe to new updates
    /// Events that match any of the filters are to be returned, i.e., multiple filters are to be interpreted as || conditions.
    pub filters: Vec<types::Filter>,
}

impl From<types::Filter> for Subscribe {
    fn from(filter: types::Filter) -> Self {
        Self {
            filters: vec![filter],
            ..Default::default()
        }
    }
}

impl From<Vec<types::Filter>> for Subscribe {
    fn from(filters: Vec<types::Filter>) -> Self {
        Self {
            filters,
            ..Default::default()
        }
    }
}

impl Default for Subscribe {
    fn default() -> Self {
        Self {
            subscription_id: types::SubscriptionId::default(),
            filters: vec![types::Filter::default()],
        }
    }
}

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

    fn serialize(&self) -> Result<Vec<Value>, String> {
        let mut r = vec![
            Value::String(Self::get_tag().to_owned()),
            Value::String((*self.subscription_id).to_owned()),
        ];

        for element in self.filters.iter() {
            r.push(serde_json::to_value(element).map_err(|e| e.to_string())?);
        }

        Ok(r)
    }

    fn deserialize(mut array: VecDeque<Value>) -> Result<Self, String>
    where
        Self: Sized,
    {
        let subscription_id = array
            .pop_front()
            .ok_or("Missing element 1 in the array")?
            .as_str()
            .ok_or("Invalid type for element 1, expecting a string")?
            .try_into()
            .map_err(|e: types::subscription_id::Error| {
                format!("Invalid subscription id: {}", e)
            })?;

        Ok(Subscribe {
            subscription_id,
            filters: if !array.is_empty() {
                serde_json::from_value::<Vec<types::Filter>>(serde_json::Value::Array(
                    array.into_iter().collect::<Vec<serde_json::Value>>(),
                ))
                .map_err(|e: serde_json::Error| e.to_string())?
            } else {
                vec![]
            },
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::Request as ClientRequest;
    use chrono::Utc;

    #[test]
    fn encoding_decoding() {
        let r: ClientRequest = Subscribe {
            filters: vec![
                types::Filter {
                    authors: vec![
                        "npub1h0h2zfcp3z7acvc9l0qvpgfqznkalj77whsdkmmlwanfssys3ngsx336a8"
                            .try_into()
                            .expect("some addr object"),
                    ],
                    until: Some(Utc::now()),
                    limit: 30,
                    ..Default::default()
                },
                types::Filter {
                    limit: 20,
                    ..Default::default()
                },
            ],
            ..Default::default()
        }
        .into();

        let serialized = serde_json::to_string(&r).expect("valid json string");
        let obj: ClientRequest = serde_json::from_str(&serialized).expect("valid request");
        let obj = obj.as_request().expect("request");
        let r = r.as_request().expect("request");
        assert_eq!(r.subscription_id, obj.subscription_id);
        assert_eq!(r.filters.len(), obj.filters.len());
    }
}
