import json

import pytest
from mrp.auth import Keypair
from mrp.crypto import (
    decrypt_message,
    ed25519_to_x25519_private,
    ed25519_to_x25519_public,
    encrypt_message,
)


class TestKeyConversion:
    def test_ed25519_to_x25519_public(self):
        kp = Keypair.generate()
        x_pub = ed25519_to_x25519_public(kp.public_key)
        # X25519 public key should be 32 bytes
        from cryptography.hazmat.primitives import serialization
        raw = x_pub.public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
        assert len(raw) == 32

    def test_ed25519_to_x25519_private(self):
        kp = Keypair.generate()
        x_priv = ed25519_to_x25519_private(kp.seed)
        # Should produce a valid X25519 private key
        from cryptography.hazmat.primitives import serialization
        raw = x_priv.private_bytes(
            serialization.Encoding.Raw,
            serialization.PrivateFormat.Raw,
            serialization.NoEncryption(),
        )
        assert len(raw) == 32

    def test_different_keys_different_x25519(self):
        kp1 = Keypair.generate()
        kp2 = Keypair.generate()
        from cryptography.hazmat.primitives import serialization
        x1 = ed25519_to_x25519_public(kp1.public_key).public_bytes(
            serialization.Encoding.Raw, serialization.PublicFormat.Raw
        )
        x2 = ed25519_to_x25519_public(kp2.public_key).public_bytes(
            serialization.Encoding.Raw, serialization.PublicFormat.Raw
        )
        assert x1 != x2


class TestEncryptDecrypt:
    def test_round_trip(self):
        sender = Keypair.generate()
        recipient = Keypair.generate()
        plaintext = b'{"message": "hello"}'

        encrypted = encrypt_message(
            sender.seed,
            recipient.public_key,
            plaintext,
            "application/json",
        )

        # Verify encrypted body has all 5 required fields
        assert encrypted["v"] == 1
        assert "eph" in encrypted
        assert "nonce" in encrypted
        assert "ct" in encrypted
        assert encrypted["ct_content_type"] == "application/json"

        # Decrypt
        decrypted, ct = decrypt_message(
            recipient.seed,
            sender.public_key,
            encrypted,
        )
        assert decrypted == plaintext
        assert ct == "application/json"

    def test_decrypt_with_wrong_key_fails(self):
        sender = Keypair.generate()
        recipient = Keypair.generate()
        wrong = Keypair.generate()

        encrypted = encrypt_message(
            sender.seed,
            recipient.public_key,
            b"secret",
            "text/plain",
        )

        with pytest.raises(Exception):
            decrypt_message(wrong.seed, sender.public_key, encrypted)

    def test_different_encryptions_differ(self):
        sender = Keypair.generate()
        recipient = Keypair.generate()
        plaintext = b"same message"

        e1 = encrypt_message(sender.seed, recipient.public_key, plaintext)
        e2 = encrypt_message(sender.seed, recipient.public_key, plaintext)

        # Different ephemeral keys -> different ciphertexts
        assert e1["ct"] != e2["ct"]
        assert e1["eph"] != e2["eph"]

    def test_large_message(self):
        sender = Keypair.generate()
        recipient = Keypair.generate()
        plaintext = b"x" * 100_000

        encrypted = encrypt_message(sender.seed, recipient.public_key, plaintext)
        decrypted, _ = decrypt_message(recipient.seed, sender.public_key, encrypted)
        assert decrypted == plaintext

    def test_empty_message(self):
        sender = Keypair.generate()
        recipient = Keypair.generate()

        encrypted = encrypt_message(sender.seed, recipient.public_key, b"")
        decrypted, _ = decrypt_message(recipient.seed, sender.public_key, encrypted)
        assert decrypted == b""

    def test_unsupported_version(self):
        sender = Keypair.generate()
        recipient = Keypair.generate()

        encrypted = encrypt_message(sender.seed, recipient.public_key, b"test")
        encrypted["v"] = 2

        with pytest.raises(ValueError, match="Unsupported encryption version"):
            decrypt_message(recipient.seed, sender.public_key, encrypted)
