//! Full-chain verification for `EvidenceKind::ReleaseReadiness`.

mod common;

use chrono::{Duration, Utc};
use cortex_core::ClaimCeiling;
use cortex_verifier::{verify, VerifiedTrustState, WitnessClass};

use crate::common::{authority_grade_input, full_witness_set, sample_evidence_digest, Kind};

#[test]
fn release_readiness_full_chain_promotes_to_authority_grade() {
    let digest = sample_evidence_digest();
    let now = Utc::now();
    let witnesses = full_witness_set(&digest, now);

    let state = verify(
        &authority_grade_input(Kind::ReleaseReadiness),
        &witnesses,
        now,
        Duration::seconds(86_400),
    );

    match state {
        VerifiedTrustState::FullChainVerified { ceiling, witnesses } => {
            assert_eq!(ceiling, ClaimCeiling::AuthorityGrade);
            assert_eq!(witnesses.len(), 4);
            let classes: Vec<String> = witnesses.iter().map(|w| w.class.clone()).collect();
            assert!(classes.contains(&WitnessClass::SignedLedgerChainHead.wire_str().to_string()));
            assert!(classes.contains(&WitnessClass::ExternalAnchorCrossing.wire_str().to_string()));
            assert!(classes.contains(&WitnessClass::RemoteCiConclusion.wire_str().to_string()));
            assert!(classes.contains(
                &WitnessClass::ReproducibleBuildProvenance
                    .wire_str()
                    .to_string()
            ));
        }
        other => panic!("expected FullChainVerified, got {other:?}"),
    }
}

#[test]
fn release_readiness_three_of_four_drops_to_partial() {
    let digest = sample_evidence_digest();
    let now = Utc::now();
    let mut witnesses = full_witness_set(&digest, now);
    // Remove the SLSA provenance witness.
    witnesses.pop();

    let state = verify(
        &authority_grade_input(Kind::ReleaseReadiness),
        &witnesses,
        now,
        Duration::seconds(86_400),
    );

    match state {
        VerifiedTrustState::Partial { reasons, witnesses } => {
            assert_eq!(witnesses.len(), 3);
            assert!(reasons
                .iter()
                .any(|reason| reason.contains("reproducible_build_provenance")));
        }
        other => panic!("expected Partial when SLSA witness is absent, got {other:?}"),
    }
}

#[test]
fn release_readiness_no_witnesses_is_partial_listing_all_required_classes() {
    let now = Utc::now();
    let state = verify(
        &authority_grade_input(Kind::ReleaseReadiness),
        &[],
        now,
        Duration::seconds(86_400),
    );
    match state {
        VerifiedTrustState::Partial { reasons, witnesses } => {
            assert!(witnesses.is_empty());
            // All four required classes must show up by name.
            for class in [
                WitnessClass::SignedLedgerChainHead,
                WitnessClass::ExternalAnchorCrossing,
                WitnessClass::RemoteCiConclusion,
                WitnessClass::ReproducibleBuildProvenance,
            ] {
                assert!(
                    reasons.iter().any(|r| r.contains(class.wire_str())),
                    "missing reason for {}",
                    class.wire_str()
                );
            }
        }
        other => panic!("expected Partial(no witnesses), got {other:?}"),
    }
}
