"""Tests for the AuditLog module (hash-chained tamper-evident logging)."""

import json

from skytale_sdk.audit import AuditEvent, AuditEntry, AuditLog, EventType


class TestAuditEvent:
    """AuditEvent factory methods produce correct event dicts."""

    def test_channel_created(self):
        e = AuditEvent.channel_created("acme/ns/svc", agent_did="did:key:z6Mk1")
        assert e["event_type"] == EventType.CHANNEL_CREATED
        assert e["channel"] == "acme/ns/svc"
        assert e["agent_did"] == "did:key:z6Mk1"

    def test_message_sent(self):
        e = AuditEvent.message_sent("acme/ns/svc", ciphertext_len=256)
        assert e["event_type"] == EventType.MESSAGE_SENT
        assert e["metadata"]["ciphertext_len"] == 256

    def test_message_received(self):
        e = AuditEvent.message_received("ch", ciphertext_len=128, count=3)
        assert e["metadata"]["count"] == 3

    def test_member_added(self):
        e = AuditEvent.member_added("ch", member_did="did:key:z6Mk2")
        assert e["agent_did"] == "did:key:z6Mk2"

    def test_identity_verified(self):
        e = AuditEvent.identity_verified("did:key:z6Mk1", method="did:web")
        assert e["metadata"]["method"] == "did:web"

    def test_invite_created(self):
        e = AuditEvent.invite_created("ch", max_uses=5, ttl=7200)
        assert e["metadata"]["max_uses"] == 5
        assert e["metadata"]["ttl"] == 7200


class TestAuditLog:
    """AuditLog hash-chain integrity."""

    def test_empty_log_verifies(self):
        log = AuditLog()
        assert log.verify_chain()
        assert len(log) == 0

    def test_single_entry_verifies(self):
        log = AuditLog(agent_did="did:key:z6Mk1")
        log.record(AuditEvent.channel_created("acme/ns/svc"))
        assert log.verify_chain()
        assert len(log) == 1

    def test_first_entry_has_empty_prev_hash(self):
        log = AuditLog()
        entry = log.record(AuditEvent.channel_created("ch"))
        assert entry.prev_hash == ""
        assert entry.entry_hash != ""

    def test_chain_links_entries(self):
        log = AuditLog()
        e1 = log.record(AuditEvent.channel_created("ch"))
        e2 = log.record(AuditEvent.message_sent("ch", ciphertext_len=100))
        assert e2.prev_hash == e1.entry_hash

    def test_multi_entry_chain_verifies(self):
        log = AuditLog(agent_did="did:key:z6Mk1")
        log.record(AuditEvent.channel_created("ch"))
        log.record(AuditEvent.message_sent("ch", ciphertext_len=100))
        log.record(AuditEvent.message_received("ch", ciphertext_len=100))
        log.record(AuditEvent.member_added("ch", member_did="did:key:z6Mk2"))
        assert log.verify_chain()
        assert len(log) == 4

    def test_tampered_entry_fails_verification(self):
        log = AuditLog()
        log.record(AuditEvent.channel_created("ch"))
        log.record(AuditEvent.message_sent("ch", ciphertext_len=100))

        # Tamper with the first entry
        log._entries[0].channel = "tampered/channel"
        assert not log.verify_chain()

    def test_tampered_hash_fails_verification(self):
        log = AuditLog()
        log.record(AuditEvent.channel_created("ch"))
        log.record(AuditEvent.message_sent("ch", ciphertext_len=100))

        # Tamper with the chain link
        log._entries[1].prev_hash = "0" * 64
        assert not log.verify_chain()

    def test_sequence_numbers_are_monotonic(self):
        log = AuditLog()
        for i in range(5):
            entry = log.record(AuditEvent.channel_created(f"ch/{i}"))
            assert entry.sequence == i


class TestAuditLogExport:
    """Export and serialization."""

    def test_export_chain_returns_dicts(self):
        log = AuditLog()
        log.record(AuditEvent.channel_created("ch"))
        log.record(AuditEvent.message_sent("ch", ciphertext_len=64))

        chain = log.export_chain()
        assert len(chain) == 2
        assert chain[0]["event_type"] == "channel_created"
        assert chain[1]["event_type"] == "message_sent"
        assert "entry_hash" in chain[0]
        assert "prev_hash" in chain[1]

    def test_export_json_is_valid(self):
        log = AuditLog()
        log.record(AuditEvent.channel_created("ch"))
        raw = log.export_json()
        parsed = json.loads(raw)
        assert isinstance(parsed, list)
        assert len(parsed) == 1

    def test_entries_property_returns_copy(self):
        log = AuditLog()
        log.record(AuditEvent.channel_created("ch"))
        entries = log.entries
        assert len(entries) == 1
        entries.clear()  # Modifying the copy
        assert len(log) == 1  # Original unchanged

    def test_agent_did_default_applied(self):
        log = AuditLog(agent_did="did:key:z6MkDefault")
        entry = log.record(AuditEvent.channel_created("ch"))
        assert entry.agent_did == "did:key:z6MkDefault"

    def test_event_agent_did_overrides_default(self):
        log = AuditLog(agent_did="did:key:z6MkDefault")
        entry = log.record(AuditEvent.channel_created("ch", agent_did="did:key:z6MkOverride"))
        assert entry.agent_did == "did:key:z6MkOverride"
