"""
Tests for the SQLite persister.
"""
from __future__ import annotations

import sqlite3

import pytest

from merkle_audit.chain import AuditChain
from merkle_audit.persister import SQLitePersister


@pytest.fixture
def db(tmp_path):
    return str(tmp_path / "audit.db")


class TestSQLitePersister:
    def test_creates_table(self, db):
        SQLitePersister(db)
        conn = sqlite3.connect(db)
        tables = {r[0] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'")}
        assert "audit_log" in tables

    def test_persists_entry(self, db):
        chain = AuditChain()
        chain.set_persist_hook(SQLitePersister(db))
        chain.append("tool_executed", {"tool": "search"}, actor="agent-1")

        conn = sqlite3.connect(db)
        rows = conn.execute("SELECT action_type, actor FROM audit_log").fetchall()
        assert len(rows) == 1
        assert rows[0] == ("tool_executed", "agent-1")

    def test_persists_all_fields(self, db):
        chain = AuditChain()
        persister = SQLitePersister(db)
        chain.set_persist_hook(persister)
        entry = chain.append("payment", {"amount": 42}, actor="banker")

        conn = sqlite3.connect(db)
        row = conn.execute(
            "SELECT leaf_index, leaf_hash, mmr_root, payload_hash, action_type, actor "
            "FROM audit_log WHERE leaf_index = 0"
        ).fetchone()
        assert row is not None
        assert row[0] == 0
        assert row[1] == entry.leaf_hash
        assert row[2] == entry.mmr_root
        assert row[3] == entry.payload_hash
        assert row[4] == "payment"
        assert row[5] == "banker"

    def test_multiple_entries(self, db):
        chain = AuditChain()
        chain.set_persist_hook(SQLitePersister(db))
        for i in range(10):
            chain.append(f"action_{i}", {"i": i})

        conn = sqlite3.connect(db)
        count = conn.execute("SELECT COUNT(*) FROM audit_log").fetchone()[0]
        assert count == 10

    def test_leaf_indices_are_sequential(self, db):
        chain = AuditChain()
        chain.set_persist_hook(SQLitePersister(db))
        for _ in range(5):
            chain.append("event", {})

        conn = sqlite3.connect(db)
        indices = [
            r[0]
            for r in conn.execute(
                "SELECT leaf_index FROM audit_log ORDER BY leaf_index"
            ).fetchall()
        ]
        assert indices == list(range(5))

    def test_idempotent_insert(self, db):
        """Duplicate inserts (same leaf_index) are silently ignored."""
        persister = SQLitePersister(db)
        chain = AuditChain()
        chain.set_persist_hook(persister)
        entry = chain.append("action", {})
        persister(entry)  # call again manually — should not raise or duplicate

        conn = sqlite3.connect(db)
        count = conn.execute("SELECT COUNT(*) FROM audit_log").fetchone()[0]
        assert count == 1

    def test_in_memory_db(self):
        chain = AuditChain()
        persister = SQLitePersister(":memory:")
        chain.set_persist_hook(persister)
        chain.append("event", {"k": "v"})
        conn = persister._conn
        count = conn.execute("SELECT COUNT(*) FROM audit_log").fetchone()[0]
        assert count == 1

    def test_close(self, db):
        persister = SQLitePersister(db)
        persister.close()
        assert persister._conn is None


class TestInstallHelper:
    def test_install_wires_global_chain(self, db, monkeypatch):
        from merkle_audit import chain as chain_mod
        fresh_chain = AuditChain()
        monkeypatch.setattr(chain_mod, "_global_chain", fresh_chain)

        from merkle_audit.persister import install_sqlite_persister
        install_sqlite_persister(db)

        chain_mod.log_event("wired_event", {"ok": True})

        conn = sqlite3.connect(db)
        row = conn.execute("SELECT action_type FROM audit_log").fetchone()
        assert row is not None
        assert row[0] == "wired_event"
