"""Regression coverage for the lifespan reopen path on ``Database``.

The production server only runs a single FastAPI lifespan per process, so
``Database.close()`` releasing the connection is fine. Pytest is different:
``ledgrab.main`` is imported once and its module-level ``db`` singleton
survives across every TestClient session, each of which closes the
connection on shutdown.

Before :meth:`Database.ensure_open` existed, the second TestClient session
hit ``sqlite3.ProgrammingError: Cannot operate on a closed database`` at
fixture-setup time when the lifespan startup tried to read settings (the
``AutoBackupEngine`` constructor was the first reader). These tests pin
the close+reopen cycle so the cascade can't silently come back.
"""

import sqlite3

import pytest

from ledgrab.storage.database import Database


def test_close_then_ensure_open_reopens_connection(tmp_path):
    db = Database(tmp_path / "reopen.db")
    db.set_setting("k", {"v": 1})
    db.close()

    db.ensure_open()

    assert db.get_setting("k") == {"v": 1}
    db.close()


def test_ensure_open_is_idempotent_when_already_open(tmp_path):
    db = Database(tmp_path / "idempotent.db")
    original_conn = db._conn

    db.ensure_open()

    # Same live connection — no spurious reconnect when already open.
    assert db._conn is original_conn
    db.close()


def test_close_is_idempotent(tmp_path):
    db = Database(tmp_path / "double_close.db")
    db.close()

    # Second close must not raise (lifespan shutdown can run twice in
    # quick test sessions). Connection stays released.
    db.close()
    assert db._conn is None


def test_operation_after_close_without_reopen_raises(tmp_path):
    db = Database(tmp_path / "no_reopen.db")
    db.close()

    # Without ensure_open, attempting to use the DB fails loudly rather
    # than silently re-connecting — callers must opt in.
    with pytest.raises((sqlite3.ProgrammingError, AttributeError)):
        db.get_setting("anything")
