"""
Tests for Rust FFI cryptographic library integration.
"""

import pytest
import os
from teeth_gnashing.crypto_ffi import CryptoFFI, CryptoFFIError, get_crypto_ffi


class TestCryptoFFI:
    """Test CryptoFFI class functionality"""

    def test_get_crypto_ffi_singleton(self):
        """Test that get_crypto_ffi returns singleton instance"""
        ffi1 = get_crypto_ffi()
        ffi2 = get_crypto_ffi()
        assert ffi1 is ffi2

    def test_derive_key_basic(self):
        """Test basic key derivation"""
        try:
            crypto_ffi = get_crypto_ffi()
            seed = 12345
            tick = 100
            salt = b"test_salt_12345678901234567890"
            output_len = 32
            
            key = crypto_ffi.derive_key(seed, tick, salt, output_len)
            
            assert len(key) == output_len
            assert isinstance(key, bytes)
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_derive_key_deterministic(self):
        """Test that key derivation is deterministic"""
        try:
            crypto_ffi = get_crypto_ffi()
            seed = 12345
            tick = 100
            salt = b"test_salt"
            
            key1 = crypto_ffi.derive_key(seed, tick, salt, 32)
            key2 = crypto_ffi.derive_key(seed, tick, salt, 32)
            
            assert key1 == key2
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_derive_key_different_seeds(self):
        """Test that different seeds produce different keys"""
        try:
            crypto_ffi = get_crypto_ffi()
            tick = 100
            salt = b"test_salt"
            
            key1 = crypto_ffi.derive_key(12345, tick, salt, 32)
            key2 = crypto_ffi.derive_key(54321, tick, salt, 32)
            
            assert key1 != key2
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_derive_key_different_ticks(self):
        """Test that different ticks produce different keys"""
        try:
            crypto_ffi = get_crypto_ffi()
            seed = 12345
            salt = b"test_salt"
            
            key1 = crypto_ffi.derive_key(seed, 100, salt, 32)
            key2 = crypto_ffi.derive_key(seed, 200, salt, 32)
            
            assert key1 != key2
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_derive_key_different_salts(self):
        """Test that different salts produce different keys"""
        try:
            crypto_ffi = get_crypto_ffi()
            seed = 12345
            tick = 100
            
            key1 = crypto_ffi.derive_key(seed, tick, b"salt1", 32)
            key2 = crypto_ffi.derive_key(seed, tick, b"salt2", 32)
            
            assert key1 != key2
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_generate_keystream_basic(self):
        """Test basic keystream generation"""
        try:
            crypto_ffi = get_crypto_ffi()
            key = os.urandom(32)
            salt = b"test_salt"
            output_len = 64
            
            keystream = crypto_ffi.generate_keystream(key, salt, output_len)
            
            assert len(keystream) == output_len
            assert isinstance(keystream, bytes)
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_generate_keystream_deterministic(self):
        """Test that keystream generation is deterministic"""
        try:
            crypto_ffi = get_crypto_ffi()
            key = os.urandom(32)
            salt = b"test_salt"
            
            keystream1 = crypto_ffi.generate_keystream(key, salt, 64)
            keystream2 = crypto_ffi.generate_keystream(key, salt, 64)
            
            assert keystream1 == keystream2
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_generate_keystream_different_keys(self):
        """Test that different keys produce different keystreams"""
        try:
            crypto_ffi = get_crypto_ffi()
            key1 = os.urandom(32)
            key2 = os.urandom(32)
            salt = b"test_salt"
            
            keystream1 = crypto_ffi.generate_keystream(key1, salt, 64)
            keystream2 = crypto_ffi.generate_keystream(key2, salt, 64)
            
            assert keystream1 != keystream2
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_generate_keystream_short_key(self):
        """Test that short key raises error"""
        try:
            crypto_ffi = get_crypto_ffi()
            short_key = os.urandom(16)  # Less than 32 bytes
            salt = b"test_salt"
            
            with pytest.raises(CryptoFFIError):
                crypto_ffi.generate_keystream(short_key, salt, 64)
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_encrypt_xor_basic(self):
        """Test basic XOR encryption"""
        try:
            crypto_ffi = get_crypto_ffi()
            data = b"Hello, World!"
            keystream = os.urandom(len(data))
            
            encrypted = crypto_ffi.encrypt_xor(data, keystream)
            
            assert len(encrypted) == len(data)
            assert isinstance(encrypted, bytes)
            assert encrypted != data  # Should be different
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_encrypt_xor_self_inverse(self):
        """Test that XOR is its own inverse"""
        try:
            crypto_ffi = get_crypto_ffi()
            data = b"Hello, World!"
            keystream = os.urandom(len(data))
            
            encrypted = crypto_ffi.encrypt_xor(data, keystream)
            decrypted = crypto_ffi.encrypt_xor(encrypted, keystream)
            
            assert decrypted == data
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_encrypt_xor_different_keystreams(self):
        """Test that different keystreams produce different results"""
        try:
            crypto_ffi = get_crypto_ffi()
            data = b"Hello, World!"
            keystream1 = os.urandom(len(data))
            keystream2 = os.urandom(len(data))
            
            encrypted1 = crypto_ffi.encrypt_xor(data, keystream1)
            encrypted2 = crypto_ffi.encrypt_xor(data, keystream2)
            
            assert encrypted1 != encrypted2
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_fast_hash_basic(self):
        """Test basic fast hashing"""
        try:
            crypto_ffi = get_crypto_ffi()
            data = b"test data"
            
            hash_result = crypto_ffi.fast_hash(data, 32)
            
            assert len(hash_result) == 32
            assert isinstance(hash_result, bytes)
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_fast_hash_deterministic(self):
        """Test that fast hash is deterministic"""
        try:
            crypto_ffi = get_crypto_ffi()
            data = b"test data"
            
            hash1 = crypto_ffi.fast_hash(data, 32)
            hash2 = crypto_ffi.fast_hash(data, 32)
            
            assert hash1 == hash2
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_fast_hash_different_inputs(self):
        """Test that different inputs produce different hashes"""
        try:
            crypto_ffi = get_crypto_ffi()
            
            hash1 = crypto_ffi.fast_hash(b"input1", 32)
            hash2 = crypto_ffi.fast_hash(b"input2", 32)
            
            assert hash1 != hash2
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_fast_hash_different_lengths(self):
        """Test fast hash with different output lengths"""
        try:
            crypto_ffi = get_crypto_ffi()
            data = b"test data"
            
            hash16 = crypto_ffi.fast_hash(data, 16)
            hash32 = crypto_ffi.fast_hash(data, 32)
            hash64 = crypto_ffi.fast_hash(data, 64)
            
            assert len(hash16) == 16
            assert len(hash32) == 32
            assert len(hash64) == 64
        except CryptoFFIError:
            pytest.skip("Rust library not available")


class TestCryptoFFIIntegration:
    """Test integration of CryptoFFI with client operations"""

    def test_key_derivation_and_keystream(self):
        """Test key derivation followed by keystream generation"""
        try:
            crypto_ffi = get_crypto_ffi()
            seed = 12345
            tick = 100
            salt = b"test_salt"
            
            # Derive key
            key = crypto_ffi.derive_key(seed, tick, salt, 32)
            
            # Generate keystream
            keystream = crypto_ffi.generate_keystream(key, salt, 64)
            
            assert len(key) == 32
            assert len(keystream) == 64
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_full_encryption_cycle(self):
        """Test full encryption cycle with FFI functions"""
        try:
            crypto_ffi = get_crypto_ffi()
            seed = 12345
            tick = 100
            salt = b"test_salt"
            plaintext = b"Hello, World! This is a test message."
            
            # Derive key
            key = crypto_ffi.derive_key(seed, tick, salt, 32)
            
            # Generate keystream
            keystream = crypto_ffi.generate_keystream(key, salt, len(plaintext))
            
            # Encrypt
            encrypted = crypto_ffi.encrypt_xor(plaintext, keystream)
            
            # Decrypt (XOR is its own inverse)
            decrypted = crypto_ffi.encrypt_xor(encrypted, keystream)
            
            assert decrypted == plaintext
        except CryptoFFIError:
            pytest.skip("Rust library not available")

    def test_hash_and_encrypt(self):
        """Test hashing followed by encryption"""
        try:
            crypto_ffi = get_crypto_ffi()
            data = b"test data for hashing"
            
            # Hash the data
            hash_result = crypto_ffi.fast_hash(data, 32)
            
            # Use hash as key for encryption
            plaintext = b"message to encrypt"
            keystream = crypto_ffi.generate_keystream(hash_result, b"salt", len(plaintext))
            
            # Encrypt
            encrypted = crypto_ffi.encrypt_xor(plaintext, keystream)
            
            # Decrypt
            decrypted = crypto_ffi.encrypt_xor(encrypted, keystream)
            
            assert decrypted == plaintext
        except CryptoFFIError:
            pytest.skip("Rust library not available")
