Documentation
// SPDX-FileCopyrightText: Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Read a transferable public key from stdin and show status information about it

use std::{
    io::{BufReader, stdin},
    time::SystemTime,
};

use chrono::{DateTime, Utc};
use minipgp6_armor::{DataType, read::AutoDearmorer};
use minipgp6_packet::{body::public_key::PublicKey, transferable::TransferableKey};
use minipgp6_validity::{Checked, KeyFlags, Status};

fn fmt_status(s: &Status) -> String {
    match s {
        Status::Valid { expires: None } => "✅ Active (no expiration)".to_string(),
        Status::Valid { expires: Some(exp) } => {
            format!(
                "✅ Active, expires {}",
                DateTime::<Utc>::from(SystemTime::from(*exp))
            )
        }

        Status::Expired { time } => {
            format!(
                "🚫 Expired {}",
                DateTime::<Utc>::from(SystemTime::from(*time))
            )
        }

        Status::RevokedSoft { time, reason: msg } => {
            format!(
                "🚫 Revoked (soft) {}: {msg}",
                DateTime::<Utc>::from(SystemTime::from(*time))
            )
        }
        Status::RevokedHard { time, reason: msg } => {
            format!(
                "🚫 Revoked (hard) {}: {msg}",
                DateTime::<Utc>::from(SystemTime::from(*time))
            )
        }

        Status::Invalid { context } => format!("🚫 Invalid: {context}"),
    }
}

fn fmt_key_flags(key_flags: &KeyFlags<'_>) -> String {
    let mut flags = Vec::new();
    if key_flags.certification_capable() {
        flags.push("Certify")
    }

    if key_flags.encryption_capable() {
        flags.push("Encrypt")
    }

    if key_flags.data_signing_capable() {
        flags.push("Sign")
    }

    flags.join(", ")
}

fn print_status(checked: Checked<'_, PublicKey>) {
    let (pri, status, kf) = checked.primary_status();

    println!(
        "🔐 {:?} {}",
        pri.key.public_key_algorithm(),
        pri.key.fingerprint()
    );
    println!(
        "  ⏱️ Created {}",
        DateTime::<Utc>::from(SystemTime::from(pri.key.creation_time))
    );
    println!("  {}", fmt_status(&status));

    if let Some(key_flags) = kf {
        println!("  🏴 Key flags: {}", fmt_key_flags(&key_flags));
    }

    println!();

    for (subkey, status, kf) in checked.subkeys_status() {
        println!(
            "  🔑 {:?} {}",
            subkey.key.public_key_algorithm(),
            subkey.key.fingerprint()
        );
        println!(
            "    ⏱️ Created {}",
            DateTime::<Utc>::from(SystemTime::from(subkey.key.creation_time))
        );
        println!("    {}", fmt_status(&status));

        if let Some(key_flags) = kf {
            println!("    🏴 Key flags: {}", fmt_key_flags(&key_flags));
        }

        println!();
    }

    for (user_id, status) in checked.user_ids_status() {
        println!("  🪪 ID {:?}", user_id.user_id.id);
        println!("    {}", fmt_status(&status));
        println!();
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let binary = AutoDearmorer::new(BufReader::new(stdin()), Some(DataType::Public))?;

    let certs = TransferableKey::<PublicKey>::load(&mut BufReader::new(binary))?;
    if certs.len() > 1 {
        eprintln!("WARN: found more than one certificate")
    }

    if let Some(cert) = certs.into_iter().next() {
        let checked = Checked::from(&cert);

        print_status(checked);
    } else {
        eprintln!("WARN: No certificates found");
    }

    Ok(())
}