"""SQLite の DB の複製（バックアップ API）と，複製した DB の点検・件数.

目録（`catalog.sqlite`）と対応表（`identity.sqlite`）の複製に使う（`tiasdata backup`）．
対応表を開くのは取り込みの処理だけという規則（coding-style.md）の例外として，複製と点検の
ためにだけ開く．中身（患者 ID）は読まず，行の数だけを返す．
"""

import sqlite3
from contextlib import closing
from pathlib import Path


def _connect_read_only(path: Path) -> sqlite3.Connection:
    # 複製元・点検する複製は書き換えない（無ければ作らない）
    uri = f"{path.resolve().as_uri()}?mode=ro"
    try:
        return sqlite3.connect(uri, uri=True)
    except sqlite3.OperationalError as e:
        raise ValueError(f"DB を開けない: {e}") from e


def copy_database(source: Path, target: Path) -> None:
    """SQLite のバックアップ API（`Connection.backup`）で DB を複製する．

    ファイルのコピーと違い，書き込みの途中の状態を写さない（複製元の 1 つの時点の内容になる）．

    Args:
        source: 複製元の DB．
        target: 複製先のファイル（空のファイルか，無いこと）．

    Raises:
        ValueError: 複製元を開けない．
        sqlite3.Error: 複製に失敗した．
    """
    with closing(_connect_read_only(source)) as src, closing(sqlite3.connect(target)) as dst:
        src.backup(dst)


def integrity_problems(path: Path) -> int:
    """DB に `PRAGMA integrity_check` をかけ，問題の数を返す（0 なら ok）．

    問題の内容（表・ページの位置）は返さない．

    Args:
        path: 点検する DB．

    Returns:
        問題の数．
    """
    with closing(_connect_read_only(path)) as conn:
        rows = conn.execute("PRAGMA integrity_check").fetchall()
    if rows == [("ok",)]:
        return 0
    return len(rows)


def user_version(path: Path) -> int:
    """DB のスキーマの版（`PRAGMA user_version`）を返す．"""
    with closing(_connect_read_only(path)) as conn:
        return conn.execute("PRAGMA user_version").fetchone()[0]


# 目録の件数として表示する表（表示名 → SQL）．値の埋め込みを避けるため SQL ごと固定する
_CATALOG_COUNTS: dict[str, str] = {
    "subjects": "SELECT COUNT(*) FROM subjects",
    "captures": "SELECT COUNT(*) FROM captures",
    "assets": "SELECT COUNT(*) FROM assets",
    "annotations": "SELECT COUNT(*) FROM annotations",
    "dataset_versions": "SELECT COUNT(*) FROM dataset_versions",
}


def count_catalog_rows(path: Path) -> dict[str, int]:
    """目録の DB の主な表の行の数を返す（症例・撮影・ファイル・所見・版）．

    Args:
        path: 目録の DB（複製したもの）．

    Returns:
        表の名前 → 行の数．
    """
    with closing(_connect_read_only(path)) as conn:
        return {name: conn.execute(sql).fetchone()[0] for name, sql in _CATALOG_COUNTS.items()}


def count_identity_rows(path: Path) -> int:
    """対応表の DB の行の数を返す（中身は読まない）．

    Args:
        path: 対応表の DB（複製したもの）．

    Returns:
        `identity_map` の行の数．
    """
    with closing(_connect_read_only(path)) as conn:
        return conn.execute("SELECT COUNT(*) FROM identity_map").fetchone()[0]
