"""Tests for utils.crypto — AES-256-GCM encryption and key derivation."""

import os
import tempfile

import pytest

from utils.crypto import (
    CryptoEngine,
    KEY_SIZE,
    NONCE_SIZE,
    derive_key,
    encrypt_file,
    decrypt_file,
    HAS_ARGON2,
)


class TestCryptoEngine:

    def test_encrypt_decrypt_roundtrip(self):
        """Encrypt then decrypt returns the original plaintext."""
        key = os.urandom(KEY_SIZE)
        engine = CryptoEngine(key)

        plaintext = b"Sensitive credential data: admin:p@ssw0rd!"
        ciphertext = engine.encrypt(plaintext)

        # Ciphertext should differ from plaintext
        assert ciphertext != plaintext
        # Should include nonce prefix
        assert len(ciphertext) > len(plaintext)

        decrypted = engine.decrypt(ciphertext)
        assert decrypted == plaintext

    def test_encrypt_file_decrypt_file(self, tmp_path):
        """encrypt_file() and decrypt_file() round-trip a file correctly."""
        src = str(tmp_path / "source.bin")
        enc = str(tmp_path / "encrypted.bb")
        dec = str(tmp_path / "decrypted.bin")

        original_data = os.urandom(4096)
        with open(src, "wb") as f:
            f.write(original_data)

        password = b"test-passphrase-42"
        # Use pbkdf2 to avoid argon2 dependency issues
        encrypt_file(src, enc, password, method="pbkdf2")
        decrypt_file(enc, dec, password, method="pbkdf2")

        with open(dec, "rb") as f:
            recovered = f.read()

        assert recovered == original_data

    def test_different_keys_fail(self):
        """Decrypting with a different key raises an error."""
        key1 = os.urandom(KEY_SIZE)
        key2 = os.urandom(KEY_SIZE)
        engine1 = CryptoEngine(key1)
        engine2 = CryptoEngine(key2)

        plaintext = b"secret data"
        ciphertext = engine1.encrypt(plaintext)

        with pytest.raises(Exception):
            engine2.decrypt(ciphertext)

    def test_key_derivation_argon2(self):
        """derive_key with argon2id returns a 32-byte key (or falls back to pbkdf2)."""
        password = b"my-strong-password"
        salt = os.urandom(16)

        key = derive_key(password, salt, method="argon2id")

        assert isinstance(key, bytes)
        assert len(key) == KEY_SIZE

        # Same inputs produce the same key
        key2 = derive_key(password, salt, method="argon2id")
        assert key == key2

        # Different salt produces a different key
        salt2 = os.urandom(16)
        key3 = derive_key(password, salt2, method="argon2id")
        assert key3 != key

    def test_key_derivation_pbkdf2(self):
        """derive_key with pbkdf2 returns a 32-byte key."""
        password = b"another-password"
        salt = os.urandom(16)

        key = derive_key(password, salt, method="pbkdf2")

        assert isinstance(key, bytes)
        assert len(key) == KEY_SIZE

        # Same inputs produce the same key
        key2 = derive_key(password, salt, method="pbkdf2")
        assert key == key2

        # Different password produces a different key
        key3 = derive_key(b"different-password", salt, method="pbkdf2")
        assert key3 != key
