"""目録の集計（読み取りのみ）."""

import sqlite3


def count_kinds(conn: sqlite3.Connection) -> dict[str, int]:
    """種類の定義の件数を分類（`asset`・`annotation`）ごとに数える．

    Args:
        conn: 目録への接続（マイグレーション適用済み）．

    Returns:
        分類 → 件数．
    """
    rows = conn.execute("SELECT category, COUNT(*) FROM kinds GROUP BY category ORDER BY category")
    return {category: count for category, count in rows}


def count_assets_by_kind(conn: sqlite3.Connection) -> dict[str, int]:
    """ファイルの数をファイルの種類ごとに数える（登録の無い種類は 0）．

    Args:
        conn: 目録への接続．

    Returns:
        種類名 → ファイル数（種類名の順）．
    """
    rows = conn.execute(
        "SELECT k.kind, COUNT(a.sha256) FROM kinds AS k"
        " LEFT JOIN assets AS a ON a.kind = k.kind"
        " WHERE k.category = 'asset' GROUP BY k.kind ORDER BY k.kind"
    )
    return {kind: count for kind, count in rows}


def count_annotation_values(conn: sqlite3.Connection) -> dict[str, dict[str, int]]:
    """有効な所見（訂正されていないもの）の数を，所見の種類・値（JSON の文字列）ごとに数える．

    Args:
        conn: 目録への接続．

    Returns:
        所見の種類名 → 値の JSON → 件数．
    """
    rows = conn.execute(
        "SELECT kind, value, COUNT(*) FROM annotations WHERE superseded_by IS NULL"
        " GROUP BY kind, value ORDER BY kind, value"
    )
    result: dict[str, dict[str, int]] = {}
    for kind, value, count in rows:
        result.setdefault(kind, {})[value] = count
    return result


def count_superseded_annotations(conn: sqlite3.Connection) -> int:
    """訂正された（`superseded_by` のある）所見の数．"""
    (count,) = conn.execute(
        "SELECT COUNT(*) FROM annotations WHERE superseded_by IS NOT NULL"
    ).fetchone()
    return count


def count_subjects(conn: sqlite3.Connection) -> int:
    """症例の数．"""
    (count,) = conn.execute("SELECT COUNT(*) FROM subjects").fetchone()
    return count


def count_captures(conn: sqlite3.Connection) -> int:
    """撮影の数．"""
    (count,) = conn.execute("SELECT COUNT(*) FROM captures").fetchone()
    return count


def count_subjects_with_multiple_captures(conn: sqlite3.Connection) -> int:
    """撮影を 2 つ以上持つ症例の数（同じ患者の複数枚がまとまっているかの目安）．"""
    (count,) = conn.execute(
        "SELECT COUNT(*) FROM"
        " (SELECT subject_id FROM captures GROUP BY subject_id HAVING COUNT(*) >= 2)"
    ).fetchone()
    return count


def list_asset_files(conn: sqlite3.Connection) -> list[tuple[str, str]]:
    """目録の全ファイルの（SHA-256, 拡張子）を SHA-256 の順に返す（点検用）．"""
    rows = conn.execute("SELECT sha256, ext FROM assets ORDER BY sha256")
    return [(sha256, ext) for sha256, ext in rows]


def count_dangling_annotations(conn: sqlite3.Connection) -> int:
    """参照先（撮影・ファイル・訂正後の所見）が目録に無い所見の数．

    外部キー制約で通常は 0 になる．制約を無効にした接続での書き込み等で壊れていないかを見る．
    """
    (count,) = conn.execute(
        "SELECT COUNT(*) FROM annotations AS a"
        " WHERE (a.capture_id IS NOT NULL"
        "        AND NOT EXISTS (SELECT 1 FROM captures AS c WHERE c.capture_id = a.capture_id))"
        "    OR (a.sha256 IS NOT NULL"
        "        AND NOT EXISTS (SELECT 1 FROM assets AS s WHERE s.sha256 = a.sha256))"
        "    OR (a.superseded_by IS NOT NULL"
        "        AND NOT EXISTS"
        "            (SELECT 1 FROM annotations AS n WHERE n.annotation_id = a.superseded_by))"
    ).fetchone()
    return count


def count_broken_dataset_members(conn: sqlite3.Connection) -> int:
    """凍結した版の構成のうち，参照先（ファイル・所見・構成のファイル）が欠けている行の数．"""
    (members,) = conn.execute(
        "SELECT COUNT(*) FROM dataset_members AS m"
        " WHERE NOT EXISTS (SELECT 1 FROM assets AS a WHERE a.sha256 = m.sha256)"
        "    OR NOT EXISTS (SELECT 1 FROM dataset_versions AS v"
        "                   WHERE v.name = m.name AND v.version = m.version)"
    ).fetchone()
    (member_annotations,) = conn.execute(
        "SELECT COUNT(*) FROM dataset_member_annotations AS d"
        " WHERE NOT EXISTS (SELECT 1 FROM annotations AS a WHERE a.annotation_id = d.annotation_id)"
        "    OR NOT EXISTS (SELECT 1 FROM dataset_members AS m"
        "                   WHERE m.name = d.name AND m.version = d.version"
        "                     AND m.sha256 = d.sha256)"
    ).fetchone()
    return members + member_annotations
