// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Generate cross-platform test vectors for mnemonic derivation.
//!
//! This module generates deterministic test vectors to verify that
//! the TypeScript and Rust implementations produce identical results.

use std::fs;

use rialo_cdk::{
    constants::{DERIVATION_PATH_COIN_TYPE, DERIVATION_PATH_PURPOSE_ED25519},
    wallet::mnemonic::{derive_keypair, derive_keypair_from_path},
};
use serde::Serialize;

#[derive(Debug, Serialize)]
struct TestVectors {
    version: String,
    vectors: Vectors,
}

#[derive(Debug, Serialize)]
struct Vectors {
    mnemonic_to_keypair: Vec<MnemonicVector>,
    signing_verification: Vec<SigningVector>,
    encoding: Vec<EncodingVector>,
    key_generation: Vec<KeyGenerationVector>,
}

#[derive(Debug, Serialize)]
struct MnemonicVector {
    id: String,
    description: String,
    input: MnemonicInput,
    expected: MnemonicExpected,
}

#[derive(Debug, Serialize)]
struct MnemonicInput {
    mnemonic: String,
    passphrase: String,
    account_index: u32,
    derivation_path: String,
}

#[derive(Debug, Serialize)]
struct MnemonicExpected {
    public_key_hex: String,
    public_key_base58: String,
    secret_key_hex: String,
}

#[derive(Debug, Serialize)]
struct SigningVector {
    id: String,
    description: String,
    input: SigningInput,
    expected: SigningExpected,
}

#[derive(Debug, Serialize)]
struct SigningInput {
    secret_key_hex: String,
    message_hex: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    message_utf8: Option<String>,
}

#[derive(Debug, Serialize)]
struct SigningExpected {
    signature_hex: String,
    signature_base58: String,
    verification_result: bool,
}

#[derive(Debug, Serialize)]
struct EncodingVector {
    id: String,
    description: String,
    input: EncodingInput,
    expected: EncodingExpected,
}

#[derive(Debug, Serialize)]
struct EncodingInput {
    #[serde(skip_serializing_if = "Option::is_none")]
    public_key_hex: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    signature_hex: Option<String>,
}

#[derive(Debug, Serialize)]
struct EncodingExpected {
    base58: String,
}

#[derive(Debug, Serialize)]
struct KeyGenerationVector {
    id: String,
    description: String,
    input: KeyGenerationInput,
    expected: KeyGenerationExpected,
}

#[derive(Debug, Serialize)]
struct KeyGenerationInput {
    secret_key_hex: String,
}

#[derive(Debug, Serialize)]
struct KeyGenerationExpected {
    public_key_hex: String,
    public_key_base58: String,
}

fn bytes_to_hex(bytes: &[u8]) -> String {
    bytes.iter().map(|b| format!("{:02x}", b)).collect()
}

// Helper functions to extract keys from dalek's SigningKey
fn get_public_key_bytes(keypair: &ed25519_dalek::SigningKey) -> [u8; 32] {
    keypair.verifying_key().to_bytes()
}

fn get_private_key_bytes(keypair: &ed25519_dalek::SigningKey) -> [u8; 32] {
    keypair.to_bytes()
}

fn generate_mnemonic_vectors() -> Vec<MnemonicVector> {
    let mut vectors = Vec::new();

    // Test case 1: Standard BIP39 test mnemonic (12 words), account 0
    let mnemonic1 = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
    let kp1 = derive_keypair(mnemonic1, None, 0).expect("Failed to derive keypair");

    vectors.push(MnemonicVector {
        id: "mnemonic_12_account_0".to_string(),
        description: "12-word mnemonic, account 0, no passphrase".to_string(),
        input: MnemonicInput {
            mnemonic: mnemonic1.to_string(),
            passphrase: String::new(),
            account_index: 0,
            derivation_path: format!(
                "m/{}'/{}'/{}'/{}'",
                DERIVATION_PATH_PURPOSE_ED25519, DERIVATION_PATH_COIN_TYPE, 0, 0
            ),
        },
        expected: MnemonicExpected {
            public_key_hex: bytes_to_hex(&get_public_key_bytes(&kp1)),
            public_key_base58: bs58::encode(&get_public_key_bytes(&kp1)).into_string(),
            secret_key_hex: bytes_to_hex(&get_private_key_bytes(&kp1)),
        },
    });

    // Test case 2: Same mnemonic, account 1
    let kp2 = derive_keypair(mnemonic1, None, 1).expect("Failed to derive keypair");

    vectors.push(MnemonicVector {
        id: "mnemonic_12_account_1".to_string(),
        description: "12-word mnemonic, account 1, no passphrase".to_string(),
        input: MnemonicInput {
            mnemonic: mnemonic1.to_string(),
            passphrase: String::new(),
            account_index: 1,
            derivation_path: format!(
                "m/{}'/{}'/{}'/{}'",
                DERIVATION_PATH_PURPOSE_ED25519, DERIVATION_PATH_COIN_TYPE, 1, 0
            ),
        },
        expected: MnemonicExpected {
            public_key_hex: bytes_to_hex(&get_public_key_bytes(&kp2)),
            public_key_base58: bs58::encode(&get_public_key_bytes(&kp2)).into_string(),
            secret_key_hex: bytes_to_hex(&get_private_key_bytes(&kp2)),
        },
    });

    // Test case 3: Same mnemonic, account 0, with passphrase
    let kp3 =
        derive_keypair(mnemonic1, Some("test-passphrase"), 0).expect("Failed to derive keypair");

    vectors.push(MnemonicVector {
        id: "mnemonic_12_passphrase".to_string(),
        description: "12-word mnemonic, account 0, with passphrase 'test-passphrase'".to_string(),
        input: MnemonicInput {
            mnemonic: mnemonic1.to_string(),
            passphrase: "test-passphrase".to_string(),
            account_index: 0,
            derivation_path: format!(
                "m/{}'/{}'/{}'/{}'",
                DERIVATION_PATH_PURPOSE_ED25519, DERIVATION_PATH_COIN_TYPE, 0, 0
            ),
        },
        expected: MnemonicExpected {
            public_key_hex: bytes_to_hex(&get_public_key_bytes(&kp3)),
            public_key_base58: bs58::encode(&get_public_key_bytes(&kp3)).into_string(),
            secret_key_hex: bytes_to_hex(&get_private_key_bytes(&kp3)),
        },
    });

    // Test case 4: Different 12-word mnemonic
    let mnemonic2 = "legal winner thank year wave sausage worth useful legal winner thank yellow";
    let kp4 = derive_keypair(mnemonic2, None, 0).expect("Failed to derive keypair");

    vectors.push(MnemonicVector {
        id: "mnemonic_12_different".to_string(),
        description: "Different 12-word mnemonic, account 0".to_string(),
        input: MnemonicInput {
            mnemonic: mnemonic2.to_string(),
            passphrase: String::new(),
            account_index: 0,
            derivation_path: format!(
                "m/{}'/{}'/{}'/{}'",
                DERIVATION_PATH_PURPOSE_ED25519, DERIVATION_PATH_COIN_TYPE, 0, 0
            ),
        },
        expected: MnemonicExpected {
            public_key_hex: bytes_to_hex(&get_public_key_bytes(&kp4)),
            public_key_base58: bs58::encode(&get_public_key_bytes(&kp4)).into_string(),
            secret_key_hex: bytes_to_hex(&get_private_key_bytes(&kp4)),
        },
    });

    // Test case 5: Explicit path derivation
    let explicit_path = format!(
        "m/{}'/{}'/{}'/{}'",
        DERIVATION_PATH_PURPOSE_ED25519, DERIVATION_PATH_COIN_TYPE, 5, 0
    );
    let kp5 = derive_keypair_from_path(mnemonic1, None, &explicit_path)
        .expect("Failed to derive keypair");

    vectors.push(MnemonicVector {
        id: "mnemonic_12_explicit_path".to_string(),
        description: format!("12-word mnemonic, explicit path {}", explicit_path),
        input: MnemonicInput {
            mnemonic: mnemonic1.to_string(),
            passphrase: String::new(),
            account_index: 5,
            derivation_path: explicit_path,
        },
        expected: MnemonicExpected {
            public_key_hex: bytes_to_hex(&get_public_key_bytes(&kp5)),
            public_key_base58: bs58::encode(&get_public_key_bytes(&kp5)).into_string(),
            secret_key_hex: bytes_to_hex(&get_private_key_bytes(&kp5)),
        },
    });

    vectors
}

fn generate_signing_vectors() -> Vec<SigningVector> {
    use ed25519_dalek::{Signer, SigningKey, Verifier};

    let mut vectors = Vec::new();

    // Generate from known mnemonic for reproducibility
    let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
    let kp = derive_keypair(mnemonic, None, 0).expect("Failed to derive keypair");

    // kp is already a SigningKey with dalek implementation
    let secret_key_bytes = get_private_key_bytes(&kp);
    let signing_key = SigningKey::from_bytes(&secret_key_bytes);

    // Test message 1: Simple text
    let message1 = b"Hello, Rialo!";
    let signature1 = signing_key.sign(message1);

    vectors.push(SigningVector {
        id: "sign_simple_text".to_string(),
        description: "Sign simple UTF-8 text message".to_string(),
        input: SigningInput {
            secret_key_hex: bytes_to_hex(&secret_key_bytes),
            message_hex: bytes_to_hex(message1),
            message_utf8: Some("Hello, Rialo!".to_string()),
        },
        expected: SigningExpected {
            signature_hex: bytes_to_hex(signature1.to_bytes().as_ref()),
            signature_base58: bs58::encode(signature1.to_bytes()).into_string(),
            verification_result: signing_key
                .verifying_key()
                .verify(message1, &signature1)
                .is_ok(),
        },
    });

    // Test message 2: Transaction-like bytes
    let message2 = hex::decode("0100000000000000").expect("Invalid hex");
    let signature2 = signing_key.sign(&message2);

    vectors.push(SigningVector {
        id: "sign_transaction_bytes".to_string(),
        description: "Sign transaction-like byte array".to_string(),
        input: SigningInput {
            secret_key_hex: bytes_to_hex(&secret_key_bytes),
            message_hex: bytes_to_hex(&message2),
            message_utf8: None,
        },
        expected: SigningExpected {
            signature_hex: bytes_to_hex(signature2.to_bytes().as_ref()),
            signature_base58: bs58::encode(signature2.to_bytes()).into_string(),
            verification_result: signing_key
                .verifying_key()
                .verify(&message2, &signature2)
                .is_ok(),
        },
    });

    vectors
}

fn generate_encoding_vectors() -> Vec<EncodingVector> {
    let mut vectors = Vec::new();

    // Test public key encoding
    let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
    let kp = derive_keypair(mnemonic, None, 0).expect("Failed to derive keypair");

    let public_key_bytes = get_public_key_bytes(&kp);

    vectors.push(EncodingVector {
        id: "encode_public_key".to_string(),
        description: "Base58 encoding of Ed25519 public key".to_string(),
        input: EncodingInput {
            public_key_hex: Some(bytes_to_hex(&public_key_bytes)),
            signature_hex: None,
        },
        expected: EncodingExpected {
            base58: bs58::encode(&public_key_bytes).into_string(),
        },
    });

    vectors
}

fn generate_key_generation_vectors() -> Vec<KeyGenerationVector> {
    use ed25519_dalek::SigningKey;

    let mut vectors = Vec::new();

    // Use known mnemonic to generate deterministic secret key
    let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
    let kp = derive_keypair(mnemonic, None, 0).expect("Failed to derive keypair");

    // kp is already a SigningKey with dalek implementation
    let secret_key_bytes = get_private_key_bytes(&kp);
    let signing_key = SigningKey::from_bytes(&secret_key_bytes);

    vectors.push(KeyGenerationVector {
        id: "keygen_from_mnemonic".to_string(),
        description: "Generate public key from secret key derived from mnemonic".to_string(),
        input: KeyGenerationInput {
            secret_key_hex: bytes_to_hex(&secret_key_bytes),
        },
        expected: KeyGenerationExpected {
            public_key_hex: bytes_to_hex(signing_key.verifying_key().as_bytes()),
            public_key_base58: bs58::encode(signing_key.verifying_key().as_bytes()).into_string(),
        },
    });

    vectors
}

pub fn generate() {
    eprintln!("Generating cross-platform test vectors");

    let test_vectors = TestVectors {
        version: "1.0.0".to_string(),
        vectors: Vectors {
            mnemonic_to_keypair: generate_mnemonic_vectors(),
            signing_verification: generate_signing_vectors(),
            encoding: generate_encoding_vectors(),
            key_generation: generate_key_generation_vectors(),
        },
    };

    // Serialize to JSON with pretty printing
    let json =
        serde_json::to_string_pretty(&test_vectors).expect("Failed to serialize test vectors");

    // Write to file
    let manifest_dir = env!("CARGO_MANIFEST_DIR");
    let output_path = format!("{manifest_dir}/test-vectors/crypto-vectors.json");
    fs::write(&output_path, json).expect("Failed to write test vectors");

    eprintln!("Test vectors generated successfully");
    eprintln!("Output: {output_path}");
    eprintln!(
        "Generated {} mnemonic test vectors",
        test_vectors.vectors.mnemonic_to_keypair.len()
    );
    eprintln!(
        "Generated {} signing test vectors",
        test_vectors.vectors.signing_verification.len()
    );
    eprintln!(
        "Generated {} encoding test vectors",
        test_vectors.vectors.encoding.len()
    );
    eprintln!(
        "Generated {} key generation test vectors",
        test_vectors.vectors.key_generation.len()
    );
}
