use crate::data_model::common::X509NameDer;
use x509_parser::prelude::FromDer;

fn canonicalize(name: &X509NameDer) -> Option<String> {
    let (rem, parsed) = x509_parser::x509::X509Name::from_der(name.as_raw()).ok()?;
    if !rem.is_empty() {
        return None;
    }
    Some(parsed.to_string())
}

/// Compare two X.509 distinguished names using a tolerant semantic comparison.
///
/// RPKI repositories in the wild sometimes encode the same name using different
/// ASN.1 string types (e.g., PrintableString vs UTF8String) while remaining
/// semantically equivalent. RFC 5280 path validation requires name matching, but
/// DER byte equality is too strict for interoperability.
pub fn x509_names_equivalent(a: &X509NameDer, b: &X509NameDer) -> bool {
    let Some(ca) = canonicalize(a) else {
        return a == b;
    };
    let Some(cb) = canonicalize(b) else {
        return a == b;
    };
    ca == cb
}

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

    #[test]
    fn x509_names_equivalent_falls_back_to_der_equality_when_parse_fails() {
        // Invalid tag (not a SEQUENCE) makes x509-parser fail and forces DER equality fallback.
        let a = X509NameDer(vec![0x01, 0x00]);
        let b = X509NameDer(vec![0x01, 0x00]);
        assert!(x509_names_equivalent(&a, &b));
    }

    #[test]
    fn x509_names_equivalent_compares_semantic_names_when_parse_succeeds() {
        let cert_der = std::fs::read(
            "tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
        )
        .expect("read certificate fixture");

        let (_rem, cert) =
            x509_parser::parse_x509_certificate(&cert_der).expect("parse certificate fixture");

        let subject = X509NameDer(cert.tbs_certificate.subject.as_raw().to_vec());
        let issuer = X509NameDer(cert.tbs_certificate.issuer.as_raw().to_vec());

        assert!(x509_names_equivalent(&subject, &subject));
        assert!(!x509_names_equivalent(&subject, &issuer));
    }

    #[test]
    fn x509_names_equivalent_falls_back_when_name_has_trailing_bytes() {
        // Use a real name DER and append a trailing byte so parsing yields leftover `rem`.
        let cert_der = std::fs::read(
            "tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
        )
        .expect("read certificate fixture");
        let (_rem, cert) =
            x509_parser::parse_x509_certificate(&cert_der).expect("parse certificate fixture");
        let mut name = cert.tbs_certificate.subject.as_raw().to_vec();
        name.push(0x00);
        let a = X509NameDer(name.clone());
        let b = X509NameDer(name);
        assert!(x509_names_equivalent(&a, &b));
    }

    #[test]
    fn x509_names_equivalent_falls_back_when_one_side_fails_to_parse() {
        let cert_der = std::fs::read(
            "tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
        )
        .expect("read certificate fixture");
        let (_rem, cert) =
            x509_parser::parse_x509_certificate(&cert_der).expect("parse certificate fixture");
        let good = X509NameDer(cert.tbs_certificate.subject.as_raw().to_vec());
        let bad = X509NameDer(vec![0x01, 0x00]);
        assert!(!x509_names_equivalent(&good, &bad));
    }
}
