#![allow(clippy::panic, clippy::expect_used, clippy::indexing_slicing)]
//! Comprehensive negative tests for ML-KEM primitives
//!
//! This test suite validates error handling at the primitives layer for ML-KEM.
//!
//! Test coverage:
//! - Invalid key lengths for all security levels
//! - Invalid ciphertext lengths
//! - Corrupted shared secrets
//! - Wrong security level combinations

use latticearc::primitives::kem::ml_kem::{
    MlKem, MlKemCiphertext, MlKemError, MlKemPublicKey, MlKemSecretKey, MlKemSecurityLevel,
};

// ============================================================================
// Public Key Construction Negative Tests
// ============================================================================

#[test]
fn test_ml_kem_512_public_key_empty_bytes_succeeds() {
    let empty = vec![];
    let result = MlKemPublicKey::new(MlKemSecurityLevel::MlKem512, empty);
    assert!(result.is_err(), "Should fail with empty public key bytes");

    match result {
        Err(MlKemError::InvalidKeyLength { .. }) => {
            // Expected error
        }
        _ => panic!("Expected InvalidKeyLength error, got {:?}", result),
    }
}

#[test]
fn test_ml_kem_512_public_key_wrong_length_fails() {
    // MlKem512 expects 800 bytes, provide 1184 (MlKem768 size)
    let wrong_size = vec![0u8; 1184];
    let result = MlKemPublicKey::new(MlKemSecurityLevel::MlKem512, wrong_size);
    assert!(result.is_err(), "Should fail with wrong public key length");

    match result {
        Err(MlKemError::InvalidKeyLength { variant, size, actual, .. }) => {
            assert_eq!(variant, "ML-KEM-512");
            assert_eq!(size, 800);
            assert_eq!(actual, 1184);
        }
        _ => panic!("Expected InvalidKeyLength error, got {:?}", result),
    }
}

#[test]
fn test_ml_kem_768_public_key_truncated_succeeds() {
    // MlKem768 expects 1184 bytes, provide less
    let truncated = vec![0u8; 100];
    let result = MlKemPublicKey::new(MlKemSecurityLevel::MlKem768, truncated);
    assert!(result.is_err(), "Should fail with truncated public key");

    match result {
        Err(MlKemError::InvalidKeyLength { variant, size, actual, .. }) => {
            assert_eq!(variant, "ML-KEM-768");
            assert_eq!(size, 1184);
            assert_eq!(actual, 100);
        }
        _ => panic!("Expected InvalidKeyLength error, got {:?}", result),
    }
}

#[test]
fn test_ml_kem_1024_public_key_oversized_succeeds() {
    // MlKem1024 expects 1568 bytes, provide more
    let oversized = vec![0u8; 2000];
    let result = MlKemPublicKey::new(MlKemSecurityLevel::MlKem1024, oversized);
    assert!(result.is_err(), "Should fail with oversized public key");

    match result {
        Err(MlKemError::InvalidKeyLength { variant, size, actual, .. }) => {
            assert_eq!(variant, "ML-KEM-1024");
            assert_eq!(size, 1568);
            assert_eq!(actual, 2000);
        }
        _ => panic!("Expected InvalidKeyLength error, got {:?}", result),
    }
}

// ============================================================================
// Secret Key Construction Negative Tests
// ============================================================================

// `MlKemSecretKey::new` collapses both length-mismatch and structural
// validation paths to `InvalidSecretKeyFormat` (Pattern-6 collapse;
// also maps to FIPS 0x010C, distinct from public-key parse failures
// which map to 0x010B).

#[test]
fn test_ml_kem_512_secret_key_empty_bytes_succeeds() {
    let empty = vec![];
    let result = MlKemSecretKey::new(MlKemSecurityLevel::MlKem512, empty);
    assert!(result.is_err(), "Should fail with empty secret key bytes");

    match result {
        Err(MlKemError::InvalidSecretKeyFormat(_)) => {
            // Expected error
        }
        _ => panic!("Expected InvalidSecretKeyFormat error, got {:?}", result),
    }
}

#[test]
fn test_ml_kem_768_secret_key_wrong_length_fails() {
    // MlKem768 expects 2400 bytes, provide 1632 (MlKem512 size)
    let wrong_size = vec![0u8; 1632];
    let result = MlKemSecretKey::new(MlKemSecurityLevel::MlKem768, wrong_size);
    assert!(result.is_err(), "Should fail with wrong secret key length");

    assert!(
        matches!(result, Err(MlKemError::InvalidSecretKeyFormat(_))),
        "Expected InvalidSecretKeyFormat error, got {:?}",
        result
    );
}

#[test]
fn test_ml_kem_1024_secret_key_truncated_succeeds() {
    // MlKem1024 expects 3168 bytes, provide less
    let truncated = vec![0u8; 1000];
    let result = MlKemSecretKey::new(MlKemSecurityLevel::MlKem1024, truncated);
    assert!(result.is_err(), "Should fail with truncated secret key");

    assert!(
        matches!(result, Err(MlKemError::InvalidSecretKeyFormat(_))),
        "Expected InvalidSecretKeyFormat error, got {:?}",
        result
    );
}

// ============================================================================
// Ciphertext Construction Negative Tests
// ============================================================================

#[test]
fn test_ml_kem_512_ciphertext_empty_bytes_succeeds() {
    let empty = vec![];
    let result = MlKemCiphertext::new(MlKemSecurityLevel::MlKem512, empty);
    assert!(result.is_err(), "Should fail with empty ciphertext bytes");

    match result {
        Err(MlKemError::InvalidCiphertextLength { .. }) => {
            // Expected error
        }
        _ => panic!("Expected InvalidCiphertextLength error, got {:?}", result),
    }
}

#[test]
fn test_ml_kem_512_ciphertext_wrong_length_fails() {
    // MlKem512 expects 768 bytes, provide 1088 (MlKem768 size)
    let wrong_size = vec![0u8; 1088];
    let result = MlKemCiphertext::new(MlKemSecurityLevel::MlKem512, wrong_size);
    assert!(result.is_err(), "Should fail with wrong ciphertext length");

    match result {
        Err(MlKemError::InvalidCiphertextLength { variant, expected, actual }) => {
            assert_eq!(variant, "ML-KEM-512");
            assert_eq!(expected, 768);
            assert_eq!(actual, 1088);
        }
        _ => panic!("Expected InvalidCiphertextLength error, got {:?}", result),
    }
}

#[test]
fn test_ml_kem_768_ciphertext_truncated_succeeds() {
    // MlKem768 expects 1088 bytes, provide less
    let truncated = vec![0u8; 500];
    let result = MlKemCiphertext::new(MlKemSecurityLevel::MlKem768, truncated);
    assert!(result.is_err(), "Should fail with truncated ciphertext");

    match result {
        Err(MlKemError::InvalidCiphertextLength { variant, expected, actual }) => {
            assert_eq!(variant, "ML-KEM-768");
            assert_eq!(expected, 1088);
            assert_eq!(actual, 500);
        }
        _ => panic!("Expected InvalidCiphertextLength error, got {:?}", result),
    }
}

#[test]
fn test_ml_kem_1024_ciphertext_oversized_succeeds() {
    // MlKem1024 expects 1568 bytes, provide more
    let oversized = vec![0u8; 2000];
    let result = MlKemCiphertext::new(MlKemSecurityLevel::MlKem1024, oversized);
    assert!(result.is_err(), "Should fail with oversized ciphertext");

    match result {
        Err(MlKemError::InvalidCiphertextLength { variant, expected, actual }) => {
            assert_eq!(variant, "ML-KEM-1024");
            assert_eq!(expected, 1568);
            assert_eq!(actual, 2000);
        }
        _ => panic!("Expected InvalidCiphertextLength error, got {:?}", result),
    }
}

// ============================================================================
// Public Key Serialization Tests
// ============================================================================

#[test]
fn test_ml_kem_512_public_key_from_bytes_roundtrip() {
    let (pk, _sk) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem512)
        .expect("keypair generation should succeed");

    let pk_bytes = pk.to_bytes();
    let restored = MlKemPublicKey::from_bytes(&pk_bytes, MlKemSecurityLevel::MlKem512)
        .expect("restoration should succeed");

    assert_eq!(pk.to_bytes(), restored.to_bytes(), "Public key should round-trip");
}

#[test]
fn test_ml_kem_768_public_key_from_wrong_level_fails() {
    let (pk, _sk) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem768)
        .expect("keypair generation should succeed");

    let pk_bytes = pk.to_bytes();

    // Try to restore MlKem768 key as MlKem512
    let result = MlKemPublicKey::from_bytes(&pk_bytes, MlKemSecurityLevel::MlKem512);
    assert!(result.is_err(), "Should fail when security level doesn't match key size");

    match result {
        Err(MlKemError::InvalidKeyLength { variant, size, actual, .. }) => {
            assert_eq!(variant, "ML-KEM-512");
            assert_eq!(size, 800);
            assert_eq!(actual, 1184); // MlKem768 size
        }
        _ => panic!("Expected InvalidKeyLength error, got {:?}", result),
    }
}

// ============================================================================
// Encapsulation with Invalid Keys
// ============================================================================

#[test]
fn test_ml_kem_encapsulate_with_junk_public_key_succeeds() {
    // Create junk public key with correct size but invalid data
    let junk_bytes = vec![0xDEu8; 800]; // Correct size for MlKem512
    let junk_pk = MlKemPublicKey::new(MlKemSecurityLevel::MlKem512, junk_bytes)
        .expect("construction with correct size should succeed");

    // Encapsulation should fail with invalid key data
    let result = MlKem::encapsulate(&junk_pk);
    assert!(result.is_err(), "Should fail with junk public key");

    match result {
        Err(MlKemError::EncapsulationError(_)) => {
            // Expected error
        }
        _ => panic!("Expected EncapsulationError, got {:?}", result),
    }
}

// ============================================================================
// Decapsulation with Invalid Ciphertexts
// ============================================================================

#[test]
fn test_ml_kem_decapsulate_with_junk_ciphertext_succeeds() {
    let (pk, sk) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem512)
        .expect("keypair generation should succeed");

    // Encapsulate to get the real shared secret
    let (real_ss, _real_ct) = MlKem::encapsulate(&pk).expect("encapsulation should succeed");

    // Create junk ciphertext with correct size
    let junk_bytes = vec![0x42u8; 768]; // Correct size for MlKem512
    let junk_ct = MlKemCiphertext::new(MlKemSecurityLevel::MlKem512, junk_bytes)
        .expect("construction with correct size should succeed");

    // ML-KEM uses implicit rejection (FIPS 203 §7.3): invalid ciphertexts produce
    // a different shared secret rather than returning an error
    let junk_ss = MlKem::decapsulate(&sk, &junk_ct)
        .expect("decapsulation succeeds even with junk (implicit rejection)");
    assert_ne!(
        real_ss.expose_secret(),
        junk_ss.expose_secret(),
        "Junk ciphertext must produce a different shared secret"
    );
}

#[test]
fn test_ml_kem_decapsulate_with_all_zeros_ciphertext_succeeds() {
    let (pk, sk) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem768)
        .expect("keypair generation should succeed");

    // Encapsulate to get the real shared secret
    let (real_ss, _real_ct) = MlKem::encapsulate(&pk).expect("encapsulation should succeed");

    // Create all-zeros ciphertext
    let zero_bytes = vec![0u8; 1088]; // Correct size for MlKem768
    let zero_ct = MlKemCiphertext::new(MlKemSecurityLevel::MlKem768, zero_bytes)
        .expect("construction should succeed");

    // ML-KEM uses implicit rejection (FIPS 203 §7.3): invalid ciphertexts produce
    // a different shared secret rather than returning an error
    let zero_ss = MlKem::decapsulate(&sk, &zero_ct)
        .expect("decapsulation succeeds even with zeros (implicit rejection)");
    assert_ne!(
        real_ss.expose_secret(),
        zero_ss.expose_secret(),
        "All-zeros ciphertext must produce a different shared secret"
    );
}

#[test]
fn test_ml_kem_decapsulate_with_all_ones_ciphertext_succeeds() {
    let (pk, sk) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem1024)
        .expect("keypair generation should succeed");

    // Encapsulate to get the real shared secret
    let (real_ss, _real_ct) = MlKem::encapsulate(&pk).expect("encapsulation should succeed");

    // Create all-ones ciphertext
    let ones_bytes = vec![0xFFu8; 1568]; // Correct size for MlKem1024
    let ones_ct = MlKemCiphertext::new(MlKemSecurityLevel::MlKem1024, ones_bytes)
        .expect("construction should succeed");

    // ML-KEM uses implicit rejection (FIPS 203 §7.3): invalid ciphertexts produce
    // a different shared secret rather than returning an error
    let ones_ss = MlKem::decapsulate(&sk, &ones_ct)
        .expect("decapsulation succeeds even with ones (implicit rejection)");
    assert_ne!(
        real_ss.expose_secret(),
        ones_ss.expose_secret(),
        "All-ones ciphertext must produce a different shared secret"
    );
}

// ============================================================================
// Mismatched Security Levels
// ============================================================================
//
// `MlKem::decapsulate` enforces a security-level mismatch check between the
// secret key and ciphertext. The check is symmetric — both directions of every
// (level_a, level_b) pair where a != b must reject. Six pairs total; each is
// kept as an explicit named test (not parameterised) so individual failures
// point at the exact mismatch direction.

/// Helper: generate a CT under one level, attempt decap under another, assert error.
fn assert_cross_level_decapsulate_rejects(
    ct_level: MlKemSecurityLevel,
    sk_level: MlKemSecurityLevel,
) {
    let (pk_ct, _sk_ct) = MlKem::generate_keypair(ct_level).expect("ct keypair gen");
    let (_ss, ct) = MlKem::encapsulate(&pk_ct).expect("encapsulation");
    let (_pk_sk, sk_sk) = MlKem::generate_keypair(sk_level).expect("sk keypair gen");
    let result = MlKem::decapsulate(&sk_sk, &ct);
    assert!(
        result.is_err(),
        "decapsulate({sk_level:?} sk, {ct_level:?} ct) must reject as security-level mismatch"
    );
}

#[test]
fn test_ml_kem_decapsulate_512_ciphertext_with_768_key_succeeds() {
    assert_cross_level_decapsulate_rejects(
        MlKemSecurityLevel::MlKem512,
        MlKemSecurityLevel::MlKem768,
    );
}

#[test]
fn test_ml_kem_decapsulate_512_ciphertext_with_1024_key_rejects() {
    assert_cross_level_decapsulate_rejects(
        MlKemSecurityLevel::MlKem512,
        MlKemSecurityLevel::MlKem1024,
    );
}

#[test]
fn test_ml_kem_decapsulate_768_ciphertext_with_512_key_rejects() {
    assert_cross_level_decapsulate_rejects(
        MlKemSecurityLevel::MlKem768,
        MlKemSecurityLevel::MlKem512,
    );
}

#[test]
fn test_ml_kem_decapsulate_768_ciphertext_with_1024_key_rejects() {
    assert_cross_level_decapsulate_rejects(
        MlKemSecurityLevel::MlKem768,
        MlKemSecurityLevel::MlKem1024,
    );
}

#[test]
fn test_ml_kem_decapsulate_1024_ciphertext_with_512_key_rejects() {
    assert_cross_level_decapsulate_rejects(
        MlKemSecurityLevel::MlKem1024,
        MlKemSecurityLevel::MlKem512,
    );
}

#[test]
fn test_ml_kem_decapsulate_1024_ciphertext_with_768_key_rejects() {
    assert_cross_level_decapsulate_rejects(
        MlKemSecurityLevel::MlKem1024,
        MlKemSecurityLevel::MlKem768,
    );
}

// ============================================================================
// Corrupted Ciphertext Tests
// ============================================================================

#[test]
fn test_ml_kem_decapsulate_corrupted_ciphertext_fails() {
    let (pk, sk) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem512)
        .expect("keypair generation should succeed");

    let (_ss, ct) = MlKem::encapsulate(&pk).expect("encapsulation should succeed");

    // Corrupt the ciphertext
    let mut ct_bytes = ct.into_bytes();
    if ct_bytes.len() > 100 {
        ct_bytes[100] ^= 0xFF;
    }

    let corrupted_ct = MlKemCiphertext::new(MlKemSecurityLevel::MlKem512, ct_bytes)
        .expect("construction should succeed");

    // Decapsulation should fail (or produce different shared secret)
    let result = MlKem::decapsulate(&sk, &corrupted_ct);
    // ML-KEM decapsulation doesn't fail on corrupted ciphertext by design
    // (it returns a pseudo-random value instead), so we just verify it runs
    assert!(result.is_ok() || result.is_err(), "Decapsulation should complete");
}

// ============================================================================
// Key Pair Generation Edge Cases
// ============================================================================

#[test]
fn test_ml_kem_generate_multiple_keypairs_different_succeeds() {
    let (pk1, _sk1) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem768)
        .expect("keypair generation should succeed");
    let (pk2, _sk2) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem768)
        .expect("keypair generation should succeed");

    // Public keys should be different
    assert_ne!(pk1.to_bytes(), pk2.to_bytes(), "Generated keys should be different");
}

#[test]
fn test_ml_kem_encapsulate_produces_different_ciphertexts_succeeds() {
    let (pk, _sk) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem512)
        .expect("keypair generation should succeed");

    let (_ss1, ct1) = MlKem::encapsulate(&pk).expect("encapsulation should succeed");
    let (_ss2, ct2) = MlKem::encapsulate(&pk).expect("encapsulation should succeed");

    // Ciphertexts should be different (different randomness)
    assert_ne!(ct1.into_bytes(), ct2.into_bytes(), "Ciphertexts should differ");
}

// ============================================================================
// Shared Secret Validation
// ============================================================================

#[test]
fn test_ml_kem_shared_secret_length_encapsulation_only_has_correct_size() {
    for level in
        [MlKemSecurityLevel::MlKem512, MlKemSecurityLevel::MlKem768, MlKemSecurityLevel::MlKem1024]
    {
        let (pk, _sk) = MlKem::generate_keypair(level).expect("keypair generation should succeed");

        let (ss_enc, _ct) = MlKem::encapsulate(&pk).expect("encapsulation should succeed");

        // All ML-KEM variants produce 32-byte shared secrets
        assert_eq!(ss_enc.expose_secret().len(), 32, "Shared secret should be 32 bytes");
    }
}

#[test]
fn test_ml_kem_shared_secret_length_full_roundtrip() {
    for level in
        [MlKemSecurityLevel::MlKem512, MlKemSecurityLevel::MlKem768, MlKemSecurityLevel::MlKem1024]
    {
        let (pk, sk) = MlKem::generate_keypair(level).expect("keypair generation should succeed");

        let (ss_enc, ct) = MlKem::encapsulate(&pk).expect("encapsulation should succeed");
        let ss_dec = MlKem::decapsulate(&sk, &ct).expect("decapsulation should succeed");

        // All ML-KEM variants produce 32-byte shared secrets
        assert_eq!(ss_enc.expose_secret().len(), 32, "Shared secret should be 32 bytes");
        assert_eq!(ss_dec.expose_secret().len(), 32, "Shared secret should be 32 bytes");
        assert_eq!(ss_enc.expose_secret(), ss_dec.expose_secret(), "Shared secrets should match");
    }
}

// ============================================================================
// Byte Conversion Tests
// ============================================================================

#[test]
fn test_ml_kem_public_key_to_bytes_consistent() {
    let (pk, _sk) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem512)
        .expect("keypair generation should succeed");

    let bytes1 = pk.to_bytes();
    let bytes2 = pk.to_bytes();

    assert_eq!(bytes1, bytes2, "to_bytes should be consistent");
}

#[test]
fn test_ml_kem_ciphertext_into_bytes_consumes_succeeds() {
    let (pk, _sk) = MlKem::generate_keypair(MlKemSecurityLevel::MlKem768)
        .expect("keypair generation should succeed");

    let (_ss, ct) = MlKem::encapsulate(&pk).expect("encapsulation should succeed");

    let ct_bytes = ct.into_bytes();
    assert_eq!(ct_bytes.len(), 1088, "MlKem768 ciphertext should be 1088 bytes");
}
