//! Tag mod
//!
//! An event can tag/reference another public key, or another event.
//!
//! It can also use the tag to add all sort of metadata, useful for their own
//! type
use super::Addr;
use serde::{
    de::{self, Deserialize, Deserializer},
    ser::{self, SerializeSeq, Serializer},
};

mod event;
mod public_key;

pub use self::{event::*, public_key::*};

/// Tags are how events relates to each other.
///
/// So far, there are two standard tags, Events and Public Key. This an event
/// can be related to another, with extra options (like a relayer url, and a pet
/// name).
///
/// Any non standard tag will be parsed as Unknown with a vector of strings as
/// their parameters
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Tag {
    /// Tag another event
    Event(Event),
    /// Tag another public key
    PubKey(PubKey),
    /// Any non standard tag
    Unknown(String, Vec<String>),
}

impl Tag {
    /// Is the tag a public key?
    pub fn is_pubkey(&self) -> bool {
        matches!(self, Self::PubKey(_))
    }

    /// Is the tag an event?
    pub fn is_event(&self) -> bool {
        matches!(self, Self::Event(_))
    }
}

impl ser::Serialize for Tag {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let typ = match self {
            Tag::Event(_) => "e",
            Tag::PubKey(_) => "p",
            Tag::Unknown(u, _) => u,
        };

        let mut seq = serializer.serialize_seq(Some(2))?;
        seq.serialize_element(typ)?;

        match self {
            Tag::Event(event) => {
                seq.serialize_element(&event.id.to_hex())?;
                if let Some(relayer) = &event.relayer_url {
                    seq.serialize_element(&relayer)?;
                    if let Some(marker) = &event.marker {
                        seq.serialize_element(&marker.to_string())?;
                    }
                }
            }
            Tag::PubKey(key) => {
                seq.serialize_element(&key.id.to_hex())?;
                if let Some(relayer) = &key.relayer_url {
                    seq.serialize_element(relayer)?;
                    if let Some(pet_name) = &key.pet_name {
                        seq.serialize_element(pet_name)?;
                    }
                }
            }
            Tag::Unknown(_, extra) => {
                for extra in extra.iter() {
                    seq.serialize_element(extra)?;
                }
            }
        }
        seq.end()
    }
}

impl<'de> Deserialize<'de> for Tag {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let parts: Vec<String> = Deserialize::deserialize(deserializer)?;
        if parts.len() < 2 {
            return Err(de::Error::custom("expected array of two strings"));
        }

        Ok(match parts[0].as_str() {
            "e" | "p" => {
                let id: Addr = parts[1]
                    .as_str()
                    .try_into()
                    .map_err(|e: super::addr::Error| de::Error::custom(e.to_string()))?;

                let relayer_url = parts.get(2).cloned();
                let extra = parts.get(3).cloned();

                if "e" == parts[0].as_str() {
                    Tag::Event(Event {
                        id,
                        relayer_url,
                        marker: extra.map(|x| x.as_str().into()),
                    })
                } else {
                    Tag::PubKey(PubKey {
                        id,
                        relayer_url,
                        pet_name: extra,
                    })
                }
            }
            extra => Tag::Unknown(extra.to_owned(), parts[1..].to_owned()),
        })
    }
}

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

    #[test]
    fn bug_01() {
        let json = r#"["EVENT","d45a98f898820258a3313f5cb14f5fe8a9263437931ac6309f23ae0324833f39",{"content":"Will fight under the light of lightnings! No darkness on nostr! 🐶🐾😂","created_at":1678685477,"id":"5462822032c16d32267ba40536409fd51ea188b20e7dd5f9e7a0aa5561346f79","kind":1,"pubkey":"8fb140b4e8ddef97ce4b821d247278a1a4353362623f64021484b372f948000c","sig":"62c8e09a0fedd8096a313ef12436b0f0bcad56e9058d4bc12f61ae6094c099bb966dce47efa2d721c5bfdf923614db46d1d1c248105cb99c4ec495292cc875b1","tags":[["e","5f63f9e7d37673e76ddf7448cd67c3d74f9be96c240a40199b59a30db32d7f43"],["e","c70894331986c66a2baf6fc12dd5c86280e4616cee0e57bbee90972ebbb4b735"],["p","ac3fb436a663b25893657f4b6a3d9d2f02d1974bb5ced603f4d0c8ee32d7e0a2"]]}]"#;
        let message: Result<Response, _> = serde_json::from_str(json);
        assert!(message.is_ok());
    }

    #[test]
    fn parse_unexpected_tag() {
        let json = r#"["EVENT","f4e62282fda9c7d93a6e3b03fd1f1a3a34936f74584b0e021edf9659fd7da9d6",{"content":"1678682368","created_at":1678682368,"id":"7506ccd8ce4de835c61c04fd16f8489b2acb8cc052ed6730cba203188dfedf57","kind":30000,"pubkey":"228cc1e37a8fec2eee3dda3a3dbd04a60968086d8f42751a7632499d938eda8f","sig":"73ec91ee5d23a93287700f31274a57d84f46c0aa06523138c8f5b0cf2f20bd8a7db72346ecae9c717d9642cea81d72c35ea95ec615146b1640f62de5e3fbbb69","tags":[["d","chats/null/lastOpened"]]}]"#;
        let message: Result<Response, _> = serde_json::from_str(json);
        assert!(message.is_ok());
    }

    #[test]
    fn serialize_deserialize() {
        let json = "[\"p\",\"a0b0c0d0\"]";
        let tag: Tag = serde_json::from_str(&json).expect("valid json");
        assert_eq!(
            Tag::PubKey(PubKey {
                id: Addr::try_from_public_key_str("a0b0c0d0", None).expect("valid addr"),
                relayer_url: None,
                pet_name: None,
            }),
            tag
        );
    }
}
