"""
백테스트 결과 Sanity Check 검증.
논리적 불변 조건(FAIL) + 통계적 이상 감지(WARNING)를 수행한다.
"""
from __future__ import annotations

from dataclasses import dataclass
import pandas as pd


RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
RESET = "\033[0m"


@dataclass
class CheckResult:
    name: str
    passed: bool
    level: str   # "FAIL" | "WARNING"
    message: str


def validate(trades: list[dict], summary: dict, cfg) -> dict:
    """
    모든 검증을 실행하고 결과를 dict로 반환한다.
    CLI에도 PASS/WARNING/FAIL을 출력한다.
    """
    results: list[CheckResult] = []

    # 검증 1: 논리적 불변 조건
    results.extend(_check_invariants(trades, cfg))

    # 검증 2: 통계적 이상 감지
    results.extend(_check_statistics(trades, summary))

    # 결과 출력
    _print_results(results)

    return {
        "overall": "PASS" if all(r.passed for r in results) else "FAIL",
        "checks": [
            {"name": r.name, "passed": r.passed, "level": r.level, "message": r.message}
            for r in results
        ],
    }


def _check_invariants(trades: list[dict], cfg=None) -> list[CheckResult]:
    """논리적 불변 조건. 하나라도 위반 시 FAIL."""
    results = []

    if not trades:
        results.append(CheckResult(
            "trade_count", True, "FAIL", "트레이드 없음 (검증 스킵)"
        ))
        return results

    # 1. 청산 시각 >= 진입 시각 (END_OF_DATA는 동일 캔들 가능)
    bad_times = []
    for i, t in enumerate(trades):
        if pd.Timestamp(t["exit_time"]) < pd.Timestamp(t["entry_time"]):
            bad_times.append(i)
    passed = len(bad_times) == 0
    results.append(CheckResult(
        "exit_after_entry",
        passed,
        "FAIL",
        f"모든 트레이드에서 청산 > 진입" if passed else f"위반 트레이드 인덱스: {bad_times}",
    ))

    # 2. SL/TP 방향 정합성
    bad_sltp = []
    for i, t in enumerate(trades):
        if t["side"] == "LONG":
            if not (t["sl"] < t["entry_price"] < t["tp"]):
                bad_sltp.append(i)
        else:
            if not (t["tp"] < t["entry_price"] < t["sl"]):
                bad_sltp.append(i)
    passed = len(bad_sltp) == 0
    results.append(CheckResult(
        "sl_tp_direction",
        passed,
        "FAIL",
        "SL/TP 방향 정합" if passed else f"위반 트레이드 인덱스: {bad_sltp}",
    ))

    # 3. 포지션 비중첩 (같은 심볼에서 직전 청산 ≤ 다음 진입)
    by_symbol: dict[str, list[dict]] = {}
    for t in trades:
        by_symbol.setdefault(t["symbol"], []).append(t)

    overlap_symbols = []
    for sym, sym_trades in by_symbol.items():
        sorted_trades = sorted(sym_trades, key=lambda x: pd.Timestamp(x["entry_time"]))
        for j in range(1, len(sorted_trades)):
            prev_exit = pd.Timestamp(sorted_trades[j - 1]["exit_time"])
            curr_entry = pd.Timestamp(sorted_trades[j]["entry_time"])
            if prev_exit > curr_entry:
                overlap_symbols.append(sym)
                break
    passed = len(overlap_symbols) == 0
    results.append(CheckResult(
        "no_overlap",
        passed,
        "FAIL",
        "포지션 비중첩 확인" if passed else f"중첩 심볼: {overlap_symbols}",
    ))

    # 4. 수수료 항상 양수
    bad_fees = [i for i, t in enumerate(trades) if t["entry_fee"] <= 0 or t["exit_fee"] <= 0]
    passed = len(bad_fees) == 0
    results.append(CheckResult(
        "positive_fees",
        passed,
        "FAIL",
        "수수료 양수 확인" if passed else f"위반 트레이드 인덱스: {bad_fees}",
    ))

    # 5. 잔고가 음수가 된 적 없음
    balance = cfg.initial_balance if cfg is not None else 1000.0
    min_balance = balance
    for t in trades:
        balance += t["net_pnl"]
        min_balance = min(min_balance, balance)
    passed = min_balance >= 0
    results.append(CheckResult(
        "no_negative_balance",
        passed,
        "FAIL",
        "잔고 양수 유지" if passed else f"최저 잔고: {min_balance:.4f}",
    ))

    return results


def _check_statistics(trades: list[dict], summary: dict) -> list[CheckResult]:
    """통계적 이상 감지. WARNING 수준."""
    results = []

    if not trades:
        return results

    win_rate = summary.get("win_rate", 0)
    mdd = summary.get("max_drawdown_pct", 0)
    pf = summary.get("profit_factor", 0)

    # 승률 > 80%
    passed = win_rate <= 80
    results.append(CheckResult(
        "win_rate_high",
        passed,
        "WARNING",
        f"승률 정상 ({win_rate:.1f}%)" if passed else f"승률 {win_rate:.1f}% > 80% — look-ahead bias 의심",
    ))

    # 승률 < 20%
    passed = win_rate >= 20
    results.append(CheckResult(
        "win_rate_low",
        passed,
        "WARNING",
        f"승률 정상 ({win_rate:.1f}%)" if passed else f"승률 {win_rate:.1f}% < 20% — 신호 로직 반전 의심",
    ))

    # MDD 0%
    passed = mdd > 0
    results.append(CheckResult(
        "mdd_nonzero",
        passed,
        "WARNING",
        f"MDD 정상 ({mdd:.1f}%)" if passed else "MDD 0% — SL 미작동 의심",
    ))

    # 월 평균 거래 < 5건
    if len(trades) >= 2:
        first = pd.Timestamp(trades[0]["entry_time"])
        last = pd.Timestamp(trades[-1]["entry_time"])
        months = max(1, (last - first).days / 30)
        trades_per_month = len(trades) / months
        passed = trades_per_month >= 5
        results.append(CheckResult(
            "trade_frequency",
            passed,
            "WARNING",
            f"월 평균 {trades_per_month:.1f}건" if passed else f"월 평균 {trades_per_month:.1f}건 < 5건 — 신호 생성 부족",
        ))

    # Profit Factor > 5.0
    if pf != float("inf"):
        passed = pf <= 5.0
        results.append(CheckResult(
            "profit_factor_high",
            passed,
            "WARNING",
            f"PF 정상 ({pf:.2f})" if passed else f"PF {pf:.2f} > 5.0 — 비현실적 수익",
        ))

    return results


def _print_results(results: list[CheckResult]):
    print("\n" + "=" * 60)
    print("  BACKTEST SANITY CHECK")
    print("=" * 60)

    has_fail = any(not r.passed and r.level == "FAIL" for r in results)
    has_warn = any(not r.passed and r.level == "WARNING" for r in results)

    for r in results:
        if r.passed:
            status = f"{GREEN}PASS{RESET}"
        elif r.level == "FAIL":
            status = f"{RED}FAIL{RESET}"
        else:
            status = f"{YELLOW}WARNING{RESET}"
        print(f"  [{status}] {r.name}: {r.message}")

    print("-" * 60)
    if has_fail:
        print(f"  {RED}RESULT: FAIL — 논리적 불변 조건 위반{RESET}")
    elif has_warn:
        print(f"  {YELLOW}RESULT: WARNING — 수동 확인 필요{RESET}")
    else:
        print(f"  {GREEN}RESULT: ALL PASS{RESET}")
    print("=" * 60 + "\n")
