use std::fs;

use sequoia_openpgp::types::ReasonForRevocation;

mod common;
use common::Environment;
use common::rotate_subkeys;
use common::revoke_cert;

#[test]
fn revoked_certificate() -> anyhow::Result<()> {
    // Consider:
    //
    // Alice is authorized to add commits at time t0.  Then she
    // rotates her signing subkey.

    let e = Environment::new()?;
    let p = e.git_state();

    let (alice, alice_pgp) = e.gen("alice", None, None);
    let alice_fpr = &alice.fingerprint().to_string();

    // Alice adds herself as the project maintainer.
    e.sq_git(&[
        "policy",
        "authorize",
        "alice",
        "--cert-file", &alice_pgp,
        "--project-maintainer"
    ])?;
    e.git(&["add", "openpgp-policy.toml"])?;
    e.git(&[
        "commit",
        "-m", "Initial commit.",
    ])?;
    let root = e.git_current_commit()?;

    e.git(&["log"])?;
    e.sq_git(&["log", "--trust-root", &root])?;

    // Check that she can add commits.
    fs::write(p.join("2"), "2.")?;
    e.git(&["add", "2"])?;
    e.git(&[
        "commit",
        "-m", "Alice adds a commit.",
        &format!("-S{}", alice_fpr),
    ])?;
    let _c2 = e.git_current_commit()?;

    e.git(&["log"])?;
    e.sq_git(&["log", "--trust-root", &root])?;

    // Alice rotates her signing subkey.
    let alice_rotated = rotate_subkeys(&alice);
    assert!(alice_rotated.keys().count() > alice.keys().count());
    e.import(&alice_rotated).expect("can import");

    e.sq_git(&[
        "policy",
        "authorize",
        "alice",
        "--cert", &alice.fingerprint().to_string(),
        "--sign-commit"
    ])?;
    e.git(&["add", "openpgp-policy.toml"])?;
    e.git(&[
        "commit",
        "-m", "Adding rotated key.",
        &format!("-S{}", alice_fpr),
    ])?;
    let _c3 = e.git_current_commit()?;

    e.git(&["log"])?;
    e.sq_git(&["log", "--trust-root", &root])?;

    e.check_export("alice", None, &[ &alice_rotated ]);


    // Check that she can still add commits.
    fs::write(p.join("4"), "4.")?;
    e.git(&["add", "4"])?;
    e.git(&[
        "commit",
        "-m", "Alice adds a commit.",
        &format!("-S{}", alice_fpr),
    ])?;
    let _c4 = e.git_current_commit()?;

    e.git(&["log"])?;
    e.sq_git(&["log", "--trust-root", &root])?;


    // Alice revokes her certificate.
    let alice_revoked
        = revoke_cert(&alice_rotated, ReasonForRevocation::KeyRetired);

    let alice_revoked_pgp
        = e.serialize_cert("alice-revoked", &alice_revoked);

    e.sq_git(&[
        "policy",
        "authorize",
        "alice",
        "--cert-file", &alice_revoked_pgp,
        "--sign-commit"
    ])?;
    e.git(&["add", "openpgp-policy.toml"])?;
    e.git(&[
        "commit",
        "-m", "Adding revoked key.",
        &format!("-S{}", alice_fpr),
    ])?;
    let _c5 = e.git_current_commit()?;

    e.check_export("alice", None, &[ &alice_revoked ]);

    e.git(&["log"])?;
    e.sq_git(&["log", "--trust-root", &root])?;


    // Alice shouldn't be able to add any more commits.
    fs::write(p.join("6"), "6.")?;
    e.git(&["add", "6"])?;
    e.git(&[
        "commit",
        "-m", "Alice adds a commit with her revoked key.",
        &format!("-S{}", alice_fpr),
    ])?;
    let _c6 = e.git_current_commit()?;

    e.git(&["log"])?;
    assert!(e.sq_git(&["log", "--trust-root", &root]).is_err());

    Ok(())
}
