import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))

from obsidian256 import obsidian256cipher, keygenerator


def test_basic_encrypt_decrypt():
    cipher = obsidian256cipher()
    plaintext = b"test message"
    ciphertext = cipher.encrypt(plaintext)
    decrypted = cipher.decrypt(ciphertext)
    assert decrypted == plaintext


def test_custom_key():
    key = obsidian256cipher.generate_key()
    iv = obsidian256cipher.generate_iv()
    
    cipher = obsidian256cipher(key=key, iv=iv)
    plaintext = b"custom key test"
    ciphertext = cipher.encrypt(plaintext)
    
    cipher2 = obsidian256cipher(key=key, iv=iv)
    decrypted = cipher2.decrypt(ciphertext)
    assert decrypted == plaintext


def test_password_key():
    kg = keygenerator()
    key, salt = kg.generate_master_key(password="test_password")
    
    cipher = obsidian256cipher(key=key)
    plaintext = b"password protected"
    ciphertext = cipher.encrypt(plaintext)
    
    key2, _ = kg.generate_master_key(password="test_password", salt=salt)
    cipher2 = obsidian256cipher(key=key2, iv=cipher.get_iv())
    decrypted = cipher2.decrypt(ciphertext)
    assert decrypted == plaintext


def test_various_sizes():
    cipher = obsidian256cipher()
    
    sizes = [1, 16, 32, 64, 100, 256, 1000]
    for size in sizes:
        plaintext = bytes([i % 256 for i in range(size)])
        ciphertext = cipher.encrypt(plaintext)
        decrypted = cipher.decrypt(ciphertext)
        assert decrypted == plaintext


def test_string_input():
    cipher = obsidian256cipher()
    plaintext = "unicode string: hello world"
    ciphertext = cipher.encrypt(plaintext)
    decrypted = cipher.decrypt(ciphertext)
    assert decrypted == plaintext.encode("utf-8")


def test_round_count():
    for _ in range(10):
        key = obsidian256cipher.generate_key()
        cipher = obsidian256cipher(key=key)
        rounds = cipher.get_round_count()
        assert 12 <= rounds <= 20


def test_avalanche():
    key1 = b"\x00" * 32
    key2 = b"\x01" + b"\x00" * 31
    iv = b"\x00" * 32
    
    cipher1 = obsidian256cipher(key=key1, iv=iv)
    cipher2 = obsidian256cipher(key=key2, iv=iv)
    
    plaintext = b"\x00" * 32
    ct1 = cipher1.encrypt(plaintext)
    ct2 = cipher2.encrypt(plaintext)
    
    diff_bits = sum(bin(b1 ^ b2).count("1") for b1, b2 in zip(ct1, ct2))
    total_bits = len(ct1) * 8
    
    assert diff_bits > total_bits * 0.3


if __name__ == "__main__":
    test_basic_encrypt_decrypt()
    print("test_basic_encrypt_decrypt: passed")
    
    test_custom_key()
    print("test_custom_key: passed")
    
    test_password_key()
    print("test_password_key: passed")
    
    test_various_sizes()
    print("test_various_sizes: passed")
    
    test_string_input()
    print("test_string_input: passed")
    
    test_round_count()
    print("test_round_count: passed")
    
    test_avalanche()
    print("test_avalanche: passed")
    
    print("\nall tests passed!")
