"""step 重跑幂等：gate 派生行必须随 step 清理，否则崩溃重跑累积重复。"""

from __future__ import annotations

import sqlite3
from typing import TYPE_CHECKING

from app.harness.runner import _clear_step_rows

if TYPE_CHECKING:
    from pathlib import Path


def _mk_db(tmp_path: Path) -> Path:
    """构造含 rollout 行、gate 派生行、他 step 行与前缀陷阱行的最小 harness.db。

    参数:
        tmp_path: pytest 临时目录。

    返回:
        harness.db 路径。
    """
    db = tmp_path / "harness.db"
    conn = sqlite3.connect(db)
    conn.execute("CREATE TABLE predictions (run_id TEXT, question_id TEXT)")
    conn.execute("CREATE TABLE traces (run_id TEXT, question_id TEXT)")
    conn.execute("CREATE TABLE gate_evidence (run_id TEXT, epoch INTEGER, step INTEGER)")
    conn.execute("CREATE TABLE quadrant_pair (run_id TEXT, epoch INTEGER, step INTEGER)")
    rows = [
        ("infer_adhoc_e1_s0", "q1"),  # rollout 行
        ("infer_adhoc_e1_s0_gate_action-reasoning_base", "q2"),  # gate base 臂
        ("infer_adhoc_e1_s0_gate_action-reasoning_cand", "q3"),  # gate cand 臂
        ("infer_adhoc_e1_s1", "q4"),  # 其他 step，不许误删
        ("infer_adhoc_e1_s10_gate_x_base", "q5"),  # s10 前缀陷阱，不许误删
    ]
    conn.executemany("INSERT INTO predictions VALUES (?, ?)", rows)
    conn.executemany("INSERT INTO traces VALUES (?, ?)", rows)
    conn.execute("INSERT INTO gate_evidence VALUES ('infer_adhoc', 1, 0)")
    conn.execute("INSERT INTO gate_evidence VALUES ('infer_adhoc', 1, 1)")
    conn.execute("INSERT INTO quadrant_pair VALUES ('infer_adhoc', 1, 0)")
    conn.commit()
    conn.close()
    return db


def test_clear_step_rows_removes_rollout_and_gate_rows(tmp_path) -> None:
    """rollout 行 + 本 step 全部 gate 派生行被清；他 step 与 s10 前缀陷阱不动。"""
    db = _mk_db(tmp_path)
    _clear_step_rows(str(db), baseline_run_id="infer_adhoc", epoch=1, step=0)
    conn = sqlite3.connect(db)
    left = {r[0] for r in conn.execute("SELECT run_id FROM predictions")}
    assert left == {"infer_adhoc_e1_s1", "infer_adhoc_e1_s10_gate_x_base"}
    left_t = {r[0] for r in conn.execute("SELECT run_id FROM traces")}
    assert left_t == left
    ge = list(conn.execute("SELECT step FROM gate_evidence"))
    assert ge == [(1,)]  # 只剩 step=1 的行
    assert list(conn.execute("SELECT COUNT(*) FROM quadrant_pair"))[0][0] == 0
    conn.close()


def test_clear_step_rows_missing_tables_is_noop(tmp_path) -> None:
    """gate_evidence/quadrant_pair 表尚未建（首个 step）时不报错。"""
    db = tmp_path / "harness.db"
    conn = sqlite3.connect(db)
    conn.execute("CREATE TABLE predictions (run_id TEXT)")
    conn.execute("CREATE TABLE traces (run_id TEXT)")
    conn.commit()
    conn.close()
    _clear_step_rows(str(db), baseline_run_id="infer_adhoc", epoch=1, step=0)
