"""SqliteDiagnosisSignalStore：baseline 逐题诊断信号的 SQLite 持久化适配器。

实现 core/evolution/protocols.py::DiagnosisSignalStore 端口。信号行以
(question_id, baseline_run_id, diag_fingerprint) 为主键，INSERT OR REPLACE
保证逐题幂等 upsert；bool 字段以 0/1 存储，可空字段以 NULL 存储。表建在
harness.db，供离线诊断编排写入、视频级切分选择器读取。
"""

from __future__ import annotations

import sqlite3
from pathlib import Path
from typing import TYPE_CHECKING

from core.evolution.types import DiagnosisSignalRow

if TYPE_CHECKING:
    from types import TracebackType

# 表列顺序即 DiagnosisSignalRow 字段顺序（question_id..session_id），
# upsert 写入与 load 还原共用，避免手写列名两处漂移。
_COLUMNS: tuple[str, ...] = (
    "question_id",
    "video_id",
    "baseline_run_id",
    "diag_fingerprint",
    "task_type",
    "error_type",
    "cause_category",
    "tier",
    "evolution_target",
    "degraded",
    "infra",
    "session_id",
)


class SqliteDiagnosisSignalStore:
    """逐题诊断信号的 SQLite 存储实现。

    构造时按需建表（CREATE TABLE IF NOT EXISTS），主键
    (question_id, baseline_run_id, diag_fingerprint) 保证同键覆盖。
    每次写操作单事务 commit，保证原子落盘与断点续跑。

    参数:
        db_path: SQLite 数据库文件路径（通常为 harness.db）。
    """

    def __init__(self, db_path: str) -> None:
        Path(db_path).parent.mkdir(parents=True, exist_ok=True)
        self._conn = sqlite3.connect(db_path)
        self._conn.row_factory = sqlite3.Row
        self._init_table()

    def _init_table(self) -> None:
        """创建 baseline_diagnosis 表（若不存在）。"""
        self._conn.execute(
            """
            CREATE TABLE IF NOT EXISTS baseline_diagnosis (
                question_id TEXT NOT NULL,
                video_id TEXT NOT NULL,
                baseline_run_id TEXT NOT NULL,
                diag_fingerprint TEXT NOT NULL,
                task_type TEXT NOT NULL,
                error_type TEXT,
                cause_category TEXT,
                tier TEXT NOT NULL,
                evolution_target TEXT,
                degraded INTEGER NOT NULL,
                infra INTEGER NOT NULL,
                session_id TEXT,
                PRIMARY KEY (question_id, baseline_run_id, diag_fingerprint)
            )
            """
        )
        self._conn.commit()

    def upsert(self, row: DiagnosisSignalRow) -> None:
        """写入或覆盖单题诊断信号（按主键幂等，单事务）。

        参数:
            row: 待持久化的诊断信号行。bool 字段转 0/1，None 存 NULL。

        关键实现:
            用 INSERT OR REPLACE 按主键覆盖，避免重复行；commit 保证原子。
        """
        placeholders = ", ".join("?" for _ in _COLUMNS)
        col_names = ", ".join(_COLUMNS)
        values = (
            row.question_id,
            row.video_id,
            row.baseline_run_id,
            row.diag_fingerprint,
            row.task_type,
            row.error_type,
            row.cause_category,
            row.tier,
            row.evolution_target,
            int(row.degraded),
            int(row.infra),
            row.session_id,
        )
        self._conn.execute(
            f"INSERT OR REPLACE INTO baseline_diagnosis ({col_names}) VALUES ({placeholders})",
            values,
        )
        self._conn.commit()

    def done_question_ids(self, baseline_run_id: str, diag_fingerprint: str) -> set[str]:
        """查询指定 run 与诊断指纹下已完成的 question_id 集合。

        参数:
            baseline_run_id: baseline run 标识。
            diag_fingerprint: 诊断口径指纹。

        返回:
            已落盘信号的 question_id 集合；无匹配时为空集，供断点续跑跳过。
        """
        cursor = self._conn.execute(
            "SELECT DISTINCT question_id FROM baseline_diagnosis"
            " WHERE baseline_run_id = ? AND diag_fingerprint = ?",
            (baseline_run_id, diag_fingerprint),
        )
        return {r["question_id"] for r in cursor.fetchall()}

    def load(self, baseline_run_id: str, diag_fingerprint: str) -> list[DiagnosisSignalRow]:
        """加载指定 run 与诊断指纹下的全部诊断信号行。

        参数:
            baseline_run_id: baseline run 标识。
            diag_fingerprint: 诊断口径指纹。

        返回:
            还原后的 DiagnosisSignalRow 列表（0/1→bool，NULL→None）。
        """
        col_names = ", ".join(_COLUMNS)
        cursor = self._conn.execute(
            f"SELECT {col_names} FROM baseline_diagnosis"
            " WHERE baseline_run_id = ? AND diag_fingerprint = ?",
            (baseline_run_id, diag_fingerprint),
        )
        return [
            DiagnosisSignalRow(
                question_id=r["question_id"],
                video_id=r["video_id"],
                baseline_run_id=r["baseline_run_id"],
                diag_fingerprint=r["diag_fingerprint"],
                task_type=r["task_type"],
                error_type=r["error_type"],
                cause_category=r["cause_category"],
                tier=r["tier"],
                evolution_target=r["evolution_target"],
                degraded=bool(r["degraded"]),
                infra=bool(r["infra"]),
                session_id=r["session_id"],
            )
            for r in cursor.fetchall()
        ]

    def close(self) -> None:
        """关闭底层 SQLite 连接，释放文件描述符与锁。"""
        self._conn.close()

    def __enter__(self) -> SqliteDiagnosisSignalStore:
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        self.close()
