#!/usr/bin/env python3
"""
꼬리 실매 vs 백테 건별 forensics (C).

실매 trade_history(SHORT) 각 건에 대해:
  유니버스 IN/OUT · 재편입 · 당일봉 entry_i · 웜업 후 align 신호 · 백테 체결 여부

사용:
  python3 -u scripts/tail_live_bt_forensics.py --date 2026-07-16
  nohup python3 -u scripts/tail_live_bt_forensics.py --date 2026-07-16 \
    > logs/tail_live_bt_forensics_20260716.log 2>&1 &
"""
from __future__ import annotations

import argparse
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--date", default="2026-07-16", help="YYYY-MM-DD")
    args = ap.parse_args()
    day = args.date.replace("-", "")
    day_dash = f"{day[:4]}-{day[4:6]}-{day[6:8]}"

    from database import TradeDB
    from kis_trader.engine import tail_engine as te
    from kis_trader.engine.tail_engine import (
        _eval_live_align_lookback,
        _last_closed_bar_index,
        _universe_enter_minutes,
    )
    from kis_trader.backtest import tail_backtest_common as tbc
    from kis_trader.backtest.universe_timeline import build_universe_timeline

    db = TradeDB()
    live = db.conn.execute(
        "SELECT code, name, buy_date, buy_price, sell_date, realized_pnl "
        "FROM trade_history WHERE strategy=%s AND buy_date LIKE %s "
        "ORDER BY buy_date",
        ("SHORT", f"{day_dash}%"),
    ).fetchall()
    print(f"=== 꼬리 forensics {day_dash} live={len(live)} ===")

    base = te.get_tail_defaults_from_db(db)
    universe, src, n_slots, _ = tbc.resolve_tail_universe(
        day, day, use_saved_history=True, strategy_id="SHORT",
    )
    tl = build_universe_timeline(
        strategy_id="SHORT", start_ymd=day, end_ymd=day,
        debounce_sec=0, strict=False,
    )
    start_key, end_key = day + "0000", day + "2359"
    candles_by_code, _, _ = tbc.load_tail_candles_by_code(
        db, start_key, end_key, int(base.get("timeframe") or 3),
        rsi_period=int(base.get("rsi_period") or 14),
    )
    # REST 웜업 (유니버스 교집합)
    tbc.inject_tail_rest_warmup_memory(
        candles_by_code, start_key,
        timeframe=int(base.get("timeframe") or 3),
        universe_by_slot=universe,
    )

    port = tbc.resolve_tail_portfolio_params(
        dict(db.conn.execute("SELECT * FROM env_config ORDER BY id DESC LIMIT 1").fetchone() or {}),
        base,
    )
    row = db.conn.execute("SELECT * FROM env_config ORDER BY id DESC LIMIT 1").fetchone()
    fee, tax, _ = tbc.fee_and_slot_from_env_row(dict(row) if row else None)
    params = dict(base)
    tbc.merge_tail_portfolio_into_params(params, port)
    meta = {"db": db, "start_key": start_key, "end_key": end_key}
    bt_trades = tbc.run_tail_backtest_web_aligned(
        candles_by_code, params, universe,
        slot_money=float(port["slot_money"]),
        fee_rate=fee, sell_tax=tax,
        total_budget_krw=float(port["total_budget_krw"]),
        meta_out=meta,
    )
    bt_by_code = {}
    for t in bt_trades:
        bt_by_code.setdefault(str(t.get("code")), []).append(t)

    enter_mins = _universe_enter_minutes(universe, tl, None)
    print(f"universe src={src} slots={n_slots} enter_minutes={len(enter_mins)}")
    print(f"warmup bars target={tbc.tail_backtest_candle_warmup_bars()} "
          f"rest={meta.get('skip_stats', {}).get('rest_warmup')}")
    print(f"BT trades={len(bt_trades)} pnl={sum(int(t.get('pnl') or 0) for t in bt_trades)}")
    print()

    for r in live:
        code = str(r["code"])
        buy_ts = str(r["buy_date"])
        buy_hm = buy_ts[11:16].replace(":", "")
        t12 = day + buy_hm
        name = r.get("name") or code
        print(f"── {code} {name} live {buy_ts} @{int(r['buy_price'])} pnl={r['realized_pnl']}")

        # transitions that day
        prev = False
        trans = []
        for et_row in db.conn.execute(
            """SELECT event_time, MAX(code=%s) has_me
               FROM target_candidates_history
               WHERE strategy_id=%s AND event_time LIKE %s
               GROUP BY event_time ORDER BY event_time""",
            (code, "SHORT", f"{day_dash}%"),
        ).fetchall():
            has = bool(et_row["has_me"])
            if has != prev:
                trans.append((str(et_row["event_time"]), "IN" if has else "OUT"))
                prev = has
        print(f"   transitions: {trans[:8]}{'...' if len(trans) > 8 else ''}")

        in_at_buy = False
        if tl is not None:
            codes = tl.codes_at(t12 + "00") or []
            in_at_buy = code in codes
        print(f"   universe@buy {t12}: {'IN' if in_at_buy else 'OUT'}")

        bars = candles_by_code.get(code) or []
        n_prev = sum(1 for c in bars if str(c.get("candle_time") or "")[:8] < day)
        ei = _last_closed_bar_index(bars, t12, int(base.get("timeframe") or 3))
        print(f"   candles n={len(bars)} prev_day={n_prev} entry_i@buy={ei}",
              f"bar={bars[ei]['candle_time'] if ei >= 0 else None}")

        st = {"daily_cnt": 0, "last_exit_dt": None, "daily_pnl_krw": 0.0}
        if ei >= 19:
            rej, msg, sig = _eval_live_align_lookback(
                bars, ei, params, st,
                lookback=max(1, int(params.get("live_signal_lookback_bars") or 1)),
            )
            print(f"   align: reject={rej} msg={(msg or '')[:70]} "
                  f"sig={bool(sig)} px={sig.get('entry_price') if sig else None}")
        else:
            print(f"   align: SKIP entry_i={ei} < 19 (웜업 부족)")

        hits = bt_by_code.get(code) or []
        if hits:
            for h in hits:
                print(f"   BT hit: {h.get('entry_time')} @{h.get('entry')} "
                      f"→ {h.get('exit_time')} pnl={h.get('pnl')}")
        else:
            print("   BT hit: NONE")
        print()

    db.close()
    return 0


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