//! Event type
//!
//! This is the core type of the protocol. All events must be signed to be valid
//! and broadcasted by relayers
use super::{content, Content, Id, Kind, Signature};
use chrono::{DateTime, Utc};
use secp256k1::{schnorr, Message, XOnlyPublicKey};
use serde::{
    de::{self, Deserializer},
    Deserialize, Serialize,
};
use serde_json::json;
use sha2::{Digest, Sha256};
use thiserror::Error;

/// Errors
#[derive(Error, Debug)]
pub enum Error {
    /// JSON serialization errors
    #[error("Error serializing object: {0}")]
    Serialize(#[from] serde_json::Error),

    /// Error serializing an ID
    #[error("Error serializing id: {0}")]
    Id(#[from] hex::FromHexError),

    /// Error in the signature
    #[error("Signature error: {0}")]
    Signature(#[from] secp256k1::Error),

    /// The provided ID is not valid, the ID is the hash of the content, as
    /// described in NIP-01
    #[error("Provided ID is not correct")]
    InvalidProvidedId,

    /// Internal Error
    #[error("Internal error: {0}")]
    Internal(#[from] content::Error),
}

/// This is the unsigned event.
///
/// The event must be signed, in the crate::types::Event, which wraps this
/// struct, but all the content of the event is defined here, but the signature.
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
pub struct UnsignedEvent {
    /// The public key that signs this event
    #[serde(rename = "pubkey")]
    pub public_key: Id,
    /// A timestamp when this event has been created
    #[serde(with = "super::ts_seconds")]
    pub created_at: DateTime<Utc>,
    /// Content kind
    pub kind: Kind,
    /// Tags
    pub tags: Vec<super::Tag>,
    #[serde(skip_serializing)]
    /// Content
    ///
    /// This is the parsed version of the raw_content. This is meant to be used
    /// at rust's level to read the content easily.
    pub content: Content,
    /// Raw content
    ///
    /// The raw content that is serialized into the wire and signed.
    #[serde(rename = "content")]
    pub raw_content: String,
}

impl<'de> de::Deserialize<'de> for UnsignedEvent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Deserialize)]
        struct MetadataInternal {
            #[serde(rename = "pubkey")]
            pub public_key: Id,
            #[serde(with = "super::ts_seconds")]
            pub created_at: DateTime<Utc>,
            pub kind: Kind,
            pub tags: Vec<super::Tag>,
            pub content: String,
        }
        let s: MetadataInternal = MetadataInternal::deserialize(deserializer)?;
        let content = Content::deserialize(s.kind, &s.content)
            .map_err(|d| de::Error::custom(d.to_string()))?;
        Ok(Self {
            public_key: s.public_key,
            created_at: s.created_at,
            kind: s.kind,
            tags: s.tags,
            raw_content: s.content,
            content,
        })
    }
}

impl UnsignedEvent {
    /// Creates a new unsigned event
    pub fn new(
        public_key: Id,
        tags: Vec<super::Tag>,
        content: Content,
        created_at: Option<DateTime<Utc>>,
    ) -> Result<Self, Error> {
        Ok(Self {
            public_key,
            created_at: created_at.unwrap_or_else(Utc::now),
            tags,
            kind: content.kind(),
            raw_content: content.try_to_string()?,
            content,
        })
    }

    /// Calculates the ID for this unsigned event
    ///
    /// This ID is then signed by the public key's counter part private key.
    pub fn id(&self) -> Result<Id, Error> {
        let data_to_hash = serde_json::to_string(&json!(vec![
            json!(0),
            json!(self.public_key.to_string()),
            json!(self.created_at.timestamp()),
            json!(self.content.kind()),
            json!(self.tags),
            json!(self.raw_content),
        ]))?;
        let mut hasher = Sha256::new();
        hasher.update(&data_to_hash);
        Ok(Id(hasher.finalize().into()))
    }
}

/// Signed event
///
/// This is a wrap for UnsignedEvent but it will serializes the ID and the signature.
///
/// This is the struct that is published to relayers from clients
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Event {
    /// Event Id
    ///
    /// This is calculated from the UnsignedEvent and later is used to be
    /// signed. Any minor alteration to the UnsignedEvent will generate a
    /// totally different Id and Signature, making the whole event invalid
    pub id: Id,
    /// Event content
    #[serde(flatten)]
    pub inner: UnsignedEvent,
    /// The signature that makes the inner unsigned event's ID
    #[serde(rename = "sig")]
    pub signature: Signature,
}

impl Event {
    /// Creates a new event
    ///
    /// At this stage user must provide the signature as well.
    pub fn new(data: UnsignedEvent, signature: Signature) -> Result<Self, Error> {
        let id = data.id()?;
        Self::verify_signature(&data.public_key, &signature, &id)?;

        Ok(Self {
            id,
            inner: data,
            signature,
        })
    }

    /// Returns the kind of this event
    pub fn kind(&self) -> Kind {
        self.inner.kind
    }

    /// Returns the date when this event was created
    pub fn created_at(&self) -> DateTime<Utc> {
        self.inner.created_at
    }

    /// Returns the event author
    pub fn author(&self) -> &Id {
        &self.inner.public_key
    }

    /// Checks if the event is valid
    pub fn is_valid(&self) -> Result<(), Error> {
        let calculated_id = self.inner.id()?;
        if calculated_id != self.id {
            return Err(Error::InvalidProvidedId);
        }
        Self::verify_signature(&self.inner.public_key, &self.signature, &self.id)
    }

    /// Returns list of tags for this event
    pub fn tags(&self) -> &[super::Tag] {
        &self.inner.tags
    }

    /// Returns a reference to the inner content (the parsed content)
    pub fn content(&self) -> &Content {
        &self.inner.content
    }

    /// Verifies if the Id, Public Key and Signature are correct
    fn verify_signature(
        public_key: &Id,
        signature: &Signature,
        content_id: &Id,
    ) -> Result<(), Error> {
        let public_key = XOnlyPublicKey::from_slice(&**public_key)?;
        let message = Message::from_slice(&**content_id)?;
        Ok(schnorr::Signature::from_slice(&**signature)?.verify(&message, &public_key)?)
    }
}

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

    #[test]
    fn parse_event_content() {
        let json = "{\"content\":\"{\\\"lud06\\\":\\\"lnbc1p3a4wxvpp5x0pa6gr55fq5s9d3dxs0vz77mqxgdw63hhtgtlfz5zvm65847vnqdqqcqpjsp5402c8rtqxd4j97rnvuejuwl4sg473g6wg08d67fvn7qc4gtpkfks9q7sqqqqqqqqqqqqqqqqqqqsqqqqqysgqmqz9gxqyjw5qrzjqwryaup9lh50kkranzgcdnn2fgvx390wgj5jd07rwr3vxeje0glclleasn65surjcsqqqqlgqqqqqeqqjqyxj968tem9ps6ttm9ukv6ag4yc6qmgj2svrccfgp4n83fpktr3dsx6fq7grfzlqt982aaemahg9q29vzl9f627kh4j8h8xc2z2mtpdqqjlekah\\\",\\\"website\\\":\\\"\\\",\\\"nip05\\\":\\\"cesar@cesar.com.py\\\",\\\"picture\\\":\\\"https://pbs.twimg.com/profile_images/1175432935337537536/_Peu9vuJ_400x400.jpg\\\",\\\"display_name\\\":\\\"C\\\",\\\"about\\\":\\\"Rust and PHP\\\",\\\"name\\\":\\\"c\\\"}\",\"created_at\":1678476588,\"id\":\"3800c787a23288641c0b96cbcc87c26cbd3ea7bee53b7748422fdb100fb7b9f0\",\"kind\":0,\"pubkey\":\"b2815682cfc83fcd2c3add05785cf4573dd388457069974cc6d8cca06b3c3b78\",\"sig\":\"c8a12ce96833e4cd67bce0e9e50f831262ef0f0c0cff5e56c38a0c90867ed1a6621e9692948ef5e85a7ca3726c3f0f43fa7e1992536bc457317123bca8784f5f\",\"tags\":[]}";

        let obj: Event = serde_json::from_str(json).expect("valid");
        obj.is_valid().expect("is valid");
        assert!(obj.is_valid().is_ok());
    }
}
