//! Content Kind
//!
//! This is how the client should process the content.
use super::Content;
use serde::{
    de::{self, Deserializer},
    ser::{self, Serializer},
    Deserialize,
};

/// Content Kind
///
/// Any unsupported Kind will be wrapped under the Unknown type
///
/// The Kind is represented as a u32 on the wire
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum Kind {
    /// Metadata
    ///
    /// The metadata is the profile information
    Metadata,
    /// Short text notes
    ShortTextNote,
    /// Recommend relayers where a given public key or event publishes their content
    RecommendRelayer,
    /// List of contacts a public key has
    ///
    /// This can be interpreted as the list of accounts / public key a given
    /// account follows
    Contacts,
    /// Encrypted messages
    ///
    /// The encrypted messages are encrypted with the user's public key. They
    /// can only be decrypted with the counterpart private key
    EncryptedDirectMessages,
    //// An event that should be removed.
    ///
    /// A client can request relayers to remove their own contents. A relayer
    /// may comply or not
    EventDeletion,
    /// Reposting content from another public key
    Repost,
    /// React to another event
    Reaction,
    /// Zap Request
    ZapRequest,
    /// Zap
    Zap,
    /// Relay List Metadata - NIP-65
    RelayListMetadata,
    /// Unknown Kind
    Unknown(u32),
}

impl PartialOrd for Kind {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Kind {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        let kind_id: u32 = (*self).into();
        let other_kind_id: u32 = (*other).into();
        kind_id.cmp(&other_kind_id)
    }
}

impl Kind {
    /// Is the Kind replaceable according to NIP-16
    pub fn is_replaceable(&self) -> bool {
        let kind_id: u32 = (*self).into();
        (10_000..=19_999).contains(&kind_id)
    }

    /// Is the Kind ephemeral according to NIP-16
    pub fn is_ephemeral(&self) -> bool {
        let kind_id: u32 = (*self).into();
        (20_000..=29_999).contains(&kind_id)
    }

    /// Convert the Kind into a byte representation
    pub fn into_bytes(&self) -> Vec<u8> {
        let kind_id: u32 = (*self).into();
        kind_id.to_be_bytes().to_vec()
    }
}

impl From<Kind> for u32 {
    fn from(kind: Kind) -> Self {
        match kind {
            Kind::Metadata => 0,
            Kind::ShortTextNote => 1,
            Kind::RecommendRelayer => 2,
            Kind::Contacts => 3,
            Kind::EncryptedDirectMessages => 4,
            Kind::EventDeletion => 5,
            Kind::Repost => 6,
            Kind::Reaction => 7,
            Kind::ZapRequest => 9734,
            Kind::Zap => 9735,
            Kind::RelayListMetadata => 10002,
            Kind::Unknown(t) => t,
        }
    }
}

impl From<u32> for Kind {
    fn from(kind: u32) -> Self {
        match kind {
            0 => Kind::Metadata,
            1 => Kind::ShortTextNote,
            2 => Kind::RecommendRelayer,
            3 => Kind::Contacts,
            4 => Kind::EncryptedDirectMessages,
            5 => Kind::EventDeletion,
            6 => Kind::Repost,
            7 => Kind::Reaction,
            9734 => Kind::ZapRequest,
            9735 => Kind::Zap,
            10002 => Kind::RelayListMetadata,
            any => Kind::Unknown(any),
        }
    }
}

impl ser::Serialize for Kind {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_u32((*self).into())
    }
}

impl<'de> de::Deserialize<'de> for Kind {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let raw: u32 = Deserialize::deserialize(deserializer)?;
        Ok(raw.into())
    }
}

impl From<&Content> for Kind {
    fn from(content: &Content) -> Self {
        match content {
            Content::Metadata(_) => Kind::Metadata,
            Content::ShortTextNote(_) => Kind::ShortTextNote,
            Content::RecommendRelayer(_) => Kind::RecommendRelayer,
            Content::Contacts(_) => Kind::Contacts,
            Content::EncryptedDirectMessage(_) => Kind::EncryptedDirectMessages,
            Content::EventDeletion => Kind::EventDeletion,
            Content::Repost => Kind::Repost,
            Content::Reaction(_) => Kind::Reaction,
            Content::Unparsed(kind, _) => *kind,
        }
    }
}

#[cfg(test)]
mod test {
    use crate::{
        types::{Content, Kind},
        Response,
    };

    #[test]
    fn deletion() {
        let json = r#"["EVENT","6414a63d7ddf1",{"content":"","created_at":1679052792,"id":"2f9b354e8ea7b28e1e33c585ceb19e0b6838e3580f2741fe5af6cbe0b6146566","kind":5,"pubkey":"bd4bdec9641883c492ca0062bb4d6aa8c756385980ec3e7fe2217b03184985c6","sig":"faa1c74d050f3d941049cdfb6c4e6865b90c11ce591fc2150d53be2acfc92e085dfe451d1bbcff3c88079034def131394b37448baa0a46c848017f09a00305a3","tags":[["e","fe722046568a80af561b23c27a381514575f028c908b428dd8ac065db8fcb9eb"]]}]"#;

        let msg: Response = serde_json::from_str(json).expect("message");
        assert_eq!(
            Kind::EventDeletion,
            msg.as_event().expect("event").event.inner.kind
        );
    }

    #[test]
    fn deletion_content_to_kind() {
        let content = Content::EventDeletion;
        let kind: Kind = (&content).into();
        assert_eq!(kind, Kind::EventDeletion);
    }

    #[test]
    fn reaction() {
        let json = r#"["EVENT","6414a69109d48",{"content":"🤙","created_at":1679074282,"id":"303eb43cea76695f11d3ceae9e1e89797034d04bbad4b922099c0c4087f30c58","kind":7,"pubkey":"e64b77a57752e98e2fa23d16773a182c46d92108a30f85e445604804d8543526","sig":"b6f2f26a2eecf13c077185f32cbfb73e3916484ef5745fdf37bd1025784935db18345b39048470bab0448cd52bcc8c7c2c140f895aa38ec1d17f1350e9d328d9","tags":[["e","5c24cac9fcdcb894d18c33c059dcc0eba7a5124387d1dbb6e03d2dd3411c4d40"],["p","bd8aed58fee47e64520ec250b70efd264b496bc3f8c2563c26c0b843cb1cdb53"]]}]"#;

        let msg: Response = serde_json::from_str(json).expect("message");
        assert_eq!(
            Kind::Reaction,
            msg.as_event().expect("event").event.inner.kind
        );
    }

    #[test]
    fn reaction_content_to_kind() {
        let content = Content::Reaction("🤙".to_owned());
        let kind: Kind = (&content).into();
        assert_eq!(kind, Kind::Reaction);
    }

    #[test]
    fn repost() {
        let json = r#"["EVENT","6414a4343e86b",{"content":"","created_at":1679068882,"id":"a08ec930d95860f0ffe53d95887ed30334562e31d137db0377828c1c1827245d","kind":6,"pubkey":"13bf6b8de4d3b8b0103529b4753c6dece8cc2cddc34a682dc6a8dbe6ab317876","sig":"2d1e4b83e2c0a498f14555e9bfbd2516294062079403bdda4db656d0edd57b428e948becb50ce044f28006899eb2fc444ce2e4e04bef0247f863bbabd26f87fe","tags":[["e","2aa7af268b11942c24016333a590aee738cf5bf28e4602421178bfb08759b13a"],["p","13bf6b8de4d3b8b0103529b4753c6dece8cc2cddc34a682dc6a8dbe6ab317876"]]}]"#;

        let msg: Response = serde_json::from_str(json).expect("message");
        assert_eq!(
            Kind::Repost,
            msg.as_event().expect("event").event.inner.kind
        );
    }

    #[test]
    fn repost_content_to_kind() {
        let content = Content::Repost;
        let kind: Kind = (&content).into();
        assert_eq!(kind, Kind::Repost);
    }

    #[test]
    fn is_replaceable() {
        let repleaceable: Kind = 15005.into();
        let not_repleaceable: Kind = 1.into();
        let not_repleaceable_2: Kind = 20101.into();
        assert!(repleaceable.is_replaceable());
        assert!(!not_repleaceable.is_replaceable());
        assert!(!not_repleaceable_2.is_replaceable());
    }

    #[test]
    fn is_ephemeral() {
        let ephemeral: Kind = 25005.into();
        let not_ephemeral: Kind = 1.into();
        let not_ephemeral_2: Kind = 10101.into();
        assert!(ephemeral.is_ephemeral());
        assert!(!not_ephemeral.is_ephemeral());
        assert!(!not_ephemeral_2.is_ephemeral());
    }
}
