//! Builders for the SOP operations.

use std::{
    fmt,
    io,
    time::SystemTime,
};

use super::{
    Error,
    ParseError,
    Load,
    Password,
    Result,
    SOP,
    Save,
    SessionKey,
};

/// Builder for [`SOP::version`].
pub trait Version<'s> {
    /// Returns name and version of the SOP implementation.
    fn frontend(&self) -> Result<VersionInfo>;

    /// Returns name and version of the primary underlying OpenPGP
    /// toolkit.
    fn backend(&self) -> Result<VersionInfo>;

    /// Returns extended version information.
    ///
    /// The information has no defined structure and may contain any
    /// information deemed useful by the implementer.
    fn extended(&self) -> Result<String>;
}

/// Represents a name and version tuple.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VersionInfo {
    /// Name of the implementation, library, or additional component.
    pub name: String,
    /// Version string.
    pub version: String,
}

impl fmt::Display for VersionInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {}", self.name, self.version)
    }
}

/// Builder for [`SOP::generate_key`].
pub trait GenerateKey<'s, S: SOP<'s>, Keys: Load<'s, S> + Save> {
    /// Lists profiles for this subcommand.
    fn list_profiles(&self) -> Vec<(String, String)> {
        vec![]
    }

    /// Selects a profile for this subcommand.
    ///
    /// Valid profiles can be queried using
    /// [`GenerateKey::list_profiles`].
    fn profile(self: Box<Self>, _profile: &str)
               -> Result<Box<dyn GenerateKey<'s, S, Keys> + 's>> {
        Err(Error::UnsupportedProfile)
    }

    /// Generates signing-only keys.
    fn signing_only(self: Box<Self>) -> Box<dyn GenerateKey<'s, S, Keys> + 's>;

    /// Protects the newly generated key with the given password.
    fn with_key_password(self: Box<Self>, password: Password)
                         -> Result<Box<dyn GenerateKey<'s, S, Keys> + 's>>;

    /// Adds a User ID.
    fn userid(self: Box<Self>, userid: &str) -> Box<dyn GenerateKey<'s, S, Keys> + 's>;

    /// Generates the OpenPGP key.
    fn generate(self: Box<Self>) -> Result<Keys>;
}

/// Builder for [`SOP::change_key_password`].
pub trait ChangeKeyPassword<'s, S: SOP<'s>, Keys: Load<'s, S>> {
    /// Supplies the new password to lock the keys with.
    ///
    /// If this method is not invoked, the keys are unlocked.
    fn new_key_password(self: Box<Self>, password: Password)
                        -> Result<Box<dyn ChangeKeyPassword<'s, S, Keys> + 's>>;

    /// Supplies a password to unlock the keys with.
    ///
    /// All passwords are tried.  If unlocking a key fails, the whole
    /// operation fails with [`Error::KeyIsProtected`].
    fn old_key_password(self: Box<Self>, password: Password)
                        -> Result<Box<dyn ChangeKeyPassword<'s, S, Keys> + 's>>;

    /// Updates `keys`.
    fn keys(self: Box<Self>, keys: &Keys) -> Result<Keys>;
}

/// Builder for [`SOP::revoke_key`].
pub trait RevokeKey<'s, S: SOP<'s>, Certs: Save, Keys: Load<'s, S>> {
    /// Supplies a password to unlock the keys with.
    ///
    /// All passwords are tried.  If unlocking a key fails, the whole
    /// operation fails with [`Error::KeyIsProtected`].
    fn with_key_password(self: Box<Self>, password: Password)
                         -> Result<Box<dyn RevokeKey<'s, S, Certs, Keys> + 's>>;

    /// Revokes `keys`.
    fn keys(self: Box<Self>, keys: &Keys) -> Result<Certs>;
}

/// Builder for [`SOP::extract_cert`].
pub trait ExtractCert<'s, S: SOP<'s>, Certs: Save, Keys: Load<'s, S>> {
    /// Extracts the certs from `keys`.
    fn keys(self: Box<Self>, keys: &Keys) -> Result<Certs>;
}

/// Builder for [`SOP::update_key`].
pub trait UpdateKey<'s, S: SOP<'s>, Certs: Save, Keys: Load<'s, S>> {
    /// Don't make the updated key encryption-capable if it isn't
    /// already.
    fn signing_only(self: Box<Self>) -> Box<dyn UpdateKey<'s, S, Certs, Keys> + 's>;

    /// Don't advertise support for capabilities that aren't already
    /// advertised by the key.
    fn no_added_capabilities(self: Box<Self>)
                             -> Box<dyn UpdateKey<'s, S, Certs, Keys> + 's>;

    /// Supplies a password to unlock the keys with.
    ///
    /// All passwords are tried.  If unlocking a key fails, the whole
    /// operation fails with [`Error::KeyIsProtected`].
    fn with_key_password(self: Box<Self>, password: Password)
                         -> Result<Box<dyn UpdateKey<'s, S, Certs, Keys> + 's>>;

    /// Merges updates into the keys.
    fn merge_updates(self: Box<Self>, updates: &Certs)
                     -> Result<Box<dyn UpdateKey<'s, S, Certs, Keys> + 's>>;

    /// Updates the keys.
    fn update(self: Box<Self>, keys: &Keys) -> Result<Keys>;
}

/// Builder for [`SOP::merge_certs`].
pub trait MergeCerts<'s, S: SOP<'s>, Certs: Load<'s, S> + Save> {
    /// Merges updates into the certs.
    fn merge_updates(self: Box<Self>, updates: &Certs)
                     -> Result<Box<dyn MergeCerts<'s, S, Certs> + 's>>;

    /// Merges the certs.
    fn merge(self: Box<Self>, certs: &Certs) -> Result<Certs>;
}

/// Builder for [`SOP::certify_userid`].
pub trait CertifyUserID<'s, S: SOP<'s>, Certs: Save, Keys: Load<'s, S>> {
    /// Certifies the given User ID.
    fn userid(self: Box<Self>, userid: String)
              -> Box<dyn CertifyUserID<'s, S, Certs, Keys> + 's>;

    /// Supplies a password to unlock the keys with.
    ///
    /// All passwords are tried.  If unlocking a key fails, the whole
    /// operation fails with [`Error::KeyIsProtected`].
    fn with_key_password(self: Box<Self>, password: Password)
                         -> Result<Box<dyn CertifyUserID<'s, S, Certs, Keys> + 's>>;

    /// Don't require self-signatures on the user IDs to be
    /// certified.
    fn no_require_self_sig(self: Box<Self>)
                           -> Box<dyn CertifyUserID<'s, S, Certs, Keys> + 's>;

    /// Create certifications using these keys.
    fn keys(self: Box<Self>, keys: &Keys)
            -> Result<Box<dyn CertifyUserID<'s, S, Certs, Keys> + 's>>;

    /// Create the certifications.
    fn certify(self: Box<Self>, certs: &Certs)-> Result<Certs>;
}

/// Builder for [`SOP::validate_userid`].
pub trait ValidateUserID<'s, S: SOP<'s>, Certs: Load<'s, S>> {
    /// Supplies authority OpenPGP certificates, i.e. trust roots.
    fn trust_roots(self: Box<Self>, certs: &Certs)
                   -> Result<Box<dyn ValidateUserID<'s, S, Certs> + 's>>;

    /// Tests that these certificates have the given User ID.
    fn target_certs(self: Box<Self>, certs: &Certs)
                    -> Result<Box<dyn ValidateUserID<'s, S, Certs> + 's>>;

    /// Evaluate the validity of the User ID at the specified time,
    /// not at the current time.
    fn validate_at(self: Box<Self>, at: SystemTime)
                   -> Result<Box<dyn ValidateUserID<'s, S, Certs> + 's>>;

    /// Validates an User ID.
    fn userid(self: Box<Self>, userid: &str) -> Result<()>;

    /// Validates an email address.
    ///
    /// Matched only against the e-mail address part of each correctly
    /// bound User ID.
    fn email(self: Box<Self>, email: &str) -> Result<()>;
}

/// Builder for [`SOP::sign`].
pub trait Sign<'s, S: SOP<'s>, Keys: Load<'s, S>, Sigs: Save> {
    /// Sets signature mode.
    fn mode(self: Box<Self>, mode: SignAs)
            -> Box<dyn Sign<'s, S, Keys, Sigs> + 's>;

    /// Adds the signer keys.
    fn keys(self: Box<Self>, keys: &Keys)
            -> Result<Box<dyn Sign<'s, S, Keys, Sigs> + 's>>;

    /// Adds a password to unlock the signing keys with.
    ///
    /// All supplied passwords will be used to try to unlock all
    /// signing keys.
    fn with_key_password(self: Box<Self>, password: Password)
                         -> Result<Box<dyn Sign<'s, S, Keys, Sigs> + 's>>;

    /// Signs data.
    fn data(self: Box<Self>, data: &mut (dyn io::Read + Send + Sync))
            -> Result<(Micalg, Sigs)>;
}

/// Builder for [`SOP::verify`].
pub trait Verify<'s, S: SOP<'s>, Certs: Load<'s, S>, Sigs: Load<'s, S>> {
    /// Makes SOP consider signatures before this date invalid.
    fn not_before(self: Box<Self>, t: SystemTime)
                  -> Box<dyn Verify<'s, S, Certs, Sigs> + 's>;

    /// Makes SOP consider signatures after this date invalid.
    fn not_after(self: Box<Self>, t: SystemTime)
                 -> Box<dyn Verify<'s, S, Certs, Sigs> + 's>;

    /// Adds the verification certs.
    fn certs(self: Box<Self>, certs: &Certs)
             -> Result<Box<dyn Verify<'s, S, Certs, Sigs> + 's>>;

    /// Provides the signatures.
    fn signatures<'sigs>(self: Box<Self>, signatures: &'sigs Sigs)
                         -> Result<Box<dyn VerifySignatures + 'sigs>>
    where
        's: 'sigs;
}

/// Finalizes detached signature verification.
pub trait VerifySignatures<'sigs> {
    /// Verifies the authenticity of `data`.
    fn data(self: Box<Self>,
            data: &mut (dyn io::Read + Send + Sync))
            -> Result<Vec<Verification>>;
}

/// Builder for [`SOP::encrypt`].
pub trait Encrypt<'s, S: SOP<'s>, Certs: Load<'s, S>, Keys: Load<'s, S>> {
    /// Disables armor encoding.
    fn no_armor(self: Box<Self>) -> Box<dyn Encrypt<'s, S, Certs, Keys> + 's>;

    /// Lists profiles for this subcommand.
    fn list_profiles(&self) -> Vec<(String, String)> {
        vec![]
    }

    /// Selects a profile for this subcommand.
    ///
    /// Valid profiles can be queried using
    /// [`GenerateKey::list_profiles`].
    fn profile(self: Box<Self>, _profile: &str)
               -> Result<Box<dyn Encrypt<'s, S, Certs, Keys> + 's>> {
        Err(Error::UnsupportedProfile)
    }

    /// Sets encryption mode.
    fn mode(self: Box<Self>, mode: EncryptAs) -> Box<dyn Encrypt<'s, S, Certs, Keys> + 's>;

    /// Adds the signer keys.
    fn sign_with_keys(self: Box<Self>, keys: &Keys)
                      -> Result<Box<dyn Encrypt<'s, S, Certs, Keys> + 's>>;

    /// Adds a password to unlock the signing keys with.
    ///
    /// All supplied passwords will be used to try to unlock all
    /// signing keys.
    fn with_key_password(self: Box<Self>, password: Password)
                         -> Result<Box<dyn Encrypt<'s, S, Certs, Keys> + 's>>;

    /// Encrypts with the given password.
    fn with_password(self: Box<Self>, password: Password)
                     -> Result<Box<dyn Encrypt<'s, S, Certs, Keys> + 's>>;

    /// Encrypts with the given certs.
    fn with_certs(self: Box<Self>, certs: &Certs)
                  -> Result<Box<dyn Encrypt<'s, S, Certs, Keys> + 's>>;

    /// Encrypts the given data yielding the ciphertext.
    fn plaintext<'d>(self: Box<Self>,
                     plaintext: &'d mut (dyn io::Read + Send + Sync))
        -> Result<Box<dyn Ready<Option<SessionKey>> + 'd>>
    where
        's: 'd;
}

/// Builder for [`SOP::decrypt`].
pub trait Decrypt<'s, S: SOP<'s>, Certs: Load<'s, S>, Keys: Load<'s, S>> {
    /// Makes SOP consider signatures before this date invalid.
    fn verify_not_before(self: Box<Self>,
                         t: SystemTime)
                         -> Box<dyn Decrypt<'s, S, Certs, Keys> + 's>;

    /// Makes SOP consider signatures after this date invalid.
    fn verify_not_after(self: Box<Self>,
                        t: SystemTime)
                        -> Box<dyn Decrypt<'s, S, Certs, Keys> + 's>;

    /// Adds the verification certs.
    fn verify_with_certs(self: Box<Self>,
                         certs: &Certs)
                         -> Result<Box<dyn Decrypt<'s, S, Certs, Keys> + 's>>;

    /// Tries to decrypt with the given session key.
    fn with_session_key(self: Box<Self>, sk: SessionKey)
                        -> Result<Box<dyn Decrypt<'s, S, Certs, Keys> + 's>>;

    /// Tries to decrypt with the given password.
    fn with_password(self: Box<Self>, password: Password)
                     -> Result<Box<dyn Decrypt<'s, S, Certs, Keys> + 's>>;

    /// Adds the decryption keys.
    fn with_keys(self: Box<Self>, key: &Keys)
                 -> Result<Box<dyn Decrypt<'s, S, Certs, Keys> + 's>>;

    /// Adds a password to unlock the decryption keys with.
    ///
    /// All supplied passwords will be used to try to unlock all keys.
    fn with_key_password(self: Box<Self>, password: Password)
                         -> Result<Box<dyn Decrypt<'s, S, Certs, Keys> + 's>>;

    /// Decrypts `ciphertext`, returning verification results and
    /// plaintext.
    fn ciphertext<'d>(self: Box<Self>,
                      ciphertext: &'d mut (dyn io::Read + Send + Sync))
                      -> Result<Box<dyn Ready<(Option<SessionKey>,
                                               Vec<Verification>)> + 'd>>
    where
        's: 'd;
}

/// Builder for [`SOP::armor`].
pub trait Armor<'s> {
    /// Overrides automatic detection of label.
    #[deprecated]
    fn label(self: Box<Self>, label: ArmorLabel) -> Box<dyn Armor<'s> + 's>;

    /// Armors `data`.
    fn data<'d>(self: Box<Self>, data: &'d mut (dyn io::Read + Send + Sync))
        -> Result<Box<dyn Ready + 'd>>
    where
        's: 'd;
}

/// Builder for [`SOP::dearmor`].
pub trait Dearmor<'s> {
    /// Dearmors `data`.
    fn data<'d>(self: Box<Self>, data: &'d mut (dyn io::Read + Send + Sync))
        -> Result<Box<dyn Ready + 'd>>
    where
        's: 'd;
}

/// Builder for [`SOP::inline_detach`].
pub trait InlineDetach<'s, Sigs: Save> {
    /// Splits Signatures from the Inline-Signed Message.
    fn message<'d>(self: Box<Self>,
                   data: &'d mut (dyn io::Read + Send + Sync))
                   -> Result<Box<dyn Ready<Sigs> + 'd>>
    where
        's: 'd;
}

/// Builder for [`SOP::inline_verify`].
pub trait InlineVerify<'s, S: SOP<'s>, Certs: Load<'s, S>> {
    /// Makes SOP consider signatures before this date invalid.
    fn not_before(self: Box<Self>, t: SystemTime) -> Box<dyn InlineVerify<'s, S, Certs> + 's>;

    /// Makes SOP consider signatures after this date invalid.
    fn not_after(self: Box<Self>, t: SystemTime) -> Box<dyn InlineVerify<'s, S, Certs> + 's>;

    /// Adds the verification certs.
    fn certs(self: Box<Self>, certs: &Certs)
             -> Result<Box<dyn InlineVerify<'s, S, Certs> + 's>>;

    /// Verifies an Inline-Signed Message.
    fn message<'d>(self: Box<Self>,
                   data: &'d mut (dyn io::Read + Send + Sync))
                   -> Result<Box<dyn Ready<Vec<Verification>> + 'd>>
    where
        's: 'd;
}

/// Builder for [`SOP::inline_sign`].
pub trait InlineSign<'s, S: SOP<'s>, Keys: Load<'s, S>> {
    /// Disables armor encoding.
    fn no_armor(self: Box<Self>) -> Box<dyn InlineSign<'s, S, Keys> + 's>;

    /// Sets signature mode.
    fn mode(self: Box<Self>, mode: InlineSignAs) -> Box<dyn InlineSign<'s, S, Keys> + 's>;

    /// Adds the signer keys.
    fn keys(self: Box<Self>, keys: &Keys)
            -> Result<Box<dyn InlineSign<'s, S, Keys> + 's>>;

    /// Adds a password to unlock the signing keys with.
    ///
    /// All supplied passwords will be used to try to unlock all
    /// signing keys.
    fn with_key_password(self: Box<Self>, password: Password)
                         -> Result<Box<dyn InlineSign<'s, S, Keys> + 's>>;

    /// Signs data.
    fn data<'d>(self: Box<Self>, data: &'d mut (dyn io::Read + Send + Sync))
            -> Result<Box<dyn Ready + 'd>>
    where
        's: 'd;
}

/// An operation that returns a value ready to be executed.
///
/// To execute the operation, either supply an [`std::io::Write`]r
/// using [`Ready::to_writer`] to write the resulting data to, or use
/// [`Ready::to_vec`] to write to a `Vec<u8>`.
pub trait Ready<T = ()> {
    /// Executes the operation writing the result to `sink`.
    fn to_writer(self: Box<Self>, sink: &mut (dyn io::Write + Send + Sync))
        -> Result<T>;

    /// Executes the operation writing the result into a `Vec<u8>`.
    fn to_vec(self: Box<Self>) -> Result<(T, Vec<u8>)> {
        let mut v = Vec::new();
        let r = self.to_writer(&mut v)?;
        Ok((r, v))
    }
}

/// A successful signature verification.
#[derive(Debug, Hash)]
pub struct Verification {
    creation_time: SystemTime,
    signing_key_fingerprint: String,
    signing_cert_fingerprint: String,
    signature_mode: SignatureMode,
    description: Description,
}

/// A successful signature verification.
#[derive(Debug, Hash)]
#[derive(serde::Serialize)]
pub struct Description {
    #[serde(skip_serializing_if = "Vec::is_empty")]
    signers: Vec<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    comment: Option<String>,
    #[serde(skip_serializing_if = "serde_json::Value::is_null")]
    ext: serde_json::Value,
}

#[cfg(any(feature = "cli", feature = "cliv"))]
impl fmt::Display for Verification {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{} {} {} {} {}",
               chrono::DateTime::<chrono::Utc>::from(self.creation_time())
               .format("%Y-%m-%dT%H:%M:%SZ"),
               self.signing_key_fingerprint(),
               self.signing_cert_fingerprint(),
               self.signature_mode(),
               serde_json::to_string(&self.description)
               .unwrap_or_else(|e| e.to_string()))
    }
}

impl Verification {
    /// Creates a `Verification` object.
    pub fn new<'m, T, K, C, M>(creation_time: T,
                               signing_key_fingerprint: K,
                               signing_cert_fingerprint: C,
                               signature_mode: SignatureMode,
                               message: M)
                           -> Result<Verification>
    where T: Into<SystemTime>,
          K: ToString,
          C: ToString,
          M: Into<Option<&'m str>>,
    {
        fn normalize(s: String) -> Result<String> {
            // XXX
            Ok(s)
        }
        let signing_key_fingerprint =
            normalize(signing_key_fingerprint.to_string())?;
        let signing_cert_fingerprint =
            normalize(signing_cert_fingerprint.to_string())?;

        Ok(Verification {
            creation_time: creation_time.into(),
            signing_key_fingerprint,
            signing_cert_fingerprint,
            signature_mode,
            description: Description {
                signers: Default::default(),
                comment: message.into().map(Into::into),
                ext: Default::default(),
            },
        })
    }

    /// Adds an issuer name.
    ///
    /// This is used by the SOP cli to name the certificates that were
    /// used to verify the signature with.
    pub fn add_signer<N: ToString>(&mut self, name: N) {
        self.description.signers.push(name.to_string());
    }

    /// Sets arbitrary, implementation-defined data to the
    /// verification object.
    pub fn set_extension_info<E: Into<serde_json::Value>>(&mut self, ext: E) {
        self.description.ext = ext.into();
    }

    /// Returns the signature's creation time.
    pub fn creation_time(&self) -> SystemTime {
        self.creation_time
    }

    /// Returns the fingerprint of the signing (sub)key.
    pub fn signing_key_fingerprint(&self) -> &str {
        &self.signing_key_fingerprint
    }

    /// Returns the fingerprint of the signing certificate.
    pub fn signing_cert_fingerprint(&self) -> &str {
        &self.signing_cert_fingerprint
    }

    /// Returns the signature mode.
    pub fn signature_mode(&self) -> SignatureMode {
        self.signature_mode
    }

    /// Returns a free-form message describing the verification.
    pub fn message(&self) -> Option<&str> {
        self.description.comment.as_ref().map(AsRef::as_ref)
    }
}

/// Indicates the type of signature in a `Verification`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SignatureMode {
    Text,
    Binary,
}

#[cfg(any(feature = "cli", feature = "cliv"))]
impl fmt::Display for SignatureMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SignatureMode::Text => f.write_str("mode:text"),
            SignatureMode::Binary => f.write_str("mode:binary"),
        }
    }
}

/// Signature type.
///
/// This is used by [`SOP::sign`] to select the signature type.  See
/// [`sop sign`].
///
///   [`sop sign`]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-sign-create-detached-signat
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum SignAs {
    Binary,
    Text,
}

impl Default for SignAs {
    fn default() -> Self {
        SignAs::Binary
    }
}

impl From<EncryptAs> for SignAs {
    fn from(a: EncryptAs) -> Self {
        match a {
            EncryptAs::Binary => SignAs::Binary,
            EncryptAs::Text => SignAs::Text,
        }
    }
}

impl std::str::FromStr for SignAs {
    type Err = ParseError;
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s {
            "binary" => Ok(SignAs::Binary),
            "text" => Ok(SignAs::Text),
            _ => Err(ParseError(format!(
                "{:?}, expected one of {{binary|text}}", s))),
        }
    }
}

impl fmt::Display for SignAs {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SignAs::Binary => f.write_str("binary"),
            SignAs::Text => f.write_str("text"),
        }
    }
}

/// Inline Signature type.
///
/// This is used by [`SOP::inline_sign`] to select the signature type.
/// See [`sop inline-sign`].
///
///   [`sop inline-sign`]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-inline-sign-create-an-inlin
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum InlineSignAs {
    Binary,
    Text,
    ClearSigned,
}

impl Default for InlineSignAs {
    fn default() -> Self {
        InlineSignAs::Binary
    }
}

impl std::str::FromStr for InlineSignAs {
    type Err = ParseError;
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s {
            "binary" => Ok(InlineSignAs::Binary),
            "text" => Ok(InlineSignAs::Text),
            "clearsigned" => Ok(InlineSignAs::ClearSigned),
            _ => Err(ParseError(format!(
                "{:?}, expected one of {{binary|text|clearsigned}}", s))),
        }
    }
}

impl fmt::Display for InlineSignAs {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            InlineSignAs::Binary => f.write_str("binary"),
            InlineSignAs::Text => f.write_str("text"),
            InlineSignAs::ClearSigned => f.write_str("clearsigned"),
        }
    }
}

/// Plaintext data format.
///
/// This is used by [`SOP::encrypt`] to select the data format.  See
/// [`sop encrypt`].
///
///   [`sop encrypt`]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-encrypt-encrypt-a-message
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum EncryptAs {
    Binary,
    Text,
}

impl Default for EncryptAs {
    fn default() -> Self {
        EncryptAs::Binary
    }
}

impl std::str::FromStr for EncryptAs {
    type Err = ParseError;
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s {
            "binary" => Ok(EncryptAs::Binary),
            "text" => Ok(EncryptAs::Text),
            _ => Err(ParseError(format!(
                "{}, expected one of {{binary|text}}", s))),
        }
    }
}

impl fmt::Display for EncryptAs {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            EncryptAs::Binary => f.write_str("binary"),
            EncryptAs::Text => f.write_str("text"),
        }
    }
}

/// The ASCII Armor Label.
///
/// This is used by [`SOP::armor`] to control the framing that is
/// emitted.  See [`sop armor`].
///
///   [`sop armor`]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-armor-convert-binary-to-asc
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum ArmorLabel {
    Auto,
    Sig,
    Key,
    Cert,
    Message,
}

impl Default for ArmorLabel {
    fn default() -> Self {
        ArmorLabel::Auto
    }
}

impl std::str::FromStr for ArmorLabel {
    type Err = ParseError;
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s {
            "auto" => Ok(ArmorLabel::Auto),
            "sig" => Ok(ArmorLabel::Sig),
            "key" => Ok(ArmorLabel::Key),
            "cert" => Ok(ArmorLabel::Cert),
            "message" => Ok(ArmorLabel::Message),
            _ => Err(ParseError(format!(
                "{:?}, expected one of \
                 {{auto|sig|key|cert|message}}", s))),
        }
    }
}

impl fmt::Display for ArmorLabel {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ArmorLabel::Auto => f.write_str("auto"),
            ArmorLabel::Sig => f.write_str("sig"),
            ArmorLabel::Key => f.write_str("key"),
            ArmorLabel::Cert => f.write_str("cert"),
            ArmorLabel::Message => f.write_str("message"),
        }
    }
}

/// Indicates the cryptographic digest used when making a signature.
///
/// It is useful specifically when generating signed PGP/MIME objects,
/// which want a `micalg=` parameter for the `multipart/signed`
/// content type as described in section 5 of [RFC3156].
///
/// [RFC3156]: https://datatracker.ietf.org/doc/html/rfc3156
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum Micalg {
    /// Rivest et.al. message digest 5 (deprecated).
    MD5,
    /// NIST Secure Hash Algorithm (deprecated).
    SHA1,
    /// RIPEMD-160 (deprecated).
    RipeMD,
    /// 256-bit version of SHA2.
    SHA256,
    /// 384-bit version of SHA2.
    SHA384,
    /// 512-bit version of SHA2.
    SHA512,
    /// 224-bit version of SHA2.
    SHA224,
    /// Unknown hash algorithm.
    Unknown(String),
}

impl From<u8> for Micalg {
    fn from(o: u8) -> Self {
        match o {
            1 => Micalg::MD5,
            2 => Micalg::SHA1,
            3 => Micalg::RipeMD,
            8 => Micalg::SHA256,
            9 => Micalg::SHA384,
            10 => Micalg::SHA512,
            11 => Micalg::SHA224,
            u => Micalg::Unknown(format!("unknown-algo-{}", u)),
        }
    }
}

impl fmt::Display for Micalg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("pgp-")?;
        match self {
            Micalg::MD5 => f.write_str("md5"),
            Micalg::SHA1 => f.write_str("sha1"),
            Micalg::RipeMD => f.write_str("ripemd160"),
            Micalg::SHA256 => f.write_str("sha256"),
            Micalg::SHA384 => f.write_str("sha384"),
            Micalg::SHA512 => f.write_str("sha512"),
            Micalg::SHA224 => f.write_str("sha224"),
            Micalg::Unknown(a) => f.write_str(&a.to_lowercase()),
        }
    }
}
