use der_parser::num_bigint::BigUint;
use time::OffsetDateTime;

use rpki::data_model::aspa::{AspaEContent, AspaValidateError};
use rpki::data_model::common::X509NameDer;
use rpki::data_model::rc::{
    AsIdOrRange, AsIdentifierChoice, AsResourceSet, IpResourceSet, RcExtensions, ResourceCertKind,
    ResourceCertificate, RpkixTbsCertificate, SubjectInfoAccess,
};

fn dummy_ee(
    ip_resources: Option<IpResourceSet>,
    as_resources: Option<AsResourceSet>,
) -> ResourceCertificate {
    ResourceCertificate {
        raw_der: vec![],
        tbs: RpkixTbsCertificate {
            version: 2,
            serial_number: BigUint::from(1u8),
            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: OffsetDateTime::UNIX_EPOCH,
            validity_not_after: OffsetDateTime::UNIX_EPOCH,
            subject_public_key_info: vec![],
            extensions: RcExtensions {
                basic_constraints_ca: false,
                basic_constraints: None,
                subject_key_identifier: Some(vec![0x01]),
                authority_key_identifier: None,
                crl_distribution_points_uris: None,
                ca_issuers_uris: None,
                subject_info_access: Some(SubjectInfoAccess::Ca(
                    rpki::data_model::rc::SubjectInfoAccessCa {
                        access_descriptions: vec![],
                    },
                )),
                certificate_policies_oid: None,
                certificate_policies: None,
                extension_oids: Vec::new(),
                ip_resources,
                as_resources,
            },
        },
        kind: ResourceCertKind::Ee,
    }
}

fn test_aspa() -> AspaEContent {
    AspaEContent {
        version: 1,
        customer_as_id: 64496,
        provider_as_ids: vec![64497],
    }
}

#[test]
fn validate_accepts_when_customer_matches_ee_asid() {
    let aspa = test_aspa();
    let ee = dummy_ee(
        None,
        Some(AsResourceSet {
            asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
                64496,
            )])),
            rdi: None,
        }),
    );
    aspa.validate_against_ee_cert(&ee)
        .expect("customer must match");
}

#[test]
fn validate_rejects_missing_as_resources() {
    let aspa = test_aspa();
    let ee = dummy_ee(None, None);
    let err = aspa.validate_against_ee_cert(&ee).unwrap_err();
    assert!(matches!(err, AspaValidateError::EeAsResourcesMissing));
}

#[test]
fn validate_rejects_as_resources_inherit_or_ranges() {
    let aspa = test_aspa();
    let ee = dummy_ee(
        None,
        Some(AsResourceSet {
            asnum: Some(AsIdentifierChoice::Inherit),
            rdi: None,
        }),
    );
    let err = aspa.validate_against_ee_cert(&ee).unwrap_err();
    assert!(matches!(err, AspaValidateError::EeAsResourcesInherit));

    let ee = dummy_ee(
        None,
        Some(AsResourceSet {
            asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![
                AsIdOrRange::Range {
                    min: 64496,
                    max: 64497,
                },
            ])),
            rdi: None,
        }),
    );
    let err = aspa.validate_against_ee_cert(&ee).unwrap_err();
    assert!(matches!(err, AspaValidateError::EeAsResourcesRangePresent));
}

#[test]
fn validate_rejects_customer_mismatch() {
    let aspa = test_aspa();
    let ee = dummy_ee(
        None,
        Some(AsResourceSet {
            asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
                64511,
            )])),
            rdi: None,
        }),
    );
    let err = aspa.validate_against_ee_cert(&ee).unwrap_err();
    assert!(matches!(
        err,
        AspaValidateError::CustomerAsIdMismatch { .. }
    ));
}

#[test]
fn validate_rejects_ip_resources_present() {
    let aspa = test_aspa();
    let ee = dummy_ee(
        Some(IpResourceSet { families: vec![] }),
        Some(AsResourceSet {
            asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
                64496,
            )])),
            rdi: None,
        }),
    );
    let err = aspa.validate_against_ee_cert(&ee).unwrap_err();
    assert!(matches!(err, AspaValidateError::EeIpResourcesPresent));
}

#[test]
fn validate_rejects_rdi_present_or_not_single_id() {
    let aspa = test_aspa();
    let ee = dummy_ee(
        None,
        Some(AsResourceSet {
            asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
                64496,
            )])),
            rdi: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
                64496,
            )])),
        }),
    );
    let err = aspa.validate_against_ee_cert(&ee).unwrap_err();
    assert!(matches!(err, AspaValidateError::EeAsResourcesRdiPresent));

    let ee = dummy_ee(
        None,
        Some(AsResourceSet {
            asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![
                AsIdOrRange::Id(64496),
                AsIdOrRange::Id(64497),
            ])),
            rdi: None,
        }),
    );
    let err = aspa.validate_against_ee_cert(&ee).unwrap_err();
    assert!(matches!(err, AspaValidateError::EeAsResourcesNotSingleId));
}
