"""
SQLite persistence for the AuditChain.

Zero external dependencies — uses stdlib ``sqlite3`` only.

Usage::

    from merkle_audit import get_chain
    from merkle_audit.persister import SQLitePersister

    chain = get_chain()
    chain.set_persist_hook(SQLitePersister("audit.db"))

Or use the convenience helper::

    from merkle_audit.persister import install_sqlite_persister
    install_sqlite_persister("audit.db")  # wires the global chain
"""
from __future__ import annotations

import sqlite3
import threading
from typing import Optional

from .chain import ChainEntry, get_chain

_CREATE_TABLE = """
CREATE TABLE IF NOT EXISTS audit_log (
    leaf_index   INTEGER PRIMARY KEY,
    leaf_hash    TEXT    NOT NULL,
    mmr_root     TEXT    NOT NULL,
    payload_hash TEXT    NOT NULL,
    action_type  TEXT    NOT NULL,
    actor        TEXT    NOT NULL DEFAULT '',
    agent_id     TEXT    NOT NULL DEFAULT '',
    created_at   REAL    NOT NULL
)
"""


class SQLitePersister:
    """
    Callable persister that writes :class:`~merkle_audit.chain.ChainEntry`
    objects to a SQLite database.

    Thread-safe. Safe to share across threads.

    Args:
        db_path: Path to the SQLite file. Created if it doesn't exist.
                 Use ``":memory:"`` for in-process ephemeral storage (tests, etc.).
    """

    def __init__(self, db_path: str) -> None:
        self._db_path = db_path
        self._lock = threading.Lock()
        self._conn: Optional[sqlite3.Connection] = None
        self._init_db()

    def _init_db(self) -> None:
        conn = sqlite3.connect(self._db_path, check_same_thread=False)
        conn.execute(_CREATE_TABLE)
        conn.commit()
        self._conn = conn

    def __call__(self, entry: ChainEntry) -> None:
        """Persist a single ChainEntry. Called by AuditChain after each append."""
        with self._lock:
            assert self._conn is not None
            self._conn.execute(
                """
                INSERT OR IGNORE INTO audit_log
                    (leaf_index, leaf_hash, mmr_root, payload_hash,
                     action_type, actor, agent_id, created_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    entry.leaf_index,
                    entry.leaf_hash,
                    entry.mmr_root,
                    entry.payload_hash,
                    entry.action_type,
                    entry.actor,
                    entry.agent_id,
                    entry.created_at,
                ),
            )
            self._conn.commit()

    def close(self) -> None:
        """Close the underlying database connection."""
        with self._lock:
            if self._conn is not None:
                self._conn.close()
                self._conn = None


def install_sqlite_persister(db_path: str) -> SQLitePersister:
    """
    Wire the global AuditChain to persist all entries to a SQLite file.

    Call once at application startup::

        from merkle_audit.persister import install_sqlite_persister
        install_sqlite_persister("/var/lib/myapp/audit.db")

    Args:
        db_path: Path to SQLite file.

    Returns:
        The :class:`SQLitePersister` instance (in case you need to call ``.close()``).
    """
    persister = SQLitePersister(db_path)
    get_chain().set_persist_hook(persister)
    return persister
