//! Content
//!
//! The content encoding logic is defined in this mod. The encoding is
//! determined by the event kind.
use super::Kind;
use base64::{engine::general_purpose, Engine as _};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;

pub mod profile;

/// Common errors
#[derive(Error, Debug)]
pub enum Error {
    /// A JSON encoding/decoding error
    #[error("JSON: {0}")]
    Json(#[from] serde_json::Error),

    /// A Base64 encoding/decoding error
    #[error("Base64: {0}")]
    Base64(#[from] base64::DecodeError),
}

/// Relay settings
#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
pub struct RelaySettings {
    /// Can this relay be used to read/subscribe to updates?
    pub read: bool,
    /// Can this relay be used to publish new events
    pub write: bool,
}

/// Encrypted message
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct EncryptedData {
    /// Encrypted data, in raw bytes
    pub encrypted_message: Vec<u8>,
    /// The Initialization vector for the encrypted message
    pub iv: Vec<u8>,
}

/// Event content
#[derive(Eq, PartialEq, Clone, Debug)]
pub enum Content {
    /// Metadata
    ///
    /// The metadata is the profile information
    Metadata(profile::Profile),
    /// Short text notes
    ShortTextNote(String),
    /// Recommend relayers where a given public key or event publishes their content
    RecommendRelayer(String),
    /// List of contacts a public key has
    ///
    /// This can be interpreted as the list of accounts / public key a given
    /// account follows
    Contacts(HashMap<String, RelaySettings>),
    /// Encrypted messages
    ///
    /// The encrypted messages are encrypted with the user's public key. They
    /// can only be decrypted with the counterpart private key
    EncryptedDirectMessage(EncryptedData),
    //// 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(String),
    /// Unknown event
    Unparsed(Kind, String),
}

impl Content {
    /// Returns the Kind for self
    pub fn kind(&self) -> Kind {
        self.into()
    }

    /// Deserializes the content with a give kind
    pub fn deserialize(kind: Kind, content: &str) -> Result<Self, Error> {
        match kind {
            Kind::Metadata => Ok(Self::Metadata(
                serde_json::from_str(content).unwrap_or_default(),
            )),
            Kind::ShortTextNote => Ok(Self::ShortTextNote(content.to_owned())),
            Kind::RecommendRelayer => Ok(Self::RecommendRelayer(content.to_owned())),
            Kind::Contacts => Ok(Self::Contacts(
                serde_json::from_str(content).unwrap_or_default(),
            )),
            Kind::EncryptedDirectMessages => {
                let parts = content.split("?iv=").collect::<Vec<&str>>();
                if parts.len() == 2 {
                    let encrypted_message = general_purpose::STANDARD.decode(parts[0])?;
                    let iv = general_purpose::STANDARD.decode(parts[1])?;

                    Ok(Self::EncryptedDirectMessage(EncryptedData {
                        encrypted_message,
                        iv,
                    }))
                } else {
                    Ok(Self::Unparsed(Kind::Unknown(4), content.to_owned()))
                }
            }
            Kind::EventDeletion => Ok(Self::EventDeletion),
            Kind::Repost => Ok(Self::Repost),
            Kind::Reaction => Ok(Self::Reaction(content.to_owned())),
            kind => Ok(Self::Unparsed(kind, content.to_owned())),
        }
    }

    /// Serializes the content to string
    ///
    /// This should be used only when raw_content is not available. This should be
    /// used once, before signing to populate raw_content in the Event struct
    pub fn try_to_string(&self) -> Result<String, Error> {
        match self {
            Self::Metadata(p) => Ok(serde_json::to_string(&p)?),
            Self::Contacts(p) => Ok(serde_json::to_string(&p)?),
            Self::EncryptedDirectMessage(message) => {
                let encrypted_message =
                    general_purpose::STANDARD.encode(&message.encrypted_message);
                let iv = general_purpose::STANDARD.encode(&message.iv);
                let to_encode = format!("{}?iv={}", encrypted_message, iv);
                Ok(serde_json::to_string(&to_encode)?)
            }
            Self::ShortTextNote(t)
            | Self::RecommendRelayer(t)
            | Self::Unparsed(_, t)
            | Self::Reaction(t) => Ok(t.to_owned()),
            Self::EventDeletion | Self::Repost => Ok("".to_owned()),
        }
    }
}
