use rpki::data_model::crl::RpkixCrl;
use rpki::data_model::manifest::ManifestObject;
use rpki::data_model::rc::ResourceCertificate;
use rpki::validation::cert_path::{CertPathError, validate_ee_cert_path};

fn max_time(mut t: time::OffsetDateTime, other: time::OffsetDateTime) -> time::OffsetDateTime {
    if other > t {
        t = other;
    }
    t
}

fn min_time(mut t: time::OffsetDateTime, other: time::OffsetDateTime) -> time::OffsetDateTime {
    if other < t {
        t = other;
    }
    t
}

#[test]
fn ee_cert_must_be_issued_by_ca_and_not_revoked() {
    let manifest_der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
    )
    .expect("read manifest fixture");
    let crl_der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.crl",
    )
    .expect("read CRL fixture");
    let issuer_ca_der = std::fs::read(
        "tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
    )
    .expect("read issuer CA cert fixture");

    let manifest = ManifestObject::decode_der(&manifest_der).expect("decode manifest");
    let ee_der = &manifest.signed_object.signed_data.certificates[0].raw_der;

    let ee = ResourceCertificate::decode_der(ee_der).expect("decode EE cert");
    let issuer = ResourceCertificate::decode_der(&issuer_ca_der).expect("decode issuer CA cert");
    let crl = RpkixCrl::decode_der(&crl_der).expect("decode CRL");

    let mut t = ee.tbs.validity_not_before;
    t = max_time(t, issuer.tbs.validity_not_before);
    t = max_time(t, crl.this_update.utc);
    t = max_time(t, manifest.manifest.this_update);
    t += time::Duration::seconds(1);

    let mut upper = ee.tbs.validity_not_after;
    upper = min_time(upper, issuer.tbs.validity_not_after);
    upper = min_time(upper, crl.next_update.utc);
    upper = min_time(upper, manifest.manifest.next_update);

    assert!(t < upper);

    validate_ee_cert_path(ee_der, &issuer_ca_der, &crl_der, None, None, t)
        .expect("cert path validates");
}

#[test]
fn wrong_issuer_ca_is_rejected() {
    let manifest_der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
    )
    .expect("read manifest fixture");
    let crl_der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.crl",
    )
    .expect("read CRL fixture");
    let wrong_issuer_ca_der = std::fs::read(
        "tests/fixtures/repository/ca.rg.net/rpki/RGnet-OU/R-lVU1XGsAeqzV1Fv0HjOD6ZFkE.cer",
    )
    .expect("read wrong issuer CA cert fixture");

    let manifest = ManifestObject::decode_der(&manifest_der).expect("decode manifest");
    let ee_der = &manifest.signed_object.signed_data.certificates[0].raw_der;
    let ee = ResourceCertificate::decode_der(ee_der).expect("decode EE cert");
    let crl = RpkixCrl::decode_der(&crl_der).expect("decode CRL");

    let t = max_time(ee.tbs.validity_not_before, crl.this_update.utc) + time::Duration::seconds(1);

    let err = validate_ee_cert_path(ee_der, &wrong_issuer_ca_der, &crl_der, None, None, t)
        .expect_err("wrong issuer must be rejected");
    assert!(
        matches!(
            err,
            CertPathError::IssuerSubjectMismatch { .. }
                | CertPathError::EeSignatureInvalid(_)
                | CertPathError::IssuerNotCa
        ),
        "{err}"
    );
}

#[test]
fn ee_not_valid_after_not_after_is_rejected() {
    let manifest_der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
    )
    .expect("read manifest fixture");
    let crl_der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.crl",
    )
    .expect("read CRL fixture");
    let issuer_ca_der = std::fs::read(
        "tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
    )
    .expect("read issuer CA cert fixture");

    let manifest = ManifestObject::decode_der(&manifest_der).expect("decode manifest");
    let ee_der = &manifest.signed_object.signed_data.certificates[0].raw_der;
    let ee = ResourceCertificate::decode_der(ee_der).expect("decode EE cert");

    let too_late = ee.tbs.validity_not_after + time::Duration::seconds(1);
    let err = validate_ee_cert_path(ee_der, &issuer_ca_der, &crl_der, None, None, too_late)
        .expect_err("expired ee rejected");
    assert!(
        matches!(err, CertPathError::CertificateNotValidAtTime),
        "{err}"
    );
}
