use der_parser::num_bigint::BigUint;
use rpki::data_model::common::X509NameDer;
use rpki::data_model::oid::{
    OID_AUTONOMOUS_SYS_IDS, OID_BASIC_CONSTRAINTS, OID_CERTIFICATE_POLICIES, OID_CP_IPADDR_ASNUMBER,
};
use rpki::data_model::rc::{
    AsIdOrRange, AsIdentifierChoice, AsResourceSet, BasicConstraintsProfile,
    CertificatePoliciesProfile, RcExtensions, ResourceCertKind, ResourceCertificate,
    ResourceCertificateProfileError, ResourceCertificateRole, RpkixTbsCertificate,
};
use time::OffsetDateTime;

fn valid_ca_certificate() -> ResourceCertificate {
    let validation_time = OffsetDateTime::from_unix_timestamp(0).expect("valid epoch");
    ResourceCertificate {
        raw_der: Vec::new(),
        kind: ResourceCertKind::Ca,
        tbs: RpkixTbsCertificate {
            version: 2,
            serial_number: BigUint::from(1u32),
            signature_algorithm: "1.2.840.113549.1.1.11".to_string(),
            issuer_name: X509NameDer(b"CN=issuer".to_vec()),
            subject_name: X509NameDer(b"CN=subject".to_vec()),
            validity_not_before: validation_time,
            validity_not_after: validation_time,
            subject_public_key_info: Vec::new(),
            extensions: RcExtensions {
                basic_constraints_ca: true,
                basic_constraints: Some(BasicConstraintsProfile {
                    ca: true,
                    critical: true,
                    path_len_constraint: None,
                }),
                subject_key_identifier: None,
                authority_key_identifier: None,
                crl_distribution_points_uris: None,
                ca_issuers_uris: None,
                subject_info_access: None,
                certificate_policies_oid: Some(OID_CP_IPADDR_ASNUMBER.to_string()),
                certificate_policies: Some(CertificatePoliciesProfile {
                    policy_oid: OID_CP_IPADDR_ASNUMBER.to_string(),
                    qualifier_oids: Vec::new(),
                }),
                extension_oids: vec![
                    OID_BASIC_CONSTRAINTS.to_string(),
                    OID_CERTIFICATE_POLICIES.to_string(),
                ],
                ip_resources: None,
                as_resources: None,
            },
        },
    }
}

#[test]
fn ca_profile_accepts_required_basic_constraints_and_policy() {
    valid_ca_certificate()
        .validate_rfc6487_profile(ResourceCertificateRole::Ca)
        .expect("valid CA profile");
}

#[test]
fn ca_profile_rejects_noncritical_basic_constraints() {
    let mut certificate = valid_ca_certificate();
    certificate
        .tbs
        .extensions
        .basic_constraints
        .as_mut()
        .expect("constraints")
        .critical = false;

    assert!(matches!(
        certificate.validate_rfc6487_profile(ResourceCertificateRole::Ca),
        Err(ResourceCertificateProfileError::BasicConstraintsCriticality)
    ));
}

#[test]
fn ca_profile_rejects_unknown_extension() {
    let mut certificate = valid_ca_certificate();
    certificate
        .tbs
        .extensions
        .extension_oids
        .push("1.2.3.4".to_string());

    assert!(matches!(
        certificate.validate_rfc6487_profile(ResourceCertificateRole::Ca),
        Err(ResourceCertificateProfileError::DisallowedExtension { .. })
    ));
}

#[test]
fn ca_profile_rejects_non_cps_policy_qualifier() {
    let mut certificate = valid_ca_certificate();
    certificate
        .tbs
        .extensions
        .certificate_policies
        .as_mut()
        .expect("certificate policies")
        .qualifier_oids
        .push("1.2.3.4".to_string());

    assert!(matches!(
        certificate.validate_rfc6487_profile(ResourceCertificateRole::Ca),
        Err(ResourceCertificateProfileError::CertificatePoliciesInvalidQualifier(_))
    ));
}

#[test]
fn ca_profile_rejects_autonomous_system_rdi() {
    let mut certificate = valid_ca_certificate();
    certificate.tbs.extensions.as_resources = Some(AsResourceSet {
        asnum: None,
        rdi: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
            64512,
        )])),
    });
    certificate
        .tbs
        .extensions
        .extension_oids
        .push(OID_AUTONOMOUS_SYS_IDS.to_string());

    assert!(matches!(
        certificate.validate_rfc6487_profile(ResourceCertificateRole::Ca),
        Err(ResourceCertificateProfileError::AsResourcesRdiPresent)
    ));
}
