"""WBS-7.6(2026-06-21) — 실거래 슬리피지 실측 캡처 스캐폴딩.

spec/55_execution_simulator_contract.yaml의 slippage_model(bps=5)은 이론치이며
"추후 실측 데이터로 보정 예정"이라는 메모만 있고 실제 캡처 경로가 없었다. 이 모듈은
주문은 사람이 HTS에서 직접 실행한다는 governance/rules/06 원칙을 그대로 유지한 채
(API로 체결을 가져오지 않는다), 실행 후 사람이 수동으로 기록한 실제 체결가를
누적해 가정치(5bps)와 비교할 수 있게 한다. 5건 미만이면 항상 DATA_GATED로 보고한다
— 추정 금지 원칙(spec/00_execution_contract.yaml)을 따른다. 표준 라이브러리
sqlite3만 사용한다.
"""
from __future__ import annotations

import sqlite3
from pathlib import Path
from typing import Any

from src.quant_engine.storage_backend_v1 import StoreSpec, default_sqlite_store_path, normalize_store_spec

SCHEMA = """
CREATE TABLE IF NOT EXISTS realized_slippage_samples (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    ticker TEXT NOT NULL,
    side TEXT NOT NULL CHECK (side IN ('BUY', 'SELL')),
    intended_price REAL NOT NULL,
    actual_fill_price REAL NOT NULL,
    slippage_bps_actual REAL NOT NULL,
    recorded_at TEXT NOT NULL,
    note TEXT,
    inserted_at TEXT DEFAULT (datetime('now'))
);
"""

ASSUMED_SLIPPAGE_BPS = 5.0
MIN_SAMPLE_FOR_COMPARISON = 5


def default_execution_slippage_store_path(root: Path) -> Path:
    return default_sqlite_store_path(root, "execution_slippage/execution_slippage.db")


def resolve_store_path(spec: StoreSpec, root: Path) -> Path:
    backend, location = normalize_store_spec(
        spec, root, default_sqlite_name="execution_slippage/execution_slippage.db"
    )
    if backend != "sqlite":
        raise ValueError("execution_slippage_store_v1 currently executes on sqlite only.")
    return Path(location)


def init_db(db_path: Path) -> None:
    db_path.parent.mkdir(parents=True, exist_ok=True)
    conn = sqlite3.connect(db_path)
    try:
        conn.executescript(SCHEMA)
        conn.commit()
    finally:
        conn.close()


def _compute_slippage_bps(intended_price: float, actual_fill_price: float, side: str) -> float:
    """체결가가 의도가(지정가)보다 불리한 방향으로 움직인 만큼을 양수 bps로 환산한다.

    BUY: 실제 체결가가 의도가보다 높으면(더 비싸게 샀으면) 양수 슬리피지.
    SELL: 실제 체결가가 의도가보다 낮으면(더 싸게 팔았으면) 양수 슬리피지.
    """
    if intended_price <= 0:
        raise ValueError("intended_price must be > 0")
    direction = 1 if side.upper() == "BUY" else -1
    return direction * (actual_fill_price - intended_price) / intended_price * 10_000.0


def insert_realized_slippage_sample(
    db_path: Path,
    *,
    ticker: str,
    side: str,
    intended_price: float,
    actual_fill_price: float,
    recorded_at: str,
    note: str | None = None,
) -> dict[str, Any]:
    init_db(db_path)
    slippage_bps = _compute_slippage_bps(intended_price, actual_fill_price, side)
    conn = sqlite3.connect(db_path)
    try:
        conn.execute(
            "INSERT INTO realized_slippage_samples "
            "(ticker, side, intended_price, actual_fill_price, slippage_bps_actual, recorded_at, note) "
            "VALUES (?, ?, ?, ?, ?, ?, ?)",
            (ticker, side.upper(), intended_price, actual_fill_price, slippage_bps, recorded_at, note),
        )
        conn.commit()
    finally:
        conn.close()
    return {
        "ticker": ticker,
        "side": side.upper(),
        "intended_price": intended_price,
        "actual_fill_price": actual_fill_price,
        "slippage_bps_actual": round(slippage_bps, 4),
        "recorded_at": recorded_at,
    }


def fetch_all_samples(db_path: Path) -> list[dict[str, Any]]:
    if not db_path.exists():
        return []
    conn = sqlite3.connect(db_path)
    conn.row_factory = sqlite3.Row
    try:
        rows = conn.execute(
            "SELECT ticker, side, intended_price, actual_fill_price, slippage_bps_actual, recorded_at, note "
            "FROM realized_slippage_samples ORDER BY recorded_at ASC"
        ).fetchall()
        return [dict(row) for row in rows]
    finally:
        conn.close()


def build_slippage_comparison_report(db_path: Path) -> dict[str, Any]:
    """WBS-7.6 성공 하네스 — 5건 미만이면 DATA_GATED를 정직하게 반환한다(추정 금지)."""
    samples = fetch_all_samples(db_path)
    sample_n = len(samples)
    if sample_n < MIN_SAMPLE_FOR_COMPARISON:
        return {
            "status": "DATA_GATED",
            "sample_n": sample_n,
            "min_required": MIN_SAMPLE_FOR_COMPARISON,
            "assumed_slippage_bps": ASSUMED_SLIPPAGE_BPS,
            "actual_mean_slippage_bps": None,
            "note": f"실측 표본 {sample_n}/{MIN_SAMPLE_FOR_COMPARISON}건 — 비교 불가, 가정치(5bps) 유지",
        }
    actual_mean = sum(s["slippage_bps_actual"] for s in samples) / sample_n
    gap = abs(actual_mean - ASSUMED_SLIPPAGE_BPS)
    return {
        "status": "OK",
        "sample_n": sample_n,
        "assumed_slippage_bps": ASSUMED_SLIPPAGE_BPS,
        "actual_mean_slippage_bps": round(actual_mean, 4),
        "gap_bps": round(gap, 4),
        "recommendation": (
            "가정치(5bps) 유지" if gap <= 3.0 else "spec/55_execution_simulator_contract.yaml의 bps 값을 실측 평균으로 갱신 검토"
        ),
    }
