"""Edge-case tests for CertiSigma crypto module and client_encrypted flag."""

import pytest
import httpx
import respx

from certisigma import CertiSigmaClient
from certisigma.crypto import (
    generate_key,
    encrypt_metadata,
    decrypt_metadata,
    is_encrypted,
    rotate_key,
)

BASE = "https://test.certisigma.ch/api"
KEY = "cs_live_test_key_000"
VALID_HASH = "a" * 64


class TestClientEncryptedFlag:
    """Verify client_encrypted is properly sent to the API."""

    @respx.mock
    def test_attest_sends_client_encrypted(self):
        route = respx.post(f"{BASE}/attest").mock(return_value=httpx.Response(200, json={
            "id": "att_1", "hash_hex": VALID_HASH, "timestamp": "2026-01-01T00:00:00Z",
            "status": "created", "client_encrypted": True,
        }))
        with CertiSigmaClient(api_key=KEY, base_url=BASE) as c:
            r = c.attest(VALID_HASH, extra_data={"ct": "blob"}, client_encrypted=True)
        assert r.client_encrypted is True
        body = route.calls[0].request.content
        import json
        payload = json.loads(body)
        assert payload["client_encrypted"] is True

    @respx.mock
    def test_attest_omits_client_encrypted_when_false(self):
        route = respx.post(f"{BASE}/attest").mock(return_value=httpx.Response(200, json={
            "id": "att_1", "hash_hex": VALID_HASH, "timestamp": "2026-01-01T00:00:00Z",
            "status": "created",
        }))
        with CertiSigmaClient(api_key=KEY, base_url=BASE) as c:
            c.attest(VALID_HASH)
        import json
        payload = json.loads(route.calls[0].request.content)
        assert "client_encrypted" not in payload

    @respx.mock
    def test_batch_attest_sends_client_encrypted(self):
        route = respx.post(f"{BASE}/attest/batch").mock(return_value=httpx.Response(200, json={
            "batch_id": "b1", "count": 1, "created": 1, "existing": 0, "attestations": [],
        }))
        with CertiSigmaClient(api_key=KEY, base_url=BASE) as c:
            c.batch_attest([VALID_HASH], extra_data={"ct": "x"}, client_encrypted=True)
        import json
        payload = json.loads(route.calls[0].request.content)
        assert payload["client_encrypted"] is True
        assert payload["extra_data"] == {"ct": "x"}

    @respx.mock
    def test_update_metadata_sends_client_encrypted(self):
        route = respx.patch(f"{BASE}/attestation/42/metadata").mock(return_value=httpx.Response(200, json={
            "success": True, "claim_id": 10, "attestation_id": "att_42",
            "client_encrypted": True, "deleted": False, "audit_entries": 1,
        }))
        with CertiSigmaClient(api_key=KEY, base_url=BASE) as c:
            r = c.update_metadata("att_42", extra_data={"ct": "x"}, client_encrypted=True)
        assert r.client_encrypted is True
        import json
        payload = json.loads(route.calls[0].request.content)
        assert payload["client_encrypted"] is True

    @respx.mock
    def test_attest_file_passes_client_encrypted(self, tmp_path):
        route = respx.post(f"{BASE}/attest").mock(return_value=httpx.Response(200, json={
            "id": "att_f", "hash_hex": VALID_HASH, "timestamp": "2026-01-01T00:00:00Z",
            "status": "created", "client_encrypted": True,
        }))
        f = tmp_path / "test.bin"
        f.write_bytes(b"test data")
        with CertiSigmaClient(api_key=KEY, base_url=BASE) as c:
            r = c.attest_file(str(f), client_encrypted=True)
        assert r.client_encrypted is True
        import json
        payload = json.loads(route.calls[0].request.content)
        assert payload["client_encrypted"] is True


class TestCryptoEdgeCases:
    """Edge cases for the crypto module."""

    def test_empty_dict(self):
        key = generate_key()
        envelope = encrypt_metadata({}, key)
        assert decrypt_metadata(envelope, key) == {}

    def test_unicode_values(self):
        key = generate_key()
        data = {"emoji": "\U0001f512", "chinese": "\u4e16\u754c", "arabic": "\u0645\u0631\u062d\u0628\u0627"}
        envelope = encrypt_metadata(data, key)
        assert decrypt_metadata(envelope, key) == data

    def test_nested_structure(self):
        key = generate_key()
        data = {"a": {"b": {"c": [1, 2, {"d": True}]}}, "null_val": None}
        envelope = encrypt_metadata(data, key)
        assert decrypt_metadata(envelope, key) == data

    def test_large_payload(self):
        key = generate_key()
        data = {"items": [{"id": i, "value": f"data_{i}" * 10} for i in range(100)]}
        envelope = encrypt_metadata(data, key)
        assert decrypt_metadata(envelope, key) == data

    def test_deterministic_nonce_uniqueness(self):
        key = generate_key()
        data = {"same": "data"}
        e1 = encrypt_metadata(data, key)
        e2 = encrypt_metadata(data, key)
        assert e1["nonce"] != e2["nonce"]
        assert e1["ciphertext"] != e2["ciphertext"]

    def test_invalid_hex_key_chars(self):
        with pytest.raises(ValueError):
            encrypt_metadata({"a": 1}, "zz" * 32)

    def test_invalid_hex_key_odd_length(self):
        with pytest.raises(ValueError):
            encrypt_metadata({"a": 1}, "abc")

    def test_tampered_ciphertext_fails(self):
        key = generate_key()
        envelope = encrypt_metadata({"secret": "data"}, key)
        tampered = dict(envelope)
        tampered["ciphertext"] = "AAAA" + tampered["ciphertext"][4:]
        with pytest.raises(Exception):
            decrypt_metadata(tampered, key)

    def test_tampered_nonce_fails(self):
        key = generate_key()
        envelope = encrypt_metadata({"secret": "data"}, key)
        tampered = dict(envelope)
        tampered["nonce"] = "00" * 12
        with pytest.raises(Exception):
            decrypt_metadata(tampered, key)

    def test_is_encrypted_with_extra_fields(self):
        key = generate_key()
        envelope = encrypt_metadata({"a": 1}, key)
        envelope["extra_field"] = "ignored"
        assert is_encrypted(envelope) is True

    def test_rotate_key_preserves_data(self):
        k1 = generate_key()
        k2 = generate_key()
        k3 = generate_key()
        data = {"multi": "rotate", "count": 42}
        e1 = encrypt_metadata(data, k1)
        e2 = rotate_key(e1, k1, k2)
        e3 = rotate_key(e2, k2, k3)
        assert decrypt_metadata(e3, k3) == data

    def test_envelope_version_check(self):
        key = generate_key()
        envelope = encrypt_metadata({"a": 1}, key)
        assert envelope["v"] == 1
