//! Reference generator for the crypto interop test vectors
//! (data/ios-app/test-vectors.md §3). This is the *source of truth*: run it once
//! and paste the output into test-vectors.md §4, then commit.
//!
//!   cargo run -p skald-relay-common --bin gen-vectors
//!
//! The relay itself only verifies Ed25519 and derives namespace_id; the full
//! E2E suite (X25519/HKDF/AES-GCM) lives in `skald-relay-common::crypto` so
//! independent implementations (Swift app, Kotlin app) can assert byte-for-byte
//! equality. This binary is a thin driver over those library functions, so the
//! plugin, relay tests and the generator all share the exact same code path.
//!
//! ## v2 framing (data/iOS-app/v2/framing.md §1)
//!
//! In v2 the bytes that get fed to AES-GCM are not the raw JSON plaintext: the
//! sender wraps them in a tiny envelope
//!
//! ```text
//! plaintext = version (0x01) ‖ comp (1B) ‖ payload(JSON)
//! ```
//!
//! where `comp = 0x00` (no compression) when `len(payload) <= 1024`, else
//! `comp = 0x01` (zlib). See [`crypto::compress_payload`] for the implementation.
//! The reference vectors below seal that framed byte stream; interop consumers
//! must do the same.

use base64::{Engine, engine::general_purpose::STANDARD as B64};
use skald_relay_common::crypto::{
    DIR_AGENT_TO_CLIENT, DIR_CLIENT_TO_AGENT, build_aad, build_nonce, compress_payload,
    decompress_payload, derive_aes_key, derive_keys, ecdh, namespace_id, seal, sign_challenge,
};

// Exact UTF-8 plaintexts from test-vectors.md §1 (no spaces, no field reorder).
const PLAINTEXT_A2C: &[u8] = br#"{"v":1,"kind":"inbox_update","id":"00000000-0000-4000-8000-000000000001","ts":1750000000000,"badge":1,"approvals":[{"request_id":"appr_test_1","tool_name":"send_email","agent_label":"Skald","summary":"Test","created_at":1750000000000}],"clarifications":[]}"#;
const PLAINTEXT_C2A: &[u8] = br#"{"v":1,"kind":"approval_response","id":"00000000-0000-4000-8000-000000000002","ts":1750000000000,"request_id":"appr_test_1","decision":"approved"}"#;

fn main() {
    let seed_a: [u8; 32] = (0u8..32).collect::<Vec<_>>().try_into().unwrap();
    let seed_c: [u8; 32] = (32u8..64).collect::<Vec<_>>().try_into().unwrap();

    // --- key derivation (crypto.md §3) ---
    let a = derive_keys(&seed_a);
    let c = derive_keys(&seed_c);

    // --- namespace_id (crypto.md §7) ---
    let (ns_raw, ns_hex) = namespace_id(&a.ed25519_pub);

    // --- ECDH + AEAD key (crypto.md §4-5), with the mandatory self-check ---
    let s1 = ecdh(&a.x25519_priv, &c.x25519_pub);
    let s2 = ecdh(&c.x25519_priv, &a.x25519_pub);
    assert_eq!(s1, s2, "ECDH mismatch");
    let shared = s1;
    let aes_key = derive_aes_key(&shared);

    // --- A2C (agent → client) ---
    let n_a2c = build_nonce(DIR_AGENT_TO_CLIENT, 1);
    let aad_a2c = build_aad(&ns_raw, &a.ed25519_pub, &c.ed25519_pub);

    // v2 framing: wrap the JSON in version+comp+payload before sealing.
    let framed_a2c = compress_payload(PLAINTEXT_A2C);
    // Self-check: decompress_payload is the exact inverse of compress_payload.
    assert_eq!(
        decompress_payload(&framed_a2c).unwrap(),
        PLAINTEXT_A2C,
        "A2C framing round-trip mismatch"
    );
    // In v2 the AES-GCM input is the *framed* plaintext (framing.md §1).
    let sealed_a2c = seal(&aes_key, &n_a2c, &aad_a2c, &framed_a2c).unwrap();

    // --- C2A (client → agent) ---
    let n_c2a = build_nonce(DIR_CLIENT_TO_AGENT, 1);
    let aad_c2a = build_aad(&ns_raw, &c.ed25519_pub, &a.ed25519_pub);

    let framed_c2a = compress_payload(PLAINTEXT_C2A);
    assert_eq!(
        decompress_payload(&framed_c2a).unwrap(),
        PLAINTEXT_C2A,
        "C2A framing round-trip mismatch"
    );
    let sealed_c2a = seal(&aes_key, &n_c2a, &aad_c2a, &framed_c2a).unwrap();

    // --- auth signature (client), crypto.md §8 ---
    let challenge: [u8; 32] =
        hex::decode("aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899")
            .unwrap()
            .try_into()
            .unwrap();
    let sig = sign_challenge(&c.signing_key(), &challenge);

    // Round-trip self-check: decrypt what we sealed. In v2 this recovers the
    // *framed* plaintext (not the raw JSON); a real receiver would then call
    // decompress_payload to peel off the version+comp header.
    let dec =
        skald_relay_common::crypto::open(&aes_key, &n_a2c, &aad_a2c, &sealed_a2c).unwrap();
    assert_eq!(dec, framed_a2c, "A2C round-trip mismatch");
    assert_eq!(
        decompress_payload(&dec).unwrap(),
        PLAINTEXT_A2C,
        "A2C decompress after open mismatch"
    );

    println!("# Generated by `cargo run -p skald-relay-common --bin gen-vectors`");
    println!("# v2 framing (data/ios-app/v2/framing.md §1): the bytes fed to AES-GCM");
    println!("# are plaintext = version(0x01) ‖ comp(1B) ‖ payload(JSON). Below the");
    println!("# threshold (1024B), comp = 0x00 and payload is the raw JSON. V14/V17");
    println!("# below are computed by sealing the FRAMED plaintext, not the raw JSON.");
    println!("V1  agent_x25519_priv  = {}", hex::encode(a.x25519_priv));
    println!("V2  agent_x25519_pub   = {}", hex::encode(a.x25519_pub));
    println!("V3  agent_ed25519_priv = {}", hex::encode(a.ed25519_priv));
    println!("V4  agent_ed25519_pub  = {}", hex::encode(a.ed25519_pub));
    println!("V5  client_x25519_priv = {}", hex::encode(c.x25519_priv));
    println!("V6  client_x25519_pub  = {}", hex::encode(c.x25519_pub));
    println!("V7  client_ed25519_priv= {}", hex::encode(c.ed25519_priv));
    println!("V8  client_ed25519_pub = {}", hex::encode(c.ed25519_pub));
    println!("V9  namespace_id       = {}", ns_hex);
    println!("V10 shared_secret      = {}", hex::encode(shared));
    println!("V11 aes_key            = {}", hex::encode(aes_key));
    println!("V12 nonce_a2c          = {}", hex::encode(n_a2c));
    println!("V13 aad_a2c            = {}", hex::encode(&aad_a2c));
    println!("V14 sealed_a2c (b64)   = {}", B64.encode(&sealed_a2c));
    println!("V15 nonce_c2a          = {}", hex::encode(n_c2a));
    println!("V16 aad_c2a            = {}", hex::encode(&aad_c2a));
    println!("V17 sealed_c2a (b64)   = {}", B64.encode(&sealed_c2a));
    println!("V18 auth_sig_client    = {}", hex::encode(sig));
    println!();
    println!("# v2 framed plaintexts (input to AES-GCM, framing.md §1):");
    println!(
        "PT_FRAMED_A2C = {}",
        hex::encode(&framed_a2c)
    );
    println!(
        "PT_FRAMED_C2A = {}",
        hex::encode(&framed_c2a)
    );
    println!(
        "# framed_a2c[:2] = {:02x}{:02x}  (version=01, comp=00 = none for <1024 B)",
        framed_a2c[0], framed_a2c[1]
    );
    println!(
        "# framed_c2a[:2] = {:02x}{:02x}  (version=01, comp=00 = none for <1024 B)",
        framed_c2a[0], framed_c2a[1]
    );
    println!(
        "# PT_FRAMED_A2C.len = {}  (PLAINTEXT_A2C.len + 2 framing header)",
        framed_a2c.len()
    );
    println!(
        "# PT_FRAMED_C2A.len = {}  (PLAINTEXT_C2A.len + 2 framing header)",
        framed_c2a.len()
    );
}
