# DatabaseManager Module Overview

`DatabaseManager` is the low-level persistence layer for Bibliotheca. It wraps a
SQLite connection, owns prepared statements, and provides a narrow set of CRUD
methods that the higher-level `BookList` uses to keep the database in sync.

## Responsibilities

- Open / close the SQLite database (`sqlite3_open` / `sqlite3_close`).
- Create the schema on first run (`ensure_schema`).
- Expose prepared insert/update/delete/select helpers for `Book` records.
- Provide a busy handler so concurrent access backs off politely.
- Guard SQLite calls with a mutex so callers can safely use the manager across
  threads.

## Structure

```
DatabaseManager
 ├── std::string db_path_   // path to the .sqlite file
 ├── sqlite3* db_           // raw SQLite handle (owned)
 └── std::mutex mtx_        // serialises public API calls
```

All statements are prepared lazily by `prepare_statements()` and reused for each
operation.

## Schema

`ensure_schema()` creates a single table named `books`:

```sql
CREATE TABLE IF NOT EXISTS books (
  id TEXT PRIMARY KEY,
  title TEXT,
  author TEXT,
  file_path TEXT,
  cover_path TEXT
);
```

Additional indices or columns should be added here, keeping in mind that the
manager is the canonical place to evolve storage.

## Public API

| Method                | Description                                   |
|-----------------------|-----------------------------------------------|
| `ensure_schema()`     | Creates tables if they do not exist.          |
| `upsert_book(book)`   | INSERT OR REPLACE using the book’s id.        |
| `remove_book(id)`     | Deletes the row; returns false if not present.|
| `get_book(id)`        | Loads a single book (optional result).        |
| `load_all_books()`    | Returns a vector of all books.                |

Internally each method locks `mtx_`, executes the prepared statement, and
translates SQLite rows into `Book` objects.

## Busy handling & thread safety

SQLite is file-based and can momentarily return `SQLITE_BUSY` if another thread
or process holds a lock. The static `busy_handler` callback retries with an
exponential backoff. Because all public methods take the mutex, only one thread
issues SQL commands at a time, simplifying concurrency.

## Extension points

- **Schema migrations**: add ALTER TABLE logic inside `ensure_schema()` and keep
  the `Book` serialization/deserialization in sync.
- **Additional queries**: expose specialised getters (e.g., search by author) if
  higher layers start to need them.
- **Alternative backends**: swapping SQLite for another storage engine would
  mean re-implementing this module while keeping `BookList` untouched.

## Expected usage

`BookList` owns a single `DatabaseManager` instance, calls `ensure_schema()` on
startup, and uses the CRUD helpers whenever a book changes. Other modules should
not talk to SQLite directly; instead they should route all persistence through
this manager to keep logic centralised.
