"""Property-based tests for transcript encryption.

Tests encrypt/decrypt roundtrip, key management, and passthrough behavior.
"""

from __future__ import annotations

import hypothesis.strategies as st
import pytest
from hypothesis import given, settings

try:
    from cryptography.fernet import Fernet
    HAS_CRYPTO = True
except ImportError:
    HAS_CRYPTO = False

from autodebug_probe.encryption import (
    TranscriptEncryption,
    generate_key,
)


def _make_encryption():
    """Create encryption instance with a known test key."""
    if not HAS_CRYPTO:
        pytest.skip("cryptography package not installed")
    key = Fernet.generate_key().decode()
    return TranscriptEncryption(key=key)


def _make_no_encryption():
    """Create encryption instance with no key (passthrough mode)."""
    return TranscriptEncryption(key=None)


@pytest.mark.skipif(not HAS_CRYPTO, reason="cryptography not installed")
class TestEncryptDecryptRoundtrip:
    """encrypt(x) -> decrypt(x) should return original data."""

    @given(st.binary(min_size=0, max_size=5000))
    @settings(max_examples=50)
    def test_roundtrip(self, data):
        """decrypt(encrypt(data)) == data for all byte strings."""
        enc = _make_encryption()
        encrypted = enc.encrypt(data)
        decrypted = enc.decrypt(encrypted)
        assert decrypted == data, "Roundtrip failed"

    @given(st.binary(min_size=1, max_size=1000))
    @settings(max_examples=30)
    def test_encrypted_differs_from_plaintext(self, data):
        """Encrypted output should differ from plaintext."""
        enc = _make_encryption()
        encrypted = enc.encrypt(data)
        assert encrypted != data, "Encryption produced same output as input"

    @given(st.binary(min_size=1, max_size=1000))
    @settings(max_examples=30)
    def test_both_encryptions_decrypt_to_same(self, data):
        """Two encryptions of same data should both decrypt to original."""
        enc = _make_encryption()
        e1 = enc.encrypt(data)
        e2 = enc.encrypt(data)
        d1 = enc.decrypt(e1)
        d2 = enc.decrypt(e2)
        assert d1 == d2 == data


class TestPassthroughMode:
    """When encryption is disabled, data passes through unchanged."""

    @given(st.binary(min_size=0, max_size=1000))
    @settings(max_examples=30)
    def test_encrypt_passthrough(self, data):
        """Without key, encrypt returns data unchanged."""
        enc = _make_no_encryption()
        result = enc.encrypt(data)
        assert result == data

    @given(st.binary(min_size=0, max_size=1000))
    @settings(max_examples=30)
    def test_decrypt_passthrough(self, data):
        """Without key, decrypt returns data unchanged."""
        enc = _make_no_encryption()
        result = enc.decrypt(data)
        assert result == data

    def test_enabled_is_false(self):
        """Without key, enabled should be False."""
        enc = _make_no_encryption()
        assert enc.enabled is False


class TestKeyManagement:
    """Tests for key generation and validation."""

    @pytest.mark.skipif(not HAS_CRYPTO, reason="cryptography not installed")
    def test_generate_key_produces_valid_fernet_key(self):
        """generate_key() should produce a valid Fernet key."""
        key = generate_key()
        assert isinstance(key, str)
        enc = TranscriptEncryption(key=key)
        assert enc.enabled is True

    @pytest.mark.skipif(not HAS_CRYPTO, reason="cryptography not installed")
    def test_wrong_key_fails_decrypt(self):
        """Decrypting with a different key should fail."""
        key1 = Fernet.generate_key().decode()
        key2 = Fernet.generate_key().decode()
        enc1 = TranscriptEncryption(key=key1)
        enc2 = TranscriptEncryption(key=key2)

        data = b"secret test data"
        encrypted = enc1.encrypt(data)

        with pytest.raises(Exception):
            enc2.decrypt(encrypted)

    @pytest.mark.skipif(not HAS_CRYPTO, reason="cryptography not installed")
    def test_enabled_is_true_with_key(self):
        """With a valid key, enabled should be True."""
        key = Fernet.generate_key().decode()
        enc = TranscriptEncryption(key=key)
        assert enc.enabled is True
