//! Phase 5 integration test: sign → verify a `RunEvidence` artifact.
//!
//! Exercises the `tsafe_core::sign` public API as an external consumer
//! would: construct a well-formed artifact, sign it with a fresh
//! Ed25519 key, then verify under both the operator-supplied verifying
//! key path and the TOFU (embedded-pubkey) path.
//!
//! Provenance: Phase 5 of the algol→tsafe merge per
//! `ecosystem-catalog/portfolio-algol-tsafe-roadmap-to-code-complete-2026-05-21.md`
//! §5.1. Re-licensed `AGPL-3.0-or-later`.

use chrono::Utc;
use ed25519_dalek::SigningKey;
use rand::rngs::OsRng;
use tsafe_core::run_evidence::{
    blake3_hash, ContractRef, DeniedSensitiveEnvEvidence, EnforcementResult, EnvironmentEvidence,
    InjectedSecretEvidence, MachineEvidence, ProcessEvidence, RiskDelta, RunEvidence,
    RUN_EVIDENCE_VERSION, RUN_SCHEMA,
};
use tsafe_core::sign::{
    canonical_bytes, sign_evidence, signed_from_run_evidence, verify_evidence,
    verify_signed_evidence, SIG_ALGO_ED25519,
};

fn well_formed() -> RunEvidence {
    let now = Utc::now();
    RunEvidence {
        schema: RUN_SCHEMA.to_string(),
        tsafe_attest_version: RUN_EVIDENCE_VERSION.to_string(),
        started_at: now,
        finished_at: now,
        repo_path: "/tmp/test-repo".to_string(),
        repo_commit: Some("0123456789abcdef0123456789abcdef01234567".to_string()),
        command: vec!["true".to_string()],
        contract: ContractRef {
            path: "tsafe.contract.json".to_string(),
            hash: blake3_hash(b"contract-bytes"),
        },
        environment: EnvironmentEvidence {
            parent_env_count: 5,
            child_env_count: 2,
            removed_env_count: 3,
            safe_baseline_injected: vec!["PATH".to_string(), "HOME".to_string()],
            secrets_injected: vec![InjectedSecretEvidence {
                name: "DATABASE_URL".to_string(),
                source: "literal://demo/DATABASE_URL".to_string(),
                hash: blake3_hash(b"postgres://example.invalid/app"),
                redacted_value: "post****/app".to_string(),
                required: true,
            }],
            sensitive_env_denied: vec![DeniedSensitiveEnvEvidence {
                name: "AWS_SECRET_ACCESS_KEY".to_string(),
                hash: blake3_hash(b"ambient-aws-secret"),
                reason: "sensitive parent-env entry denied by policy".to_string(),
            }],
        },
        process: ProcessEvidence {
            pid: 1234,
            exit_code: 0,
            duration_ms: 42,
            cwd: "/tmp/test-repo".to_string(),
        },
        machine: MachineEvidence {
            hostname_hash: blake3_hash(b"test-host"),
            username_hash: blake3_hash(b"test-user"),
            os: "linux".to_string(),
            arch: "x86_64".to_string(),
        },
        result: EnforcementResult {
            contract_enforced: true,
            violations: Vec::new(),
            risk_delta: RiskDelta {
                before_score: 30,
                after_score: 0,
            },
        },
        signature: None,
    }
}

fn signing_key() -> SigningKey {
    SigningKey::generate(&mut OsRng)
}

#[test]
fn sign_evidence_returns_signed_artifact_with_some_signature() {
    let evidence = well_formed();
    let key = signing_key();
    let signed = sign_evidence(&evidence, &key).expect("sign");
    assert!(
        signed.evidence.signature.is_some(),
        "sign_evidence must install a SignaturePayload on the returned artifact"
    );
    assert_eq!(signed.signature.algo, SIG_ALGO_ED25519);
}

#[test]
fn verify_evidence_accepts_a_correctly_signed_artifact() {
    let evidence = well_formed();
    let key = signing_key();
    let signed = sign_evidence(&evidence, &key).expect("sign");
    verify_evidence(&signed, &key.verifying_key())
        .expect("verify must succeed with the matching verifying key");
}

#[test]
fn verify_signed_evidence_uses_embedded_pubkey_tofu_path() {
    let evidence = well_formed();
    let key = signing_key();
    let signed = sign_evidence(&evidence, &key).expect("sign");
    verify_signed_evidence(&signed)
        .expect("verify_signed_evidence must succeed via the embedded pubkey");
}

#[test]
fn signed_artifact_round_trips_through_json() {
    let evidence = well_formed();
    let key = signing_key();
    let signed = sign_evidence(&evidence, &key).expect("sign");
    let json = serde_json::to_string(&signed.evidence).expect("serialise signed artifact");
    let parsed: RunEvidence = serde_json::from_str(&json).expect("deserialise signed artifact");
    let reconstituted = signed_from_run_evidence(parsed).expect("signature field present");
    verify_evidence(&reconstituted, &key.verifying_key())
        .expect("signature survives JSON round-trip");
}

#[test]
fn canonical_bytes_is_deterministic_across_calls() {
    let evidence = well_formed();
    assert_eq!(canonical_bytes(&evidence), canonical_bytes(&evidence));
}

#[test]
fn canonical_bytes_strips_signature_field_before_signing() {
    let evidence = well_formed();
    let key = signing_key();
    // Sign the artifact, then sign the result again with a fresh key.
    // The canonical bytes used for the second signing must match the
    // canonical bytes used for the first — i.e. signature presence
    // must not affect the encoded payload.
    let signed_once = sign_evidence(&evidence, &key).expect("first sign");
    let key2 = signing_key();
    let signed_twice = sign_evidence(&signed_once.evidence, &key2).expect("second sign");
    // verify_evidence on signed_twice with key2 must succeed.
    verify_evidence(&signed_twice, &key2.verifying_key())
        .expect("re-signed artifact must verify under the new key");
}
