//! Freshness invariant: a claim's REPORTED status must always equal the fold of the ledger.
//!
//! Regression for the divergence bug found in publish review: `invalidate` appended a
//! `proven -> stale` event to the ledger, but `status` read the un-rewritten `claims/*.json`
//! file and still reported `proven` — so the read surface lied about a claim the ledger had
//! already invalidated. A truth tool must never do that. This pins `status == fold(ledger)`
//! across invalidate, contradict, and re-prove.

use std::fs;
use std::path::Path;

use eidos_claims::{
    Claim, ClaimStatus, ClaimStore, FileClaimStore, invalidate, status, write_claim,
};

const PROVEN: &str = r#"{
    "id": "claim.capability.x",
    "kind": "capability",
    "subject": "src/h",
    "statement": "x is healthy",
    "status": "proven",
    "evidence_refs": [{"kind": "check", "ref": "c.sh"}],
    "verifier_ref": "telos:prove",
    "invalidated_by": ["src/h"],
    "created_at": "2026-06-15T00:00:00Z",
    "updated_at": "2026-06-15T00:00:00Z",
    "last_verified_at": "2026-06-15T00:00:00Z"
}"#;

fn reported_status(root: &Path, subject: &str) -> ClaimStatus {
    let s = status(root, Some(subject)).unwrap();
    assert_eq!(s.len(), 1, "expected exactly one claim for {subject}");
    s[0].status
}

#[test]
fn status_tracks_the_ledger_not_the_stale_file() {
    let tmp = std::env::temp_dir().join(format!("aether-freshness-{}", std::process::id()));
    let _ = fs::remove_dir_all(&tmp);
    let claim: Claim = serde_json::from_str(PROVEN).unwrap();
    write_claim(&tmp, &claim).unwrap();

    // baseline: the seeded claim reads as proven
    assert_eq!(reported_status(&tmp, "src/h"), ClaimStatus::Proven);

    // a tracked file changed → invalidate flips proven -> stale (event appended to the ledger)
    let flips = invalidate(&tmp, &["src/h".to_string()], "2026-06-16T00:00:00Z").unwrap();
    assert_eq!(flips.len(), 1, "the proven claim should flip");
    assert_eq!(flips[0].to, ClaimStatus::Stale);

    // THE INVARIANT (this was the bug): status must reflect the ledger, not the un-rewritten file.
    assert_eq!(
        reported_status(&tmp, "src/h"),
        ClaimStatus::Stale,
        "status must fold the ledger, not report the stale claims/*.json file"
    );

    // a reverify that fails (stale -> contradicted) must also be reflected by status
    let store = FileClaimStore::new(&tmp);
    store
        .transition(
            &claim.id,
            ClaimStatus::Contradicted,
            "telos:reverify",
            Some("over threshold"),
        )
        .unwrap();
    assert_eq!(reported_status(&tmp, "src/h"), ClaimStatus::Contradicted);

    // and a repair (contradicted -> proven) heals it — still no divergence
    store
        .transition(
            &claim.id,
            ClaimStatus::Proven,
            "telos:reverify",
            Some("repaired"),
        )
        .unwrap();
    assert_eq!(reported_status(&tmp, "src/h"), ClaimStatus::Proven);

    let _ = fs::remove_dir_all(&tmp);
}
