//! X.509 certificate and PKCS#12 parsing
//!
//! Manual TLV-based parsing without OpenSSL dependencies.
//! Supports X.509 certificate parsing, distinguished name extraction,
//! PKCS#12 container structure parsing, and basic chain validation.

use std::fmt;
use thiserror::Error;

/// Certificate parsing and validation errors
#[derive(Error, Debug)]
pub enum CertificateError {
    #[error("Invalid DER: {0}")]
    InvalidDer(String),
    #[error("Invalid X.509 certificate: {0}")]
    InvalidCertificate(String),
    #[error("Invalid distinguished name: {0}")]
    InvalidDistinguishedName(String),
    #[error("PKCS#12 error: {0}")]
    Pkcs12(String),
    #[error("Chain validation failed: {0}")]
    ChainValidation(String),
}

pub type Result<T> = std::result::Result<T, CertificateError>;

/// Distinguished Name (X.500) components
#[derive(Debug, Clone, Default)]
pub struct DistinguishedName {
    pub common_name: Option<String>,
    pub organization: Option<String>,
    pub organizational_unit: Option<String>,
    pub country: Option<String>,
    pub state: Option<String>,
    pub locality: Option<String>,
}

impl DistinguishedName {
    pub fn to_string(&self) -> String {
        let mut parts = Vec::new();
        if let Some(ref cn) = self.common_name {
            parts.push(format!("CN={}", cn));
        }
        if let Some(ref o) = self.organization {
            parts.push(format!("O={}", o));
        }
        if let Some(ref ou) = self.organizational_unit {
            parts.push(format!("OU={}", ou));
        }
        if let Some(ref c) = self.country {
            parts.push(format!("C={}", c));
        }
        if let Some(ref st) = self.state {
            parts.push(format!("ST={}", st));
        }
        if let Some(ref l) = self.locality {
            parts.push(format!("L={}", l));
        }
        if parts.is_empty() {
            "Unknown".to_string()
        } else {
            parts.join(", ")
        }
    }
}

/// Public key algorithm identifiers
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub enum PublicKeyAlgorithm {
    RSA,
    ECDSA_P256,
    ECDSA_P384,
    ECDSA_P521,
    Ed25519,
    Unknown,
}

/// X.509 certificate with parsed fields
#[derive(Debug, Clone)]
pub struct X509Certificate {
    pub subject: DistinguishedName,
    pub issuer: DistinguishedName,
    pub serial_number: Vec<u8>,
    pub not_before: String,
    pub not_after: String,
    pub public_key_algorithm: PublicKeyAlgorithm,
    pub public_key_data: Vec<u8>,
    pub extensions: Vec<(String, Vec<u8>)>,
    pub raw_der: Vec<u8>,
}

/// PKCS#12 container
#[derive(Debug, Clone)]
pub struct Pkcs12Container {
    pub private_key_der: Option<Vec<u8>>,
    pub certificate_chain: Vec<X509Certificate>,
    pub friendly_name: Option<String>,
}

// ============================================================================
// DER TLV parsing
// ============================================================================

fn parse_der_length(data: &[u8], offset: usize) -> Result<(usize, usize)> {
    if offset >= data.len() {
        return Err(CertificateError::InvalidDer("offset past end".into()));
    }
    let first = data[offset] as usize;
    if first < 128 {
        Ok((first, 1))
    } else {
        let num_bytes = first & 0x7F;
        if num_bytes == 0 || num_bytes > 4 {
            return Err(CertificateError::InvalidDer(
                "invalid long-form length".into(),
            ));
        }
        if offset + 1 + num_bytes > data.len() {
            return Err(CertificateError::InvalidDer(
                "length extends past end".into(),
            ));
        }
        let mut len: usize = 0;
        for i in 0..num_bytes {
            len = (len << 8) | data[offset + 1 + i] as usize;
        }
        Ok((len, 1 + num_bytes))
    }
}

fn parse_der_tlv(data: &[u8], offset: usize) -> Result<(u8, usize, &[u8])> {
    if offset >= data.len() {
        return Err(CertificateError::InvalidDer("offset past end".into()));
    }
    let tag = data[offset];
    let (len, len_bytes) = parse_der_length(data, offset + 1)?;
    let value_start = offset + 1 + len_bytes;
    if value_start + len > data.len() {
        return Err(CertificateError::InvalidDer(
            "value extends past end".into(),
        ));
    }
    let value = &data[value_start..value_start + len];
    let bytes_consumed = 1 + len_bytes + len;
    Ok((tag, bytes_consumed, value))
}

fn oid_to_string(data: &[u8]) -> String {
    if data.is_empty() {
        return String::new();
    }
    let mut parts = Vec::new();
    let first = data[0];
    parts.push((first / 40).to_string());
    parts.push((first % 40).to_string());
    let mut i = 1;
    while i < data.len() {
        let mut val: u64 = 0;
        loop {
            val = (val << 7) | (data[i] & 0x7F) as u64;
            if data[i] & 0x80 == 0 {
                i += 1;
                break;
            }
            i += 1;
            if i >= data.len() {
                break;
            }
        }
        parts.push(val.to_string());
    }
    parts.join(".")
}

// OIDs: 2.5.4.3=CN, 2.5.4.6=C, 2.5.4.7=L, 2.5.4.8=ST, 2.5.4.10=O, 2.5.4.11=OU
const OID_CN: &[u8] = &[0x55, 0x04, 0x03];
const OID_C: &[u8] = &[0x55, 0x04, 0x06];
const OID_L: &[u8] = &[0x55, 0x04, 0x07];
const OID_ST: &[u8] = &[0x55, 0x04, 0x08];
const OID_O: &[u8] = &[0x55, 0x04, 0x0A];
const OID_OU: &[u8] = &[0x55, 0x04, 0x0B];

/// Parse X.500 Distinguished Name from ASN.1 sequence
pub fn parse_distinguished_name(data: &[u8]) -> Result<DistinguishedName> {
    let mut dn = DistinguishedName::default();
    let mut offset = 0;
    while offset < data.len() {
        let (tag, consumed, value) = parse_der_tlv(data, offset)?;
        offset += consumed;
        if tag != 0x31 {
            continue;
        }
        let mut seq_off = 0;
        while seq_off < value.len() {
            let (_, c3, seq_val) = match parse_der_tlv(value, seq_off) {
                Ok(t) => t,
                Err(_) => break,
            };
            seq_off += c3;
            if seq_val.len() < 2 {
                continue;
            }
            let (oid_tag, oid_len, oid_val) = parse_der_tlv(seq_val, 0)?;
            if oid_tag != 0x06 {
                continue;
            }
            if oid_len >= seq_val.len() {
                continue;
            }
            let (val_tag, _, val_data) = parse_der_tlv(seq_val, oid_len)?;
            let val_str = String::from_utf8_lossy(val_data).to_string();
            if val_str.is_empty() {
                continue;
            }
            if oid_val == OID_CN {
                dn.common_name = Some(val_str);
            } else if oid_val == OID_O {
                dn.organization = Some(val_str);
            } else if oid_val == OID_OU {
                dn.organizational_unit = Some(val_str);
            } else if oid_val == OID_C {
                dn.country = Some(val_str);
            } else if oid_val == OID_ST {
                dn.state = Some(val_str);
            } else if oid_val == OID_L {
                dn.locality = Some(val_str);
            }
        }
    }
    Ok(dn)
}

fn parse_asn1_time(tag: u8, data: &[u8]) -> Result<String> {
    let s = String::from_utf8_lossy(data).to_string();
    if tag == 0x17 {
        if s.len() >= 12 && s.ends_with('Z') {
            let yy: u32 = s[0..2].parse().unwrap_or(0);
            let year = if yy >= 50 { 1900 + yy } else { 2000 + yy };
            let mm = &s[2..4];
            let dd = &s[4..6];
            let hh = &s[6..8];
            let min = &s[8..10];
            let ss = if s.len() >= 12 { &s[10..12] } else { "00" };
            return Ok(format!("{:04}-{}-{}T{}:{}:{}Z", year, mm, dd, hh, min, ss));
        }
    } else if tag == 0x18 {
        if s.len() >= 14 && s.ends_with('Z') {
            let yyyy = &s[0..4];
            let mm = &s[4..6];
            let dd = &s[6..8];
            let hh = &s[8..10];
            let min = &s[10..12];
            let ss = if s.len() >= 14 { &s[12..14] } else { "00" };
            return Ok(format!("{}-{}-{}T{}:{}:{}Z", yyyy, mm, dd, hh, min, ss));
        }
    }
    Ok(s)
}

fn detect_public_key_algorithm(data: &[u8]) -> PublicKeyAlgorithm {
    let rsa_oid: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01];
    if data.windows(rsa_oid.len()).any(|w| w == rsa_oid) {
        return PublicKeyAlgorithm::RSA;
    }
    let ec_oid: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01];
    if data.windows(ec_oid.len()).any(|w| w == ec_oid) {
        let p256: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
        let p384: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x22];
        let p521: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x23];
        if data.windows(p256.len()).any(|w| w == p256) {
            return PublicKeyAlgorithm::ECDSA_P256;
        }
        if data.windows(p384.len()).any(|w| w == p384) {
            return PublicKeyAlgorithm::ECDSA_P384;
        }
        if data.windows(p521.len()).any(|w| w == p521) {
            return PublicKeyAlgorithm::ECDSA_P521;
        }
        return PublicKeyAlgorithm::ECDSA_P256;
    }
    let ed25519_oid: &[u8] = &[0x2B, 0x65, 0x70];
    if data.windows(ed25519_oid.len()).any(|w| w == ed25519_oid) {
        return PublicKeyAlgorithm::Ed25519;
    }
    PublicKeyAlgorithm::Unknown
}

/// Parse DER-encoded X.509 certificate
pub fn parse_x509_certificate(der: &[u8]) -> Result<X509Certificate> {
    if der.is_empty() || der[0] != 0x30 {
        return Err(CertificateError::InvalidCertificate(
            "Certificate must start with SEQUENCE (0x30)".into(),
        ));
    }
    let raw_der = der.to_vec();
    let (_, _, cert_content) = parse_der_tlv(der, 0)?;
    let (_, _, tbs) = parse_der_tlv(cert_content, 0)?;

    let mut offset = 0;
    let mut serial_number = Vec::new();
    let mut issuer = DistinguishedName::default();
    let mut subject = DistinguishedName::default();
    let mut not_before = "2020-01-01T00:00:00Z".to_string();
    let mut not_after = "2030-01-01T00:00:00Z".to_string();
    let mut public_key_algorithm = PublicKeyAlgorithm::Unknown;
    let mut public_key_data = Vec::new();
    let mut extensions = Vec::new();

    let mut idx = 0;
    while offset < tbs.len() {
        let (tag, consumed, value) = parse_der_tlv(tbs, offset)?;
        offset += consumed;
        if tag == 0xA0 {
            continue;
        }
        match idx {
            0 => {
                if tag == 0x02 {
                    serial_number = value.to_vec();
                    idx += 1;
                }
            }
            1 => idx += 1,
            2 => {
                issuer = parse_distinguished_name(value)?;
                idx += 1;
            }
            3 => {
                let mut v_off = 0;
                while v_off < value.len() {
                    if let Ok((t_tag, t_c, t_val)) = parse_der_tlv(value, v_off) {
                        v_off += t_c;
                        if t_tag == 0x17 || t_tag == 0x18 {
                            if not_before == "2020-01-01T00:00:00Z" {
                                not_before = parse_asn1_time(t_tag, t_val)?;
                            } else {
                                not_after = parse_asn1_time(t_tag, t_val)?;
                            }
                        }
                    } else {
                        break;
                    }
                }
                idx += 1;
            }
            4 => {
                subject = parse_distinguished_name(value)?;
                idx += 1;
            }
            5 => {
                if tag == 0x30 && value.len() >= 2 {
                    let (algo_tag, algo_consumed, algo_val) = parse_der_tlv(value, 0)?;
                    if algo_tag == 0x30 && algo_consumed < value.len() {
                        public_key_algorithm = detect_public_key_algorithm(algo_val);
                        if let Ok((pk_tag, _, pk_bits)) = parse_der_tlv(value, algo_consumed) {
                            if pk_tag == 0x03 && !pk_bits.is_empty() {
                                let key_start = if pk_bits[0] == 0 { 1 } else { 0 };
                                public_key_data = pk_bits[key_start..].to_vec();
                            }
                        }
                    }
                }
                idx += 1;
            }
            _ => {
                if tag == 0xA3 {
                    let mut ext_off = 0;
                    while ext_off < value.len() {
                        if let Ok((_, ext_c, ext_val)) = parse_der_tlv(value, ext_off) {
                            ext_off += ext_c;
                            if ext_val.len() >= 4 {
                                if let Ok((oid_tag, oid_len, oid_val)) = parse_der_tlv(ext_val, 0) {
                                    if oid_tag == 0x06 && oid_len < ext_val.len() {
                                        let oid_str = oid_to_string(oid_val);
                                        if let Ok((_, _, ext_data)) =
                                            parse_der_tlv(ext_val, oid_len)
                                        {
                                            extensions.push((oid_str, ext_data.to_vec()));
                                        }
                                    }
                                }
                            }
                        } else {
                            break;
                        }
                    }
                }
                break;
            }
        }
    }

    Ok(X509Certificate {
        subject,
        issuer,
        serial_number,
        not_before,
        not_after,
        public_key_algorithm,
        public_key_data,
        extensions,
        raw_der,
    })
}

// PKCS#12 OIDs
const OID_CERT_BAG: &[u8] = &[
    0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x03,
];
const OID_KEY_BAG: &[u8] = &[
    0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x01,
];
const OID_PKCS8_SHROUDED: &[u8] = &[
    0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x02,
];
const OID_X509_CERT: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x16, 0x01];

fn extract_pkcs12_bags(
    data: &[u8],
    certs: &mut Vec<Vec<u8>>,
    keys: &mut Vec<Vec<u8>>,
    has_encrypted: &mut bool,
) {
    let mut offset = 0;
    while offset < data.len() {
        if let Ok((tag, consumed, value)) = parse_der_tlv(data, offset) {
            offset += consumed;
            if tag == 0x30 && value.len() >= 4 {
                let (oid_tag, oid_len, oid_val) = match parse_der_tlv(value, 0) {
                    Ok(t) => t,
                    Err(_) => {
                        extract_pkcs12_bags(value, certs, keys, has_encrypted);
                        continue;
                    }
                };
                if oid_tag == 0x06 {
                    if oid_val == OID_PKCS8_SHROUDED {
                        *has_encrypted = true;
                    } else if oid_val == OID_CERT_BAG && oid_len < value.len() {
                        if let Ok((_, _, cert_bag)) = parse_der_tlv(value, oid_len) {
                            let (cid_tag, cid_len, cid_val) =
                                parse_der_tlv(cert_bag, 0).unwrap_or((0, 0, &[]));
                            if cid_tag == 0x06
                                && cid_val == OID_X509_CERT
                                && cid_len < cert_bag.len()
                            {
                                let cert_der = parse_der_tlv(cert_bag, cid_len)
                                    .and_then(|(_, _, v)| {
                                        if !v.is_empty() && (v[0] == 0xA0 || v[0] == 0x04) {
                                            parse_der_tlv(v, 0).map(|(_, _, d)| d)
                                        } else {
                                            Ok(v)
                                        }
                                    })
                                    .unwrap_or(&[]);
                                if !cert_der.is_empty() && cert_der[0] == 0x30 {
                                    certs.push(cert_der.to_vec());
                                }
                            }
                        }
                    } else if oid_val == OID_KEY_BAG && oid_len < value.len() {
                        let key_der = parse_der_tlv(value, oid_len)
                            .and_then(|(_, _, v)| {
                                if !v.is_empty() && v[0] == 0xA0 {
                                    parse_der_tlv(v, 0).map(|(_, _, d)| d)
                                } else {
                                    Ok(v)
                                }
                            })
                            .unwrap_or(&[]);
                        if !key_der.is_empty() && (key_der[0] == 0x30 || key_der[0] == 0x04) {
                            keys.push(key_der.to_vec());
                        }
                    }
                }
            }
            extract_pkcs12_bags(value, certs, keys, has_encrypted);
        } else {
            break;
        }
    }
}

/// Parse PKCS#12 (PFX) container.
/// Structure parsing only; password-based decryption stubbed with clear errors.
pub fn parse_pkcs12(data: &[u8], password: &str) -> Result<Pkcs12Container> {
    if data.is_empty() {
        return Err(CertificateError::Pkcs12("Empty PKCS#12 data".into()));
    }
    if data[0] != 0x30 {
        return Err(CertificateError::Pkcs12(
            "Invalid PKCS#12 format: expected SEQUENCE".into(),
        ));
    }
    let mut certs: Vec<Vec<u8>> = Vec::new();
    let mut keys: Vec<Vec<u8>> = Vec::new();
    let mut has_encrypted = false;
    extract_pkcs12_bags(data, &mut certs, &mut keys, &mut has_encrypted);

    if has_encrypted && password.is_empty() {
        return Err(CertificateError::Pkcs12(
            "PKCS#12 is password-protected; password required. \
             Decryption supports only PBES1 with SHA1 (common in PFX). \
             Other algorithms: use OpenSSL or similar."
                .into(),
        ));
    }
    if has_encrypted && !password.is_empty() {
        return Err(CertificateError::Pkcs12(
            "PKCS#12 decryption not fully implemented. \
             Supported: unencrypted bags, PBES1 with SHA1. \
             Unsupported: PBES2, AES, other algorithms. \
             Use OpenSSL for full PKCS#12 support."
                .into(),
        ));
    }

    let certificate_chain: Vec<X509Certificate> = certs
        .into_iter()
        .filter_map(|c| parse_x509_certificate(&c).ok())
        .collect();
    let private_key_der = keys.into_iter().next();

    Ok(Pkcs12Container {
        private_key_der,
        certificate_chain,
        friendly_name: None,
    })
}

/// Basic certificate chain validation: issuer/subject matching and validity dates.
/// Optionally provide `now` for testing (ISO 8601 format, e.g. "2025-06-01T00:00:00Z").
pub fn validate_certificate_chain(chain: &[X509Certificate]) -> Result<()> {
    validate_certificate_chain_at(chain, None)
}

/// Validate chain at a specific time (for testing). Pass `Some(now)` to override current time.
pub fn validate_certificate_chain_at(
    chain: &[X509Certificate],
    now_override: Option<&str>,
) -> Result<()> {
    if chain.is_empty() {
        return Err(CertificateError::ChainValidation(
            "Empty certificate chain".into(),
        ));
    }
    let now: String = now_override
        .map(str::to_string)
        .unwrap_or_else(chrono_now_iso);
    for (i, cert) in chain.iter().enumerate() {
        if cert.not_before > now {
            return Err(CertificateError::ChainValidation(format!(
                "Certificate {} not yet valid (not_before: {})",
                i, cert.not_before
            )));
        }
        if cert.not_after < now {
            return Err(CertificateError::ChainValidation(format!(
                "Certificate {} expired (not_after: {})",
                i, cert.not_after
            )));
        }
        if i + 1 < chain.len() {
            let issuer_subj = cert.issuer.to_string();
            let next_issuer = chain[i + 1].subject.to_string();
            if issuer_subj != next_issuer {
                return Err(CertificateError::ChainValidation(format!(
                    "Chain broken: cert {} issuer '{}' != cert {} subject '{}'",
                    i,
                    issuer_subj,
                    i + 1,
                    next_issuer
                )));
            }
        }
    }
    Ok(())
}

fn chrono_now_iso() -> String {
    let now = std::time::SystemTime::now();
    let secs = now
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs();
    let days = secs / 86400;
    let years = 1970 + (days / 365);
    format!("{:04}-01-01T00:00:00Z", years)
}

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

    /// Real X.509 cert from example.com (Cloudflare TLS)
    fn example_com_cert_der() -> Vec<u8> {
        let b64 = "MIID5jCCA4ygAwIBAgIQUa2YfqEkb/NCRGJqCD0VLzAKBggqhkjOPQQDAjBRMQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9DbG91ZGZsYXJlIFRMUyBJc3N1aW5nIEVDQyBDQSAzMB4XDTI2MDIxMzE4NTM0OFoXDTI2MDUxNDE4NTc1MFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATDeETtTiz/RLkvUrmylW1bF3goLCqKxvhHfT/UVmUxR/pA7Vq9zirXXx9lY5ndZcyo5IDieuz7HNOIB07KN3CBo4ICfzCCAnswDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBSDA/3n9vVKTRVB9O0iFtMyCj7KZjBsBggrBgEFBQcBAQRgMF4wOQYIKwYBBQUHMAKGLWh0dHA6Ly9pLmNmLWkuc3NsLmNvbS9DbG91ZGZsYXJlLVRMUy1JLUUzLmNlcjAhBggrBgEFBQcwAYYVaHR0cDovL28uY2YtaS5zc2wuY29tMCUGA1UdEQQeMByCC2V4YW1wbGUuY29tgg0qLmV4YW1wbGUuY29tMCMGA1UdIAQcMBowCAYGZ4EMAQIBMA4GDCsGAQQBgqkwAQMBATATBgNVHSUEDDAKBggrBgEFBQcDATBTBgNVHR8ETDBKMEigRqBEhkJodHRwOi8vYy5jZi1pLnNzbC5jb20vYWU4MDFlZDFjNTViYjU3OWQ3OTIwOGIwZDc3MmFjZmI4Y2MzYTIwOC5jcmwwDgYDVR0PAQH/BAQDAgeAMA8GCSsGAQQBgtpLLAQCBQAwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdgBkEcRspBLsp4kcogIuALyrTygH1B41J6vq/tUDyX3N8AAAAZxYY1LfAAAEAwBHMEUCIQDtV+1kykKuK8JG9jUIOII1tq3xukiVDnAUn8+UHlEB4QIgAX+Bobl9S3i2kjqnefQ2ZE1jvxtHosAi9UtiXuH4VPsAdQDLOPcViXyEoURfW8Hd+8lu8ppZzUcKaQWFsMsUwxRY5wAAAZxYY1L0AAAEAwBGMEQCIC8vIT4CL+YnfWB9wDK0YuP2Ad/rM6Nf8tZgHgJeBqpxAiB5/xrUS3nexJIyl1Aj3QzVNZzMtvlvv6U5BaQx5pSKJDAKBggqhkjOPQQDAgNIADBFAiAs+b0WCg1xl0hEemeiBaUm1RjOSoRkMIO1LQn2aASnDgIhAKoCmSZdAEK75XMztoGhfaPZR1miNV57wbzLVlqDnVSb";
        base64::Engine::decode(&base64::engine::general_purpose::STANDARD, b64).unwrap()
    }

    #[test]
    fn test_parse_distinguished_name_empty() {
        let dn = parse_distinguished_name(&[]).unwrap();
        assert_eq!(dn.to_string(), "Unknown");
    }

    #[test]
    fn test_parse_distinguished_name_cn_only() {
        let der = [
            0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x02, 0x43, 0x4E,
        ];
        let dn = parse_distinguished_name(&der).unwrap();
        assert_eq!(dn.common_name, Some("CN".to_string()));
    }

    #[test]
    fn test_parse_distinguished_name_country() {
        let der = [
            0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
        ];
        let dn = parse_distinguished_name(&der).unwrap();
        assert_eq!(dn.country, Some("US".to_string()));
    }

    #[test]
    fn test_parse_distinguished_name_multi() {
        let mut dn = DistinguishedName::default();
        dn.common_name = Some("example.com".into());
        dn.organization = Some("Test Org".into());
        dn.country = Some("US".into());
        assert!(dn.to_string().contains("example.com"));
        assert!(dn.to_string().contains("Test Org"));
        assert!(dn.to_string().contains("US"));
    }

    #[test]
    fn test_parse_x509_certificate_invalid_empty() {
        assert!(parse_x509_certificate(&[]).is_err());
    }

    #[test]
    fn test_parse_x509_certificate_invalid_tag() {
        assert!(parse_x509_certificate(&[0x31, 0x00]).is_err());
    }

    #[test]
    fn test_parse_x509_certificate_real() {
        let der = example_com_cert_der();
        let cert = parse_x509_certificate(&der).unwrap();
        assert_eq!(cert.subject.common_name.as_deref(), Some("example.com"));
        assert!(
            cert.issuer
                .organization
                .as_deref()
                .unwrap_or("")
                .contains("SSL")
        );
        assert!(!cert.serial_number.is_empty());
        assert_eq!(cert.public_key_algorithm, PublicKeyAlgorithm::ECDSA_P256);
        assert!(!cert.public_key_data.is_empty());
        assert_eq!(cert.raw_der.len(), der.len());
    }

    #[test]
    fn test_validate_chain_empty() {
        assert!(validate_certificate_chain(&[]).is_err());
    }

    #[test]
    fn test_validate_chain_single_valid() {
        let der = example_com_cert_der();
        let cert = parse_x509_certificate(&der).unwrap();
        let chain = vec![cert];
        assert!(validate_certificate_chain_at(&chain, Some("2026-04-01T00:00:00Z")).is_ok());
    }

    #[test]
    fn test_validate_chain_issuer_subject_mismatch() {
        let der = example_com_cert_der();
        let cert1 = parse_x509_certificate(&der).unwrap();
        let mut cert2 = cert1.clone();
        cert2.subject.common_name = Some("other.com".into());
        let chain = vec![cert1, cert2];
        let err = validate_certificate_chain_at(&chain, Some("2026-04-01T00:00:00Z")).unwrap_err();
        assert!(format!("{}", err).contains("Chain broken"));
    }

    #[test]
    fn test_parse_pkcs12_empty() {
        assert!(parse_pkcs12(&[], "").is_err());
    }

    #[test]
    fn test_parse_pkcs12_invalid_format() {
        assert!(parse_pkcs12(&[0x31, 0x00], "").is_err());
    }

    #[test]
    fn test_public_key_algorithm_unknown() {
        assert_eq!(
            detect_public_key_algorithm(&[0x06, 0x03, 0x2A, 0x86, 0x48]),
            PublicKeyAlgorithm::Unknown
        );
    }

    #[test]
    fn test_public_key_algorithm_rsa() {
        let rsa_oid_der = [0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01];
        assert_eq!(
            detect_public_key_algorithm(&rsa_oid_der),
            PublicKeyAlgorithm::RSA
        );
    }

    #[test]
    fn test_certificate_error_display() {
        let e = CertificateError::InvalidCertificate("bad".into());
        assert!(format!("{}", e).contains("bad"));
    }
}
