"""
kis_trader/scripts/approx_param_search_from_log.py
==================================================
log_backfill(판정 로그 백필) 기반 **근사 파람서치** — 완화 방향 1차 추정 전용.

⚠️ 한계 (정직):
  - 매 판정마다 "처음 걸린 검사 1개"의 수치만 있다.
  - 따라서 임계값을 **완화**했을 때 "그 검사를 통과로 전환하는 건수의 상한"만 안다.
  - 전환 후 다음 검사(미관측) 통과 여부는 모른다 → 실제 매수 증가는 이보다 적다.
  - 임계 **강화** 방향은 통과(시그널) 건에 수치가 없어 계산 불가.
  → 정밀 파람서치는 filter_eval(호가 본체)이 쌓인 뒤 별도로.

사용:
  python -m kis_trader.scripts.approx_param_search_from_log --ymd 20260626 --strategy MOMENTUM
"""
from __future__ import annotations

import argparse
import re
from collections import Counter
from typing import Any, Dict, List, Optional

from kis_trader.database.db_manager import get_db

LOG_BACKFILL_SOURCE = "log_backfill"

_RATIO_RE = re.compile(r"잔량\(\d+호가\)\s+([\d.]+)\s+<\s+([\d.]+)")
_SPREAD_RE = re.compile(r"스프레드\s+([\d.]+)%\s+>\s+([\d.]+)%")
_WALL_RE = re.compile(r"합\s+(\d+)주\s+>\s+허용\s+(\d+)주")


def _load_rows(db, ymd: str, strategy: str) -> List[Dict[str, Any]]:
    sql = (
        "SELECT snap_time, code, strategy, reject_code, reject_msg "
        "FROM ws_orderbook WHERE source=%s AND snap_time LIKE %s"
    )
    params: List[Any] = [LOG_BACKFILL_SOURCE, ymd + "%"]
    strat = (strategy or "").strip().upper()
    if strat in ("TAIL", "SHORT"):
        sql += " AND strategy IN ('TAIL','SHORT')"
    elif strat:
        sql += " AND strategy=%s"
        params.append(strat)
    sql += " ORDER BY snap_time"
    return [dict(r) for r in db.conn.execute(sql, tuple(params)).fetchall()]


def run(ymd: str, strategy: str, *, ask_mult_default: float = 3.0) -> None:
    db = get_db()
    rows = _load_rows(db, ymd, strategy)
    if not rows:
        print(f"⚠️ log_backfill 데이터 없음 (ymd={ymd}, strategy={strategy}). 먼저 백필 실행.")
        return

    reason_cnt: Counter = Counter()
    passes = 0
    ratios: List[float] = []        # 호가수급: 실제 ratio
    spreads: List[float] = []       # 호가스프레드: 실제 spread%
    walls: List[tuple] = []         # 매도벽: (ask3, allow)
    cur_ratio_thr = 0.85
    cur_spread_thr = 0.45
    for r in rows:
        rc = r.get("reject_code")
        msg = r.get("reject_msg") or ""
        if not rc:
            passes += 1
            continue
        reason_cnt[rc] += 1
        if rc == "탈락-호가수급":
            m = _RATIO_RE.search(msg)
            if m:
                ratios.append(float(m.group(1)))
                cur_ratio_thr = float(m.group(2))
        elif rc == "탈락-호가스프레드":
            m = _SPREAD_RE.search(msg)
            if m:
                spreads.append(float(m.group(1)))
                cur_spread_thr = float(m.group(2))
        elif rc == "탈락-매도벽":
            m = _WALL_RE.search(msg)
            if m:
                walls.append((int(m.group(1)), int(m.group(2))))

    total = len(rows)
    print(f"\n===== 근사 파람서치 (ymd={ymd}, strategy={strategy or 'ALL'}) =====")
    print(f"호가필터 판정 총 {total}건 | 통과(시그널) {passes} | 거절 {total - passes}")
    print("거절 사유 분포:", dict(reason_cnt))

    # ── 1) 호가수급(min_bid_ask_ratio) 완화 스윕 ───────────────
    print(f"\n[호가수급] 현재 임계 min_bid_ask_ratio = {cur_ratio_thr}")
    print("  임계 낮추면 '호가수급 거절→통과 전환(상한)':")
    for thr in (0.85, 0.7, 0.5, 0.35, 0.2, 0.0):
        flip = sum(1 for x in ratios if x >= thr)
        print(f"    ratio>={thr:<4} → {flip:>4}/{len(ratios)} 전환")

    # ── 2) 스프레드(max_spread_pct) 완화 스윕 ──────────────────
    print(f"\n[스프레드] 현재 임계 max_spread_pct = {cur_spread_thr}%")
    print("  임계 높이면 '스프레드 거절→통과 전환(상한)':")
    for thr in (0.45, 0.6, 0.8, 1.0, 1.5, 2.0):
        flip = sum(1 for x in spreads if x <= thr)
        print(f"    spread<={thr:<4}% → {flip:>4}/{len(spreads)} 전환")

    # ── 3) 매도벽(entry_ask_max_mult) 완화 스윕 ────────────────
    print(f"\n[매도벽] 현재 entry_ask_max_mult = {ask_mult_default} (허용=주문수량×배수)")
    print("  배수 높이면 '매도벽 거절→통과 전환(상한)':")
    for new_mult in (3.0, 5.0, 8.0, 12.0, 20.0, 50.0):
        flip = 0
        for ask3, allow in walls:
            qty_need = allow / ask_mult_default if ask_mult_default > 0 else 0
            new_allow = qty_need * new_mult
            if ask3 <= new_allow:
                flip += 1
        print(f"    mult={new_mult:<5} → {flip:>4}/{len(walls)} 전환")

    print("\n※ 모든 수치는 '해당 검사 통과 전환 상한'이다. 다음 검사(미관측) 통과 여부는")
    print("  포함하지 않으므로 실제 매수 증가는 이보다 적다. 정밀치는 filter_eval 누적 후.")


def main(argv: Optional[List[str]] = None) -> int:
    ap = argparse.ArgumentParser(description="log_backfill 기반 근사 파람서치(완화방향)")
    ap.add_argument("--ymd", required=True, help="거래일 YYYYMMDD")
    ap.add_argument("--strategy", default="MOMENTUM", help="전략 (기본 MOMENTUM)")
    ap.add_argument("--ask-mult-default", type=float, default=3.0,
                    help="당시 entry_ask_max_mult (허용 역산용, 기본 3.0)")
    args = ap.parse_args(argv)
    run(args.ymd, args.strategy, ask_mult_default=args.ask_mult_default)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
