//! Key generation for all cryptographic schemes

use crate::unified_api::logging::{KeyPurpose, KeyType};
use tracing::debug;

use crate::primitives::{
    ec::{
        ed25519::{Ed25519KeyPair, Ed25519Signature as Ed25519SignatureOps},
        traits::{EcKeyPair, EcSignature},
    },
    kem::ml_kem::{MlKem, MlKemSecurityLevel},
    sig::{
        fndsa::FnDsaSecurityLevel,
        ml_dsa::{MlDsaParameterSet, generate_keypair as ml_dsa_generate_keypair},
        slh_dsa::{SigningKey as SlhDsaSigningKey, SlhDsaSecurityLevel},
    },
};

use crate::types::{PrivateKey, PublicKey};
use crate::unified_api::CoreConfig;
use crate::unified_api::error::{CoreError, Result};

/// Generate an Ed25519 keypair
///
/// # Errors
///
/// Returns an error if:
/// - The generated keypair fails FIPS 186-5 validation
/// - The public key is the identity element (all zeros)
/// - The keypair consistency test signature verification fails
#[must_use = "generated keypair must be stored or used"]
pub fn generate_keypair() -> Result<(PublicKey, PrivateKey)> {
    super::api::fips_verify_operational()?;
    debug!("Generating Ed25519 keypair");

    // Delegate to the primitives layer so all Ed25519 key generation goes
    // through a single entry point. `Ed25519KeyPair::generate()` already
    // performs a pairwise consistency test internally.
    let keypair = Ed25519KeyPair::generate().map_err(|e| CoreError::KeyGenerationFailed {
        reason: format!("Ed25519 key generation failed: {e}"),
        recovery: "Check RNG availability and retry".to_string(),
    })?;

    // Run convenience-layer FIPS 186-5 sanity validation on top of the
    // primitives-level PCT: rejects the identity/zero edge cases and
    // re-verifies a test signature before exposing the keys.
    validate_ed25519_keypair(&keypair)?;

    let public_key = PublicKey::new(keypair.public_key_bytes());
    // secret_key_bytes() returns Zeroizing<Vec<u8>>; take the inner vec out
    // so PrivateKey owns the bytes (the emptied Zeroizing wrapper is wiped
    // on drop).
    let mut sk_zeroizing = keypair.secret_key_bytes();
    let sk_bytes = std::mem::take(&mut *sk_zeroizing);
    let private_key = PrivateKey::new(sk_bytes);

    crate::log_key_generated!("ed25519-keypair", "Ed25519", KeyType::KeyPair, KeyPurpose::Signing);

    Ok((public_key, private_key))
}

/// Generate an Ed25519 keypair with configuration
///
/// # Errors
///
/// Returns an error if:
/// - The configuration validation fails
/// - The generated keypair fails FIPS 186-5 validation
#[must_use = "generated keypair must be stored or used"]
pub fn generate_keypair_with_config(config: &CoreConfig) -> Result<(PublicKey, PrivateKey)> {
    config.validate()?;
    generate_keypair()
}

/// Validate Ed25519 keypair per FIPS 186-5 requirements.
///
/// Delegates all raw-byte access to the primitives layer so the convenience
/// layer never touches the underlying Ed25519 backend directly.
fn validate_ed25519_keypair(keypair: &Ed25519KeyPair) -> Result<()> {
    // Validate public key format (32 bytes guaranteed by Ed25519KeyPair type)
    let public_bytes = keypair.public_key_bytes();

    // Validate that public key is not the identity element (all zeros).
    // the previous `iter().all(|&b| b == 0)`
    // short-circuits on the first non-zero byte, leaking the position
    // of the first non-zero byte to a timing observer. PK bytes are
    // public input — leakage is harmless here — but the same check is
    // applied to the secret key below where leakage IS sensitive, so
    // we use a non-short-circuit fold here for symmetry with that path.
    let pk_or = public_bytes.iter().fold(0u8, |a, &b| a | b);
    if pk_or == 0 {
        return Err(CoreError::KeyGenerationFailed {
            reason: "Public key is identity element".to_string(),
            recovery: "Generate a new keypair, identity element is invalid".to_string(),
        });
    }

    // Validate private key format (32 bytes guaranteed by Ed25519KeyPair type)
    let private_bytes = keypair.secret_key_bytes();

    // non-short-circuit fold — leak of the
    // first-non-zero-byte position would be a side-channel into the
    // structure of the secret key. The fold loops over all 32 bytes
    // unconditionally, so the operation is data-independent.
    let sk_or = private_bytes.iter().fold(0u8, |a, &b| a | b);
    if sk_or == 0 {
        return Err(CoreError::KeyGenerationFailed {
            reason: "Private key is zero".to_string(),
            recovery: "Generate a new keypair, zero private key is invalid".to_string(),
        });
    }

    // Perform a test signature to ensure keypair consistency. Both sign and
    // verify go through the primitives API. Ed25519 signing is infallible.
    let test_message = b"key_validation_test";
    // `Ed25519KeyPair::sign` is now fallible
    // (validate_signature_size); the test message is well under the
    // cap, so the gate never trips here, but the `?` keeps the surface
    // honest if the cap is ever lowered.
    let signature = keypair.sign(test_message).map_err(|e| CoreError::KeyGenerationFailed {
        reason: format!("Keypair validation signing failed: {e}"),
        recovery: "Regenerate keypair and retry validation".to_string(),
    })?;
    Ed25519SignatureOps::verify(&public_bytes, test_message, &signature).map_err(|e| {
        CoreError::KeyGenerationFailed {
            reason: format!("Keypair validation failed: {e}"),
            recovery: "Regenerate keypair and retry validation".to_string(),
        }
    })?;

    Ok(())
}

/// Generate an ML-KEM keypair
///
/// Returns `(public_key_bytes, private_key_bytes)` suitable for encryption and decryption.
/// The secret key contains real key material serialized from aws-lc-rs `DecapsulationKey`.
///
/// # Errors
///
/// Returns an error if:
/// - The ML-KEM key generation operation fails
/// - The RNG fails to provide sufficient randomness
#[must_use = "generated keypair must be stored or used"]
pub fn generate_ml_kem_keypair(
    security_level: MlKemSecurityLevel,
) -> Result<(PublicKey, PrivateKey)> {
    super::api::fips_verify_operational()?;
    debug!(security_level = ?security_level, "Generating ML-KEM keypair");

    let (pk, sk) =
        MlKem::generate_keypair(security_level).map_err(|e| CoreError::KeyGenerationFailed {
            reason: format!("ML-KEM key generation failed: {e}"),
            recovery: "Check security level and RNG".to_string(),
        })?;

    let algorithm = format!("{:?}", security_level);
    crate::log_key_generated!(
        "ml-kem-keypair",
        algorithm,
        KeyType::KeyPair,
        KeyPurpose::KeyExchange
    );

    // into_bytes() returns Zeroizing<Vec<u8>> for auto-zeroization.
    // Extract the inner vec for PrivateKey construction; the empty vec left
    // in the Zeroizing wrapper is harmlessly zeroized on drop.
    let mut sk_bytes = sk.into_bytes();
    let sk_data = std::mem::take(&mut *sk_bytes);
    Ok((PublicKey::new(pk.into_bytes()), PrivateKey::new(sk_data)))
}

/// Generate an ML-KEM keypair with configuration
///
/// See [`generate_ml_kem_keypair`] for details.
///
/// # Errors
///
/// Returns an error if:
/// - The configuration validation fails
/// - The ML-KEM key generation operation fails
#[must_use = "generated keypair must be stored or used"]
pub fn generate_ml_kem_keypair_with_config(
    security_level: MlKemSecurityLevel,
    config: &CoreConfig,
) -> Result<(PublicKey, PrivateKey)> {
    config.validate()?;
    generate_ml_kem_keypair(security_level)
}

/// Generate an ML-DSA keypair
///
/// # Errors
///
/// Returns an error if the ML-DSA key generation operation fails for the given parameter set.
#[must_use = "generated keypair must be stored or used"]
pub fn generate_ml_dsa_keypair(
    parameter_set: MlDsaParameterSet,
) -> Result<(PublicKey, PrivateKey)> {
    debug!(parameter_set = ?parameter_set, "Generating ML-DSA keypair");

    let (pk, sk) =
        ml_dsa_generate_keypair(parameter_set).map_err(|e| CoreError::KeyGenerationFailed {
            reason: format!("ML-DSA key generation failed: {e}"),
            recovery: "Check parameter set".to_string(),
        })?;

    let algorithm = format!("{:?}", parameter_set);
    crate::log_key_generated!("ml-dsa-keypair", algorithm, KeyType::KeyPair, KeyPurpose::Signing);

    Ok((PublicKey::new(pk.as_bytes().to_vec()), PrivateKey::new(sk.expose_secret().to_vec())))
}

/// Generate an ML-DSA keypair with configuration
///
/// # Errors
///
/// Returns an error if:
/// - The configuration validation fails
/// - The ML-DSA key generation operation fails
#[must_use = "generated keypair must be stored or used"]
pub fn generate_ml_dsa_keypair_with_config(
    parameter_set: MlDsaParameterSet,
    config: &CoreConfig,
) -> Result<(PublicKey, PrivateKey)> {
    config.validate()?;
    generate_ml_dsa_keypair(parameter_set)
}

/// Generate an SLH-DSA keypair
///
/// # Errors
///
/// Returns an error if the SLH-DSA key generation operation fails for the given security level.
#[must_use = "generated keypair must be stored or used"]
pub fn generate_slh_dsa_keypair(
    security_level: SlhDsaSecurityLevel,
) -> Result<(PublicKey, PrivateKey)> {
    debug!(security_level = ?security_level, "Generating SLH-DSA keypair");

    let (sk, pk) =
        SlhDsaSigningKey::generate(security_level).map_err(|e| CoreError::KeyGenerationFailed {
            reason: format!("SLH-DSA key generation failed: {e}"),
            recovery: "Check security level".to_string(),
        })?;

    let algorithm = format!("{:?}", security_level);
    crate::log_key_generated!("slh-dsa-keypair", algorithm, KeyType::KeyPair, KeyPurpose::Signing);

    Ok((PublicKey::new(pk.as_bytes().to_vec()), PrivateKey::new(sk.expose_secret().to_vec())))
}

/// Generate an SLH-DSA keypair with configuration
///
/// # Errors
///
/// Returns an error if:
/// - The configuration validation fails
/// - The SLH-DSA key generation operation fails
#[must_use = "generated keypair must be stored or used"]
pub fn generate_slh_dsa_keypair_with_config(
    security_level: SlhDsaSecurityLevel,
    config: &CoreConfig,
) -> Result<(PublicKey, PrivateKey)> {
    config.validate()?;
    generate_slh_dsa_keypair(security_level)
}

/// Generate an FN-DSA keypair (Level512).
///
/// For Level1024, use [`generate_fn_dsa_keypair_with_level`].
///
/// # Errors
///
/// Returns an error if:
/// - The FN-DSA key generation operation fails
/// - The RNG is unavailable or fails to provide sufficient randomness
#[must_use = "generated keypair must be stored or used"]
pub fn generate_fn_dsa_keypair() -> Result<(PublicKey, PrivateKey)> {
    generate_fn_dsa_keypair_with_level(FnDsaSecurityLevel::Level512)
}

/// Generate an FN-DSA keypair at the specified security level.
///
/// # Arguments
/// * `level` - Security level: `Level512` (NIST Level I) or `Level1024` (NIST Level V)
///
/// # Errors
///
/// Returns an error if:
/// - The FN-DSA key generation operation fails
/// - The RNG is unavailable or fails to provide sufficient randomness
///
/// # Stack Usage
/// FN-DSA keygen needs ~32 MB of stack (Level1024 in debug builds is the
/// worst case). Default thread stacks are typically 2–8 MB, so this
/// function spawns a dedicated 32 MB-stack worker thread internally and
/// joins it before returning. Callers do not need to manage stack size
/// themselves — the API is safe to call from any thread.
#[must_use = "generated keypair must be stored or used"]
pub fn generate_fn_dsa_keypair_with_level(
    level: FnDsaSecurityLevel,
) -> Result<(PublicKey, PrivateKey)> {
    // Stack size for the inner FN-DSA keygen worker. Sized for Level1024
    // debug builds; smaller levels fit comfortably.
    const FN_DSA_KEYGEN_STACK: usize = 32 * 1024 * 1024;

    let level_name = match level {
        FnDsaSecurityLevel::Level512 => "FN-DSA-512",
        FnDsaSecurityLevel::Level1024 => "FN-DSA-1024",
    };

    debug!("Generating FN-DSA keypair ({:?})", level);

    let handle = std::thread::Builder::new()
        .name(format!("latticearc-fn-dsa-keygen-{level_name}"))
        .stack_size(FN_DSA_KEYGEN_STACK)
        .spawn(move || -> Result<(Vec<u8>, Vec<u8>)> {
            let keypair = crate::primitives::sig::fndsa::KeyPair::generate(level).map_err(|e| {
                CoreError::KeyGenerationFailed {
                    reason: format!("FN-DSA key generation failed: {e}"),
                    recovery: "Check RNG availability".to_string(),
                }
            })?;
            let pk_bytes = keypair.verifying_key().to_bytes();
            let mut sk_zeroizing = keypair.signing_key().to_bytes();
            // mem::take the signing-key bytes out of the Zeroizing wrapper so
            // the final PrivateKey owns them without a transient unzeroized
            // clone on the heap (matches the Ed25519 path above).
            let sk_bytes = std::mem::take(&mut *sk_zeroizing);
            Ok((pk_bytes, sk_bytes))
        })
        .map_err(|e| CoreError::KeyGenerationFailed {
            reason: format!("Failed to spawn FN-DSA keygen worker thread: {e}"),
            recovery: "Investigate OS thread limits".to_string(),
        })?;

    let (pk_bytes, sk_bytes) =
        handle.join().map_err(|_panic| CoreError::KeyGenerationFailed {
            reason: "FN-DSA keygen worker thread panicked".to_string(),
            recovery: "File an issue with the panic context".to_string(),
        })??;

    crate::log_key_generated!("fn-dsa-keypair", level_name, KeyType::KeyPair, KeyPurpose::Signing);

    Ok((PublicKey::new(pk_bytes), PrivateKey::new(sk_bytes)))
}

/// Generate an FN-DSA keypair with configuration
///
/// # Errors
///
/// Returns an error if:
/// - The configuration validation fails
/// - The FN-DSA key generation operation fails
#[must_use = "generated keypair must be stored or used"]
pub fn generate_fn_dsa_keypair_with_config(config: &CoreConfig) -> Result<(PublicKey, PrivateKey)> {
    config.validate()?;
    generate_fn_dsa_keypair()
}

#[cfg(test)]
#[expect(
    clippy::panic_in_result_fn,
    reason = "test/bench scaffolding: lints suppressed for this module"
)]
mod tests {
    use super::*;
    use crate::primitives::kem::ml_kem::MlKemSecurityLevel;
    use crate::primitives::sig::ml_dsa::MlDsaParameterSet;
    use crate::primitives::sig::slh_dsa::SlhDsaSecurityLevel;
    use crate::unified_api::convenience::ed25519::{
        sign_ed25519_unverified, verify_ed25519_unverified,
    };
    use crate::unified_api::convenience::pq_kem::encrypt_pq_ml_kem_unverified;
    use crate::unified_api::convenience::pq_sig::{
        sign_pq_ml_dsa_unverified, verify_pq_ml_dsa_unverified,
    };
    use crate::unified_api::convenience::pq_sig::{
        sign_pq_slh_dsa_unverified, verify_pq_slh_dsa_unverified,
    };

    // Ed25519 comprehensive tests
    #[test]
    fn test_ed25519_keypair_format_has_correct_sizes_has_correct_size() -> Result<()> {
        let (pk, sk) = generate_keypair()?;
        assert_eq!(pk.len(), 32, "Ed25519 public key must be exactly 32 bytes");
        assert_eq!(sk.expose_secret().len(), 32, "Ed25519 secret key must be exactly 32 bytes");
        Ok(())
    }

    #[test]
    fn test_ed25519_keypair_functionality_signs_and_verifies_succeeds() -> Result<()> {
        let (pk, sk) = generate_keypair()?;
        let message = b"Test message to verify key functionality";

        // Keys should actually work for signing and verification
        let signature = sign_ed25519_unverified(message, sk.expose_secret())?;
        let is_valid = verify_ed25519_unverified(message, &signature, pk.as_slice())?;
        assert!(is_valid, "Generated keypair should produce valid signatures");
        Ok(())
    }

    #[test]
    fn test_ed25519_keypair_uniqueness_produces_distinct_keys_are_unique() -> Result<()> {
        let (pk1, sk1) = generate_keypair()?;
        let (pk2, sk2) = generate_keypair()?;
        let (pk3, sk3) = generate_keypair()?;

        // All keys should be different
        assert_ne!(pk1, pk2, "Public keys must be unique");
        assert_ne!(pk1, pk3, "Public keys must be unique");
        assert_ne!(pk2, pk3, "Public keys must be unique");
        assert_ne!(sk1.expose_secret(), sk2.expose_secret(), "Secret keys must be unique");
        assert_ne!(sk1.expose_secret(), sk3.expose_secret(), "Secret keys must be unique");
        assert_ne!(sk2.expose_secret(), sk3.expose_secret(), "Secret keys must be unique");
        Ok(())
    }

    #[test]
    fn test_ed25519_keypair_with_config_succeeds() -> Result<()> {
        let config = CoreConfig::default();
        let (pk, sk) = generate_keypair_with_config(&config)?;

        // Validate format
        assert_eq!(pk.len(), 32);
        assert_eq!(sk.expose_secret().len(), 32);

        // Validate functionality
        let message = b"Config test";
        let signature = sign_ed25519_unverified(message, sk.expose_secret())?;
        let is_valid = verify_ed25519_unverified(message, &signature, pk.as_slice())?;
        assert!(is_valid);
        Ok(())
    }

    #[test]
    fn test_ed25519_cross_keypair_verification_returns_false() -> Result<()> {
        // cross-keypair verification is the
        // canonical "wrong PK" adversary-reachable path. Verify collapses
        // it to `Ok(false)` rather than `Err`.
        let (_pk1, sk1) = generate_keypair()?;
        let (pk2, _sk2) = generate_keypair()?;
        let message = b"Cross validation test";

        let signature = sign_ed25519_unverified(message, sk1.expose_secret())?;
        let result = verify_ed25519_unverified(message, &signature, pk2.as_slice());
        assert_eq!(
            result.ok(),
            Some(false),
            "Signature from one key should yield Ok(false) under different PK"
        );
        Ok(())
    }

    // ML-KEM comprehensive tests
    // Note: Full encryption/decryption roundtrip tested in integration tests
    #[test]
    fn test_ml_kem_512_keypair_generation_produces_non_empty_keys_fails() -> Result<()> {
        let (pk, sk) = generate_ml_kem_keypair(MlKemSecurityLevel::MlKem512)?;

        // Validate keys are generated with expected properties
        assert!(!pk.is_empty(), "Public key should not be empty");
        assert!(!sk.expose_secret().is_empty(), "Secret key should not be empty");

        // Public key can be used for encryption
        let plaintext = b"Test data for ML-KEM-512";
        let ciphertext =
            encrypt_pq_ml_kem_unverified(plaintext, pk.as_slice(), MlKemSecurityLevel::MlKem512)?;
        assert!(ciphertext.len() > plaintext.len(), "Ciphertext should be larger than plaintext");
        Ok(())
    }

    #[test]
    fn test_ml_kem_768_keypair_generation_produces_non_empty_keys_fails() -> Result<()> {
        let (pk, sk) = generate_ml_kem_keypair(MlKemSecurityLevel::MlKem768)?;
        assert!(!pk.is_empty());
        assert!(!sk.expose_secret().is_empty());

        let plaintext = b"Test data";
        let ciphertext =
            encrypt_pq_ml_kem_unverified(plaintext, pk.as_slice(), MlKemSecurityLevel::MlKem768)?;
        assert!(ciphertext.len() > plaintext.len());
        Ok(())
    }

    #[test]
    fn test_ml_kem_1024_keypair_generation_produces_non_empty_keys_fails() -> Result<()> {
        let (pk, sk) = generate_ml_kem_keypair(MlKemSecurityLevel::MlKem1024)?;
        assert!(!pk.is_empty());
        assert!(!sk.expose_secret().is_empty());

        let plaintext = b"Test data";
        let ciphertext =
            encrypt_pq_ml_kem_unverified(plaintext, pk.as_slice(), MlKemSecurityLevel::MlKem1024)?;
        assert!(ciphertext.len() > plaintext.len());
        Ok(())
    }

    #[test]
    fn test_ml_kem_keypair_uniqueness_produces_distinct_keys_are_unique() -> Result<()> {
        let (pk1, sk1) = generate_ml_kem_keypair(MlKemSecurityLevel::MlKem768)?;
        let (pk2, sk2) = generate_ml_kem_keypair(MlKemSecurityLevel::MlKem768)?;

        // Public keys must be unique
        assert_ne!(pk1, pk2, "ML-KEM public keys must be unique");

        // Secret keys must also be unique
        assert_ne!(sk1.expose_secret(), sk2.expose_secret(), "ML-KEM secret keys must be unique");
        Ok(())
    }

    #[test]
    fn test_ml_kem_with_config_succeeds() -> Result<()> {
        let config = CoreConfig::default();
        let (pk, sk) = generate_ml_kem_keypair_with_config(MlKemSecurityLevel::MlKem768, &config)?;

        assert!(!pk.is_empty());
        assert!(!sk.expose_secret().is_empty());

        // Validate public key works for encryption
        let plaintext = b"Config test";
        let ciphertext =
            encrypt_pq_ml_kem_unverified(plaintext, pk.as_slice(), MlKemSecurityLevel::MlKem768)?;
        assert!(ciphertext.len() > plaintext.len());
        Ok(())
    }

    // ML-DSA comprehensive tests
    #[test]
    fn test_ml_dsa_44_keypair_functionality_signs_and_verifies_succeeds() -> Result<()> {
        let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa44)?;
        let message = b"Test ML-DSA-44 signature";

        let signature =
            sign_pq_ml_dsa_unverified(message, sk.expose_secret(), MlDsaParameterSet::MlDsa44)?;
        let is_valid = verify_pq_ml_dsa_unverified(
            message,
            &signature,
            pk.as_slice(),
            MlDsaParameterSet::MlDsa44,
        )?;
        assert!(is_valid, "Generated ML-DSA-44 keys should produce valid signatures");
        Ok(())
    }

    #[test]
    fn test_ml_dsa_65_keypair_functionality_signs_and_verifies_succeeds() -> Result<()> {
        let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
        let message = b"Test ML-DSA-65 signature";

        let signature =
            sign_pq_ml_dsa_unverified(message, sk.expose_secret(), MlDsaParameterSet::MlDsa65)?;
        let is_valid = verify_pq_ml_dsa_unverified(
            message,
            &signature,
            pk.as_slice(),
            MlDsaParameterSet::MlDsa65,
        )?;
        assert!(is_valid);
        Ok(())
    }

    #[test]
    fn test_ml_dsa_87_keypair_functionality_signs_and_verifies_succeeds() -> Result<()> {
        let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa87)?;
        let message = b"Test ML-DSA-87 signature";

        let signature =
            sign_pq_ml_dsa_unverified(message, sk.expose_secret(), MlDsaParameterSet::MlDsa87)?;
        let is_valid = verify_pq_ml_dsa_unverified(
            message,
            &signature,
            pk.as_slice(),
            MlDsaParameterSet::MlDsa87,
        )?;
        assert!(is_valid);
        Ok(())
    }

    #[test]
    fn test_ml_dsa_keypair_uniqueness_produces_distinct_keys_are_unique() -> Result<()> {
        let (pk1, sk1) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
        let (pk2, sk2) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;

        assert_ne!(pk1, pk2, "ML-DSA public keys must be unique");
        assert_ne!(sk1.expose_secret(), sk2.expose_secret(), "ML-DSA secret keys must be unique");
        Ok(())
    }

    #[test]
    fn test_ml_dsa_with_config_succeeds() -> Result<()> {
        let config = CoreConfig::default();
        let (pk, sk) = generate_ml_dsa_keypair_with_config(MlDsaParameterSet::MlDsa65, &config)?;
        let message = b"Config test";

        let signature =
            sign_pq_ml_dsa_unverified(message, sk.expose_secret(), MlDsaParameterSet::MlDsa65)?;
        let is_valid = verify_pq_ml_dsa_unverified(
            message,
            &signature,
            pk.as_slice(),
            MlDsaParameterSet::MlDsa65,
        )?;
        assert!(is_valid);
        Ok(())
    }

    #[test]
    fn test_ml_dsa_cross_keypair_verification_fails() -> Result<()> {
        let (_pk1, sk1) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
        let (pk2, _sk2) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
        let message = b"Cross validation";

        let signature =
            sign_pq_ml_dsa_unverified(message, sk1.expose_secret(), MlDsaParameterSet::MlDsa65)?;
        let result = verify_pq_ml_dsa_unverified(
            message,
            &signature,
            pk2.as_slice(),
            MlDsaParameterSet::MlDsa65,
        );
        // verify collapses Err to Ok(false) (Pattern 6).
        assert_eq!(
            result.ok(),
            Some(false),
            "ML-DSA verify must return Ok(false) for cross-keypair, not Err"
        );
        Ok(())
    }

    // SLH-DSA comprehensive tests
    #[test]
    fn test_slh_dsa_128s_keypair_functionality_signs_and_verifies_succeeds() -> Result<()> {
        let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)?;
        let message = b"Test SLH-DSA-128s";

        let signature = sign_pq_slh_dsa_unverified(
            message,
            sk.expose_secret(),
            SlhDsaSecurityLevel::Shake128s,
        )?;
        let is_valid = verify_pq_slh_dsa_unverified(
            message,
            &signature,
            pk.as_slice(),
            SlhDsaSecurityLevel::Shake128s,
        )?;
        assert!(is_valid, "Generated SLH-DSA keys should produce valid signatures");
        Ok(())
    }

    #[test]
    fn test_slh_dsa_keypair_uniqueness_produces_distinct_keys_are_unique() -> Result<()> {
        let (pk1, sk1) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)?;
        let (pk2, sk2) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)?;

        assert_ne!(pk1, pk2, "SLH-DSA public keys must be unique");
        assert_ne!(sk1.expose_secret(), sk2.expose_secret(), "SLH-DSA secret keys must be unique");
        Ok(())
    }

    #[test]
    fn test_slh_dsa_with_config_succeeds() -> Result<()> {
        let config = CoreConfig::default();
        let (pk, sk) =
            generate_slh_dsa_keypair_with_config(SlhDsaSecurityLevel::Shake128s, &config)?;
        let message = b"Config test";

        let signature = sign_pq_slh_dsa_unverified(
            message,
            sk.expose_secret(),
            SlhDsaSecurityLevel::Shake128s,
        )?;
        let is_valid = verify_pq_slh_dsa_unverified(
            message,
            &signature,
            pk.as_slice(),
            SlhDsaSecurityLevel::Shake128s,
        )?;
        assert!(is_valid);
        Ok(())
    }

    // FN-DSA tests — must run in release mode (stack overflow in debug)
    #[test]
    fn test_fn_dsa_keypair_functionality_signs_and_verifies_succeeds() -> Result<()> {
        use crate::unified_api::convenience::pq_sig::{
            sign_pq_fn_dsa_unverified, verify_pq_fn_dsa_unverified,
        };

        let (pk, sk) = generate_fn_dsa_keypair()?;
        let message = b"Test FN-DSA";

        let signature =
            sign_pq_fn_dsa_unverified(message, sk.expose_secret(), FnDsaSecurityLevel::Level512)?;
        let is_valid = verify_pq_fn_dsa_unverified(
            message,
            &signature,
            pk.as_slice(),
            FnDsaSecurityLevel::Level512,
        )?;
        assert!(is_valid);
        Ok(())
    }

    #[test]
    fn test_fn_dsa_with_config_succeeds() -> Result<()> {
        use crate::unified_api::convenience::pq_sig::{
            sign_pq_fn_dsa_unverified, verify_pq_fn_dsa_unverified,
        };

        let config = CoreConfig::default();
        let (pk, sk) = generate_fn_dsa_keypair_with_config(&config)?;
        let message = b"Config test";

        let signature =
            sign_pq_fn_dsa_unverified(message, sk.expose_secret(), FnDsaSecurityLevel::Level512)?;
        let is_valid = verify_pq_fn_dsa_unverified(
            message,
            &signature,
            pk.as_slice(),
            FnDsaSecurityLevel::Level512,
        )?;
        assert!(is_valid);
        Ok(())
    }

    // === Ed25519 validation tests ===

    #[test]
    fn test_validate_ed25519_keypair_success_succeeds() -> Result<()> {
        // A normal keypair should pass validation
        let keypair = Ed25519KeyPair::generate().map_err(|e| CoreError::KeyGenerationFailed {
            reason: format!("test keypair generation failed: {e}"),
            recovery: String::new(),
        })?;
        assert!(validate_ed25519_keypair(&keypair).is_ok());
        Ok(())
    }

    // Note: Ed25519 SigningKey/VerifyingKey are always 32 bytes, so
    // the length checks (lines 63, 80) can't be triggered directly.
    // The identity check (all zeros) and keypair consistency check are the
    // meaningful error branches.

    #[test]
    fn test_generate_keypair_produces_valid_ed25519_succeeds() -> Result<()> {
        // Verify our keygen goes through the full validation path
        let (pk, sk) = generate_keypair()?;
        assert_eq!(pk.len(), 32, "Ed25519 public key should be 32 bytes");
        assert_eq!(sk.expose_secret().len(), 32, "Ed25519 secret key should be 32 bytes");

        // Verify keys are not zero (validation check)
        assert!(!pk.as_slice().iter().all(|&b| b == 0), "Public key should not be all zeros");
        assert!(!sk.expose_secret().iter().all(|&b| b == 0), "Secret key should not be all zeros");
        Ok(())
    }

    #[test]
    fn test_generate_keypair_with_config_validates_succeeds() -> Result<()> {
        let config = CoreConfig::default();
        let (pk, sk) = generate_keypair_with_config(&config)?;
        assert_eq!(pk.len(), 32);
        assert_eq!(sk.expose_secret().len(), 32);
        Ok(())
    }

    // === ML-KEM keygen with all security levels ===

    #[test]
    fn test_ml_kem_keypair_768_with_config_succeeds() -> Result<()> {
        let config = CoreConfig::default();
        let (pk, sk) = generate_ml_kem_keypair_with_config(MlKemSecurityLevel::MlKem768, &config)?;
        assert!(!pk.is_empty(), "ML-KEM-768 public key should not be empty");
        assert!(!sk.expose_secret().is_empty(), "ML-KEM-768 secret key should not be empty");
        Ok(())
    }

    // === ML-DSA keygen with all parameter sets ===

    #[test]
    fn test_ml_dsa_keypair_44_produces_non_empty_keys_fails() -> Result<()> {
        let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa44)?;
        assert!(!pk.is_empty());
        assert!(!sk.expose_secret().is_empty());
        Ok(())
    }

    #[test]
    fn test_ml_dsa_keypair_87_produces_non_empty_keys_fails() -> Result<()> {
        let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa87)?;
        assert!(!pk.is_empty());
        assert!(!sk.expose_secret().is_empty());
        Ok(())
    }

    #[test]
    fn test_ml_dsa_keypair_with_config_44_succeeds() -> Result<()> {
        let config = CoreConfig::default();
        let (pk, sk) = generate_ml_dsa_keypair_with_config(MlDsaParameterSet::MlDsa44, &config)?;
        assert!(!pk.is_empty());
        assert!(!sk.expose_secret().is_empty());
        Ok(())
    }

    // === SLH-DSA keygen with higher security levels ===

    #[test]
    fn test_slh_dsa_keypair_192s_produces_non_empty_keys_fails() -> Result<()> {
        let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake192s)?;
        assert!(!pk.is_empty());
        assert!(!sk.expose_secret().is_empty());
        Ok(())
    }

    #[test]
    fn test_slh_dsa_keypair_256s_produces_non_empty_keys_fails() -> Result<()> {
        let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake256s)?;
        assert!(!pk.is_empty());
        assert!(!sk.expose_secret().is_empty());
        Ok(())
    }
}
