"""Durability tests for the persistent Merkle transparency log.

The audit's second trust-hole: the gate's Merkle log lived only in process
memory, so it evaporated on exit and the RFC 6962 consistency-proof machinery
was dead code (nothing ever compared two persisted roots). These tests pin the
fix: the log survives a simulated restart, and a consistency proof against the
*persisted* prior root proves append-only growth across that restart.
"""
import base64


from abom.merkle import (
    PersistentMerkleLog,
    MerkleLog,
    verify_consistency_hex,
    verify_inclusion_hex,
)


def test_log_survives_restart(tmp_path):
    path = tmp_path / "log.mlog"

    log = PersistentMerkleLog(path)
    log.append(b"decision-0")
    log.append(b"decision-1")
    root_before = log.head().root_hash
    size_before = log.size

    # Simulate a fresh process: brand-new object, same file.
    reloaded = PersistentMerkleLog(path)
    assert reloaded.size == size_before
    assert reloaded.head().root_hash == root_before
    # entries are intact and inclusion still proves out
    proof = reloaded.prove_inclusion(0)
    assert verify_inclusion_hex(b"decision-0", proof)


def test_append_after_reload_extends_same_chain(tmp_path):
    path = tmp_path / "log.mlog"
    log = PersistentMerkleLog(path)
    log.append(b"a")
    log.append(b"b")
    old_size = log.size
    old_root = log.head().root_hash

    # New process appends more.
    reloaded = PersistentMerkleLog(path)
    reloaded.append(b"c")
    assert reloaded.size == 3

    # Consistency proof against the PERSISTED old root: append-only growth,
    # nothing already-published was edited or dropped across the restart.
    cproof = reloaded.prove_consistency(old_size)
    assert verify_consistency_hex(old_root, cproof)


def test_tamper_with_persisted_file_is_detectable(tmp_path):
    """Editing a persisted leaf changes the reloaded root — the whole point."""
    path = tmp_path / "log.mlog"
    log = PersistentMerkleLog(path)
    log.append(b"honest-0")
    log.append(b"honest-1")
    honest_root = log.head().root_hash

    # Rewrite the first line to a forged entry.
    lines = path.read_text().splitlines()
    lines[0] = base64.b64encode(b"forged-0").decode()
    path.write_text("\n".join(lines) + "\n")

    reloaded = PersistentMerkleLog(path)
    assert reloaded.head().root_hash != honest_root


def test_persistent_matches_in_memory_for_same_entries(tmp_path):
    """Persistence must not change the hashing: same entries -> same root as the
    pure in-memory MerkleLog."""
    mem = MerkleLog()
    disk = PersistentMerkleLog(tmp_path / "log.mlog")
    for e in (b"x", b"y", b"z"):
        mem.append(e)
        disk.append(e)
    assert mem.head().root_hash == disk.head().root_hash


def test_empty_reload_is_empty(tmp_path):
    log = PersistentMerkleLog(tmp_path / "nope.mlog")
    assert log.size == 0
    # matches the RFC 6962 empty-tree root of the in-memory log
    assert log.head().root_hash == MerkleLog().head().root_hash
