use crate::crypto::CertificateDer;
use crate::key_info::{KeyInfo, X509Data};
use base64::{engine::general_purpose, Engine as _};
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::Writer;
use serde::Deserialize;
use std::io::Cursor;
use std::str::FromStr;

const NAME: &str = "ds:Signature";
const SCHEMA: (&str, &str) = ("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#");

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Signature {
    #[serde(rename = "@Id")]
    pub id: Option<String>,
    #[serde(rename = "SignedInfo")]
    pub signed_info: SignedInfo,
    #[serde(rename = "SignatureValue")]
    pub signature_value: SignatureValue,
    #[serde(rename = "KeyInfo")]
    pub key_info: Option<Vec<KeyInfo>>,
}

impl Signature {
    pub fn template(ref_id: &str, x509_cert_der: &CertificateDer) -> Self {
        Signature {
            id: None,
            signed_info: SignedInfo {
                id: None,
                canonicalization_method: CanonicalizationMethod {
                    algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#".to_string(),
                },
                signature_method: SignatureMethod {
                    algorithm: SignatureAlgorithm::RsaSha256,
                    hmac_output_length: None,
                },
                reference: vec![Reference {
                    transforms: Some(Transforms {
                        transforms: vec![
                            Transform {
                                algorithm: "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
                                    .to_string(),
                                xpath: None,
                            },
                            Transform {
                                algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#".to_string(),
                                xpath: None,
                            },
                        ],
                    }),
                    digest_method: DigestMethod {
                        algorithm: DigestAlgorithm::Sha1,
                    },
                    digest_value: Some(DigestValue {
                        base64_content: Some("".to_string()),
                    }),
                    uri: Some(format!("#{}", ref_id)),
                    reference_type: None,
                    id: None,
                }],
            },
            signature_value: SignatureValue {
                id: None,
                base64_content: Some("".to_string()),
            },
            key_info: Some(vec![KeyInfo {
                id: None,
                x509_data: Some(X509Data {
                    certificates: vec![crate::crypto::mime_encode_x509_cert(x509_cert_der)],
                }),
            }]),
        }
    }

    pub fn add_key_info(&mut self, public_cert_der: &CertificateDer) -> &mut Self {
        self.key_info.get_or_insert(Vec::new()).push(KeyInfo {
            id: None,
            x509_data: Some(X509Data {
                certificates: vec![general_purpose::STANDARD.encode(public_cert_der.der_data())],
            }),
        });
        self
    }
}

impl TryFrom<Signature> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: Signature) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&Signature> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &Signature) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));
        let mut root = BytesStart::new(NAME);
        root.push_attribute(SCHEMA);
        if let Some(id) = &value.id {
            root.push_attribute(("Id", id.as_ref()));
        }
        writer.write_event(Event::Start(root))?;
        let event: Event<'_> = (&value.signed_info).try_into()?;
        writer.write_event(event)?;
        let event: Event<'_> = (&value.signature_value).try_into()?;
        writer.write_event(event)?;
        if let Some(key_infos) = &value.key_info {
            for key_info in key_infos {
                let event: Event<'_> = key_info.try_into()?;
                writer.write_event(event)?;
            }
        }
        writer.write_event(Event::End(BytesEnd::new(NAME)))?;
        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

const SIGNATURE_VALUE_NAME: &str = "ds:SignatureValue";

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct SignatureValue {
    #[serde(rename = "@Id")]
    pub id: Option<String>,
    #[serde(rename = "$value")]
    pub base64_content: Option<String>,
}

impl TryFrom<SignatureValue> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: SignatureValue) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&SignatureValue> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &SignatureValue) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));
        let mut root = BytesStart::new(SIGNATURE_VALUE_NAME);
        if let Some(id) = &value.id {
            root.push_attribute(("Id", id.as_ref()));
        }
        writer.write_event(Event::Start(root))?;
        if let Some(ref base64_content) = value.base64_content {
            writer.write_event(Event::Text(BytesText::from_escaped(base64_content)))?;
        }
        writer.write_event(Event::End(BytesEnd::new(SIGNATURE_VALUE_NAME)))?;
        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

const SIGNED_INFO_NAME: &str = "ds:SignedInfo";

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct SignedInfo {
    #[serde(rename = "@Id")]
    pub id: Option<String>,
    #[serde(rename = "CanonicalizationMethod")]
    pub canonicalization_method: CanonicalizationMethod,
    #[serde(rename = "SignatureMethod")]
    pub signature_method: SignatureMethod,
    #[serde(rename = "Reference")]
    pub reference: Vec<Reference>,
}

impl TryFrom<SignedInfo> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: SignedInfo) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&SignedInfo> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &SignedInfo) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));
        let mut root = BytesStart::new(SIGNED_INFO_NAME);
        if let Some(id) = &value.id {
            root.push_attribute(("Id", id.as_ref()));
        }
        writer.write_event(Event::Start(root))?;
        let event: Event<'_> = (&value.canonicalization_method).try_into()?;
        writer.write_event(event)?;
        let event: Event<'_> = (&value.signature_method).try_into()?;
        writer.write_event(event)?;
        for reference in &value.reference {
            let event: Event<'_> = reference.try_into()?;
            writer.write_event(event)?;
        }
        writer.write_event(Event::End(BytesEnd::new(SIGNED_INFO_NAME)))?;
        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

const CANONICALIZATION_METHOD: &str = "ds:CanonicalizationMethod";

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct CanonicalizationMethod {
    #[serde(rename = "@Algorithm")]
    pub algorithm: String,
}

impl TryFrom<CanonicalizationMethod> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: CanonicalizationMethod) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&CanonicalizationMethod> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &CanonicalizationMethod) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));
        let mut root = BytesStart::new(CANONICALIZATION_METHOD);
        root.push_attribute(("Algorithm", value.algorithm.as_ref()));
        writer.write_event(Event::Empty(root))?;
        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

const SIGNATURE_METHOD_NAME: &str = "ds:SignatureMethod";
const HMAC_OUTPUT_LENGTH_NAME: &str = "ds:HMACOutputLength";

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct SignatureMethod {
    #[serde(rename = "@Algorithm")]
    pub algorithm: SignatureAlgorithm,
    #[serde(rename = "@ds:HMACOutputLength")]
    #[serde(alias = "@HMACOutputLength")]
    pub hmac_output_length: Option<usize>,
}

impl TryFrom<SignatureMethod> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: SignatureMethod) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&SignatureMethod> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &SignatureMethod) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));

        let mut root = BytesStart::new(SIGNATURE_METHOD_NAME);
        root.push_attribute(("Algorithm", value.algorithm.value()));

        if let Some(hmac_output_length) = &value.hmac_output_length {
            writer.write_event(Event::Start(root))?;

            writer.write_event(Event::Start(BytesStart::new(HMAC_OUTPUT_LENGTH_NAME)))?;

            writer.write_event(Event::Text(BytesText::from_escaped(
                hmac_output_length.to_string(),
            )))?;

            writer.write_event(Event::End(BytesEnd::new(HMAC_OUTPUT_LENGTH_NAME)))?;

            writer.write_event(Event::End(BytesEnd::new(SIGNATURE_METHOD_NAME)))?;
            writer.write_event(Event::End(BytesEnd::new(SIGNATURE_METHOD_NAME)))?;
        } else {
            writer.write_event(Event::Empty(root))?;
        }

        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum SignatureAlgorithm {
    #[serde(rename = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")]
    RsaSha256,
    #[serde(rename = "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1")]
    Sha256RsaMGF1,
    #[serde(rename = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256")]
    EcdsaSha256,
    #[serde(untagged)]
    Unsupported(String),
}

impl FromStr for SignatureAlgorithm {
    type Err = Box<dyn std::error::Error>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(match s {
            "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" => SignatureAlgorithm::RsaSha256,
            "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1" => {
                SignatureAlgorithm::Sha256RsaMGF1
            }
            "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256" => {
                SignatureAlgorithm::EcdsaSha256
            }
            i => SignatureAlgorithm::Unsupported(i.to_string()),
        })
    }
}

impl SignatureAlgorithm {
    const RSA_SHA256: &'static str = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
    const SHA256_RSA_MGF1: &'static str = "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1";
    const SHA256_ECDSA: &'static str = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256";

    pub fn value(&self) -> &str {
        match self {
            SignatureAlgorithm::RsaSha256 => Self::RSA_SHA256,
            SignatureAlgorithm::Sha256RsaMGF1 => Self::SHA256_RSA_MGF1,
            SignatureAlgorithm::EcdsaSha256 => Self::SHA256_ECDSA,
            SignatureAlgorithm::Unsupported(algo) => algo,
        }
    }
}

const TRANSFORMS_NAME: &str = "ds:Transforms";
const TRANSFORM_NAME: &str = "ds:Transform";
const XPATH_NAME: &str = "ds:XPath";

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Transform {
    #[serde(rename = "@Algorithm")]
    pub algorithm: String,
    #[serde(rename = "@ds:XPath")]
    #[serde(alias = "@XPath")]
    pub xpath: Option<String>,
}

impl TryFrom<Transform> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: Transform) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&Transform> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &Transform) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));
        let mut root = BytesStart::new(TRANSFORM_NAME);
        root.push_attribute(("Algorithm", value.algorithm.as_ref()));

        if let Some(xpath) = &value.xpath {
            writer.write_event(Event::Start(root))?;

            let xpath_root = Event::Start(BytesStart::new(XPATH_NAME));
            writer.write_event(xpath_root)?;
            writer.write_event(Event::Text(BytesText::from_escaped(xpath)))?;
            writer.write_event(Event::End(BytesEnd::new(XPATH_NAME)))?;

            writer.write_event(Event::End(BytesEnd::new(TRANSFORM_NAME)))?;
        } else {
            writer.write_event(Event::Empty(root))?;
        }

        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Transforms {
    #[serde(rename = "Transform")]
    pub transforms: Vec<Transform>,
}

impl TryFrom<Transforms> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: Transforms) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&Transforms> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &Transforms) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));
        let root = BytesStart::new(TRANSFORMS_NAME);
        writer.write_event(Event::Start(root))?;
        for transform in &value.transforms {
            let event: Event<'_> = transform.try_into()?;
            writer.write_event(event)?;
        }
        writer.write_event(Event::End(BytesEnd::new(TRANSFORMS_NAME)))?;
        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

const DIGEST_METHOD: &str = "ds:DigestMethod";

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct DigestMethod {
    #[serde(rename = "@Algorithm")]
    pub algorithm: DigestAlgorithm,
}

impl TryFrom<DigestMethod> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: DigestMethod) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&DigestMethod> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &DigestMethod) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));
        let mut root = BytesStart::new(DIGEST_METHOD);
        root.push_attribute(("Algorithm", value.algorithm.value()));
        writer.write_event(Event::Empty(root))?;
        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum DigestAlgorithm {
    #[serde(rename = "http://www.w3.org/2000/09/xmldsig#sha1")]
    Sha1,
    #[serde(rename = "http://www.w3.org/2001/04/xmlenc#sha256")]
    Sha256,
    #[serde(untagged)]
    Unsupported(String),
}

impl DigestAlgorithm {
    const SHA1: &'static str = "http://www.w3.org/2000/09/xmldsig#sha1";
    const SHA256: &'static str = "http://www.w3.org/2001/04/xmlenc#sha256";

    pub fn value(&self) -> &str {
        match self {
            DigestAlgorithm::Sha1 => Self::SHA1,
            DigestAlgorithm::Sha256 => Self::SHA256,
            DigestAlgorithm::Unsupported(algo) => algo,
        }
    }
}

const DIGEST_VALUE_NAME: &str = "ds:DigestValue";

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct DigestValue {
    #[serde(rename = "$value")]
    pub base64_content: Option<String>,
}
impl TryFrom<DigestValue> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: DigestValue) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&DigestValue> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &DigestValue) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));
        let root = BytesStart::new(DIGEST_VALUE_NAME);
        writer.write_event(Event::Start(root))?;
        if let Some(ref base64_content) = value.base64_content {
            writer.write_event(Event::Text(BytesText::from_escaped(base64_content)))?;
        }
        writer.write_event(Event::End(BytesEnd::new(DIGEST_VALUE_NAME)))?;
        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

const REFERENCE_NAME: &str = "ds:Reference";

#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Reference {
    #[serde(rename = "Transforms")]
    pub transforms: Option<Transforms>,
    #[serde(rename = "DigestMethod")]
    pub digest_method: DigestMethod,
    #[serde(rename = "DigestValue")]
    pub digest_value: Option<DigestValue>,

    #[serde(rename = "@URI")]
    pub uri: Option<String>,
    #[serde(rename = "@Type")]
    pub reference_type: Option<String>,
    #[serde(rename = "@Id")]
    pub id: Option<String>,
}

impl TryFrom<Reference> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: Reference) -> Result<Self, Self::Error> {
        (&value).try_into()
    }
}

impl TryFrom<&Reference> for Event<'_> {
    type Error = Box<dyn std::error::Error>;

    fn try_from(value: &Reference) -> Result<Self, Self::Error> {
        let mut write_buf = Vec::new();
        let mut writer = Writer::new(Cursor::new(&mut write_buf));
        let mut root = BytesStart::new(REFERENCE_NAME);
        if let Some(id) = &value.id {
            root.push_attribute(("Id", id.as_ref()));
        }
        if let Some(uri) = &value.uri {
            root.push_attribute(("URI", uri.as_ref()));
        }
        if let Some(reference_type) = &value.reference_type {
            root.push_attribute(("Type", reference_type.as_ref()));
        }
        writer.write_event(Event::Start(root))?;

        if let Some(transforms) = &value.transforms {
            let event: Event<'_> = transforms.try_into()?;
            writer.write_event(event)?;
        }

        let event: Event<'_> = (&value.digest_method).try_into()?;
        writer.write_event(event)?;
        if let Some(ref digest_value) = value.digest_value {
            let event: Event<'_> = digest_value.try_into()?;
            writer.write_event(event)?;
        }

        writer.write_event(Event::End(BytesEnd::new(REFERENCE_NAME)))?;
        Ok(Event::Text(BytesText::from_escaped(String::from_utf8(
            write_buf,
        )?)))
    }
}

#[cfg(test)]
mod test {
    use crate::traits::ToXml;

    use super::*;

    #[test]
    pub fn test_signature_deserialization() -> Result<(), Box<dyn std::error::Error>> {
        let signature = r##"
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
  <ds:Reference URI="#pfxe33e94d1-ae1f-a607-5c5d-f0fd9d1bd963"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/>
</ds:Signature>
"##;
        let deserialized: Signature = quick_xml::de::from_str(signature)?;
        let serialized = deserialized.to_string()?;
        let re_deserialized: Signature = quick_xml::de::from_str(&serialized)?;
        assert_eq!(deserialized, re_deserialized);

        Ok(())
    }

    #[test]
    pub fn test_signedinfo_deserialization() -> Result<(), Box<dyn std::error::Error>> {
        let signed_info = r##"
<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
  <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#pfxe33e94d1-ae1f-a607-5c5d-f0fd9d1bd963"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue/></ds:Reference></ds:SignedInfo>
"##;
        let deserialized: SignedInfo = quick_xml::de::from_str(signed_info)?;
        let serialized = deserialized.to_string()?;
        let re_deserialized: SignedInfo = quick_xml::de::from_str(&serialized)?;
        assert_eq!(deserialized, re_deserialized);

        Ok(())
    }

    #[test]
    pub fn test_canonicalizationmethod_deserialization() -> Result<(), Box<dyn std::error::Error>> {
        let canonicalization_method =
            r#"<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>"#;
        let deserialized: CanonicalizationMethod =
            quick_xml::de::from_str(canonicalization_method)?;
        let serialized = deserialized.to_string()?;
        let re_deserialized: CanonicalizationMethod = quick_xml::de::from_str(&serialized)?;
        assert_eq!(deserialized, re_deserialized);

        Ok(())
    }

    #[test]
    pub fn test_signaturemethod_deserialization() -> Result<(), Box<dyn std::error::Error>> {
        let signature_method = r#"<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>"#;
        let deserialized: SignatureMethod = quick_xml::de::from_str(signature_method)?;
        let serialized = deserialized.to_string()?;
        let re_deserialized: SignatureMethod = quick_xml::de::from_str(&serialized)?;
        assert_eq!(deserialized, re_deserialized);

        let algo = r#"<ds:SignatureMethod Algorithm="UNSUPPORTED" />"#;
        let deserialized: SignatureMethod = quick_xml::de::from_str(algo)?;
        let serialized = deserialized.to_string()?;
        let re_deserialized: SignatureMethod = quick_xml::de::from_str(&serialized)?;
        assert_eq!(deserialized, re_deserialized);

        Ok(())
    }

    #[test]
    pub fn test_transform_deserialization() -> Result<(), Box<dyn std::error::Error>> {
        let transforms = r#"<ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms>"#;
        let deserialized: Transforms = quick_xml::de::from_str(transforms)?;
        let serialized = deserialized.to_string()?;
        let re_deserialized: Transforms = quick_xml::de::from_str(&serialized)?;
        assert_eq!(deserialized, re_deserialized);

        Ok(())
    }

    #[test]
    pub fn test_digestmethod_deserialization() -> Result<(), Box<dyn std::error::Error>> {
        let digest_method =
            r#"<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />"#;
        let deserialized: DigestMethod = quick_xml::de::from_str(digest_method)?;
        let serialized = deserialized.to_string()?;
        let re_deserialized: DigestMethod = quick_xml::de::from_str(&serialized)?;
        assert_eq!(deserialized, re_deserialized);

        let digest_method = r#"<ds:DigestMethod Algorithm="UNSUPPORTED" />"#;
        let deserialized: DigestMethod = quick_xml::de::from_str(digest_method)?;
        let serialized = deserialized.to_string()?;
        let re_deserialized: DigestMethod = quick_xml::de::from_str(&serialized)?;
        assert_eq!(deserialized, re_deserialized);

        Ok(())
    }
}
