//! Test data.
//!
//! This module includes the test data from `keystore/tests/data` in a
//! structured way.

use std::fmt;
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::OnceLock;

use sequoia_openpgp as openpgp;
use openpgp::Fingerprint;

pub struct Test {
    path: &'static str,
    pub bytes: &'static [u8],
}

impl fmt::Display for Test {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "keystore/tests/data/{}", self.path)
    }
}

pub fn dir() -> PathBuf {
    PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data"))
}

/// Returns the content of the given file below `keystore/tests/data`.
pub fn file(name: &str) -> &'static [u8] {
    static FILES: OnceLock<BTreeMap<&'static str, &'static [u8]>>
        = OnceLock::new();
    FILES.get_or_init(|| {
        let mut m: BTreeMap<&'static str, &'static [u8]> =
            Default::default();

        macro_rules! add {
            ( $key: expr, $path: expr ) => {
                m.insert($key, include_bytes!($path))
            }
        }
        include!(concat!(env!("OUT_DIR"), "/tests.index.rs.inc"));

        // Sanity checks.
        assert!(m.contains_key("simple/dave.pgp"));
        m
    }).get(name).unwrap_or_else(|| panic!("No such file {:?}", name))
}


pub struct EncryptedMessage {
    pub filename: &'static str,
    pub recipients: Vec<&'static Fingerprint>,
    pub content: &'static str,
}

macro_rules! em {
    ( $dir:expr, $name:expr $(, $recip: expr)* ) => {
        EncryptedMessage {
            filename: concat!($dir, "/", $name, ".pgp"),
            recipients: vec![ $(&*$recip,)* ],
            content: concat!($name, "\n"),
        }
    }
}

#[allow(non_upper_case_globals)]
pub mod simple {
    use super::*;

    /// The encryption subkeys.
    pub struct EncryptionSubkeys {
        pub alice: Fingerprint,
        pub alice_pri: Fingerprint,
        pub bob: Fingerprint,
        pub carol: Fingerprint,
        pub carol_pri: Fingerprint,
    }

    pub fn fp() -> &'static EncryptionSubkeys {
        static FPS: OnceLock<EncryptionSubkeys> = OnceLock::new();
        FPS.get_or_init(|| {
            EncryptionSubkeys {
                alice:
                "EBB7CD5ED3AF234A0AD15BDA1B19948D7A8424A4"
                    .parse().unwrap(),
                alice_pri:
                "AE81192975C08CA56C257BD3A29F4223DDD881B2"
                    .parse().unwrap(),
                bob:
                "92D7E5A2005676E7F2B350B11DF3BDD1D6072B44"
                    .parse().unwrap(),
                carol:
                "AF2620370975120E25A0469FAC13CAA7E282324B"
                    .parse().unwrap(),
                carol_pri:
                "CD6C4799E67CC4A551B7F2F400431FF5B8A682CD"
                    .parse().unwrap(),
            }
        })
    }

    pub fn msgs() -> impl Iterator<Item=EncryptedMessage> {
        let f = fp();
        [
            em!("simple", "for-alice", &f.alice),
            em!("simple", "for-alice-hidden", &f.alice),
            em!("simple", "for-bob", &f.bob),
            em!("simple", "for-carol", &f.carol),
            em!("simple", "for-dave"),
            em!("simple", "for-alice-bob", &f.alice, &f.bob),
            em!("simple", "for-alice-carol", &f.alice, &f.carol),
            em!("simple", "for-alice-dave", &f.alice),
            em!("simple", "for-bob-alice", &f.bob, &f.alice),
            em!("simple", "for-carol-alice", &f.carol, &f.alice),
            em!("simple", "for-carol-dave", &f.carol),
            em!("simple", "for-dave-alice", &f.alice),
            em!("simple", "for-dave-carol", &f.carol),
            em!("simple", "for-dave-alice-bob-carol", &f.alice, &f.bob, &f.carol),
        ].into_iter()
    }

    pub fn dir() -> PathBuf {
        super::dir().join("simple")
    }
}

#[allow(non_upper_case_globals)]
pub mod password {
    use super::*;

    /// The encryption subkeys.
    pub struct EncryptionSubkeys {
        pub alice: Fingerprint,
        pub alice_pri: Fingerprint,
        pub bob: Fingerprint,
        pub bob_pri: Fingerprint,
        pub frank: Fingerprint,
        pub frank_pri: Fingerprint,
    }

    pub fn fp() -> &'static EncryptionSubkeys {
        static FPS: OnceLock<EncryptionSubkeys> = OnceLock::new();
        FPS.get_or_init(|| {
            EncryptionSubkeys {
                alice_pri:
                "B9D905221577435EC69E6C10F9DF67187D2BF8B2"
                    .parse().unwrap(),
                alice:
                "9B980721629F4A98332438635AE7278072A2D0D2"
                    .parse().unwrap(),
                bob_pri:
                "7E0622ECF641D2FFE9A47C854A04BA480CE3E102"
                    .parse().unwrap(),
                bob:
                "568078BB9A65285B8A7B4498E3B63B9AE4D10DD5"
                    .parse().unwrap(),
                frank_pri:
                "D7347AEF97CCAF209FCC4DBBFEB42E3C9D46ED92"
                    .parse().unwrap(),
                frank:
                "9FB98AB6ADEBCCD0D11943258738B7C8950072AE"
                    .parse().unwrap(),
            }
        })
    }
    // The string is the password needed to decrypt the message.
    //
    // - Alice: unencrypted.
    // - Bob: two variants: unencrypted and encrypted.
    // - Frank: encrypted.
    pub fn msgs()
                -> impl Iterator<Item=(EncryptedMessage, Option<&'static str>)>
    {
        let f = fp();
        [
            (em!("password", "for-alice-bob", &f.alice, &f.bob), None),
            (em!("password", "for-bob-alice", &f.bob, &f.alice), None),
            (em!("password", "for-bob", &f.bob), None),
            (em!("password", "for-frank", &f.frank), Some("foo")),
            (em!("password", "for-frank-bob", &f.frank, &f.bob), None),
            (em!("password", "for-frank-bob", &f.frank, &f.bob), None),
        ].into_iter()
    }

    pub fn dir() -> PathBuf {
        super::dir().join("password")
    }
}
