"""
League Model Backtest — Son 100+ Maç
======================================
Her lig için en son 100-200 maçı (eğitim datasından bağımsız, test seti)
lig bazlı modelle tahmin eder ve gerçek sonuçla karşılaştırır.

Usage:
  python scripts/backtest_league_models.py
  python scripts/backtest_league_models.py --min-matches 150
"""

import os, sys, json, warnings, argparse
import numpy as np
import pandas as pd
import xgboost as xgb
from sklearn.metrics import accuracy_score

warnings.filterwarnings("ignore")
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from models.league_model import get_league_model_loader, MARKET_META, FILE_TO_SIGNAL

AI_ENGINE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATA_PATH     = os.path.join(AI_ENGINE_DIR, "data", "training_data.csv")
REPORTS_DIR   = os.path.join(AI_ENGINE_DIR, "reports")
QL_PATH       = os.path.join(os.path.dirname(AI_ENGINE_DIR), "qualified_leagues.json")

# Gerçek label kolonları (CSV'den)
LABEL_COLS = {
    "MS":      "label_ms",
    "OU15":    "label_ou15",
    "OU25":    "label_ou25",
    "OU35":    "label_ou35",
    "BTTS":    "label_btts",
    "HT":      "label_ht_result",
    "HT_OU05": "label_ht_ou05",
    "HT_OU15": "label_ht_ou15",
    "HTFT":    "label_ht_ft",
    "OE":      "label_odd_even",
    "CARDS":   "label_cards_ou45",
    "HCAP":    "label_handicap_ms",
}

# Model dosya adı → signal key eşlemesi
SIGNAL_TO_FILE = {v: k for k, v in FILE_TO_SIGNAL.items()}

SKIP_COLS = {
    "match_id","home_team_id","away_team_id","league_id","mst_utc",
    "score_home","score_away","total_goals","ht_score_home","ht_score_away","ht_total_goals",
    "label_ms","label_ou05","label_ou15","label_ou25","label_ou35","label_btts",
    "label_ht_result","label_ht_ou05","label_ht_ou15","label_ht_ft",
    "label_odd_even","label_yellow_cards","label_cards_ou45","label_handicap_ms",
}


def backtest_league(
    league_id: str,
    df_league: pd.DataFrame,
    feature_cols: list,
    league_model,
    n_test: int,
) -> dict:
    """Son n_test maçı backtest et, her market için doğruluk döndür."""
    df_sorted = df_league.sort_values("mst_utc")
    df_test = df_sorted.tail(n_test)

    X = df_test[feature_cols].fillna(0)
    results = {}

    for sig_key, mfile_key in SIGNAL_TO_FILE.items():
        label_col = LABEL_COLS.get(sig_key)
        if not label_col or label_col not in df_test.columns:
            continue

        y_true = df_test[label_col].dropna().values
        if len(y_true) < 30:
            continue

        # League-specific model varsa kullan
        if league_model and league_model.has_market(mfile_key):
            probs_list = []
            preds = []
            for _, row in df_test.iterrows():
                feat = row[feature_cols].fillna(0).to_dict()
                probs = league_model.predict_market(mfile_key, feat)
                if probs:
                    best = max(probs, key=probs.__getitem__)
                    meta = MARKET_META[mfile_key]
                    labels = meta[1]
                    pred_idx = labels.index(best)
                    preds.append(pred_idx)
                    probs_list.append(list(probs.values()))

            if not preds:
                continue

            y_valid = df_test[label_col].dropna()
            if len(preds) != len(y_valid):
                min_len = min(len(preds), len(y_valid))
                preds = preds[:min_len]
                y_valid = y_valid.values[:min_len]
            else:
                y_valid = y_valid.values

            acc = accuracy_score(y_valid, preds)
            results[sig_key] = {
                "accuracy": round(acc, 4),
                "n": len(preds),
                "source": "league_specific",
            }

    return results


def backtest_with_general_v25(
    df_test: pd.DataFrame,
    feature_cols: list,
) -> dict:
    """Genel V25 modeli ile backtest."""
    try:
        from models.v25_ensemble import get_v25_predictor
        v25 = get_v25_predictor()
        if not v25._loaded:
            v25.load_models()
    except Exception as e:
        return {}

    X = df_test[feature_cols].fillna(0)
    results = {}

    mkey_map = {
        "MS":      ("ms",          {"1": 0, "X": 1, "2": 2}),
        "OU15":    ("ou15",        {"Over": 0, "Under": 1}),
        "OU25":    ("ou25",        {"Over": 0, "Under": 1}),
        "OU35":    ("ou35",        {"Over": 0, "Under": 1}),
        "BTTS":    ("btts",        {"Yes": 0, "No": 1}),
    }

    for sig_key, (mkey, label_to_idx) in mkey_map.items():
        label_col = LABEL_COLS.get(sig_key)
        if not label_col or label_col not in df_test.columns:
            continue
        y_true = df_test[label_col].dropna().values
        if len(y_true) < 30 or not v25.has_market(mkey):
            continue

        try:
            dmat = xgb.DMatrix(X.values, feature_names=feature_cols)
            models_v25 = v25.models.get(mkey, {})
            if "xgb" not in models_v25:
                continue
            raw = models_v25["xgb"].predict(dmat)
            num_class = list(MARKET_META.get(mkey, (2,)))[0]

            if num_class > 2:
                raw = raw.reshape(-1, num_class)
                preds = np.argmax(raw, axis=1)
            else:
                preds = (raw >= 0.5).astype(int)

            acc = accuracy_score(y_true, preds)
            results[sig_key] = {
                "accuracy": round(acc, 4),
                "n": len(preds),
                "source": "general_v25",
            }
        except Exception:
            continue

    return results


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--min-matches", type=int, default=100)
    parser.add_argument("--test-size",   type=int, default=150,
                        help="Son kaç maçı test için kullan (min 100)")
    args = parser.parse_args()
    n_test = max(args.min_matches, args.test_size)

    print(f"Loading training data ...")
    df = pd.read_csv(DATA_PATH, low_memory=False)
    feature_cols = [c for c in df.columns if c not in SKIP_COLS]
    print(f"  {len(df):,} maç | {len(feature_cols)} feature")

    qualified = json.load(open(QL_PATH)) if os.path.exists(QL_PATH) else []
    loader = get_league_model_loader()

    try:
        import psycopg2
        from data.db import get_clean_dsn
        conn = psycopg2.connect(get_clean_dsn())
        cur = conn.cursor()
        cur.execute("SELECT id, name FROM leagues WHERE id = ANY(%s)", (qualified,))
        league_names = {r[0]: r[1] for r in cur.fetchall()}
        conn.close()
    except Exception:
        league_names = {}

    counts = df[df["league_id"].isin(qualified)].groupby("league_id").size()
    leagues_to_test = counts[counts >= n_test].index.tolist()
    print(f"\nBacktest: {len(leagues_to_test)} lig (>={n_test} maç) | son {n_test} maç kullanılacak\n")

    all_results = []
    markets_order = ["MS", "OU15", "OU25", "OU35", "BTTS", "HT", "HT_OU05", "HT_OU15", "HTFT", "OE", "CARDS", "HCAP"]

    header = f"{'Liga':<35} {'Maç':>5} | " + " | ".join(f"{m:>7}" for m in markets_order)
    print(header)
    print("-" * len(header))

    for league_id in leagues_to_test:
        df_league = df[df["league_id"] == league_id].copy()
        name = league_names.get(league_id, league_id[:20])

        league_model = loader.get(league_id)

        if league_model and league_model.models:
            # Batch predict from CSV features (fast)
            df_test = df_league.sort_values("mst_utc").tail(n_test)
            X = df_test[feature_cols].fillna(0)
            mkt_results = {}

            for mfile_key in list(league_model.models.keys()):
                sig_key = FILE_TO_SIGNAL.get(mfile_key)
                if not sig_key:
                    continue
                label_col = LABEL_COLS.get(sig_key)
                if not label_col or label_col not in df_test.columns:
                    continue
                y_true = df_test[label_col].dropna().values
                if len(y_true) < 30:
                    continue

                try:
                    dmat = xgb.DMatrix(X.values, feature_names=feature_cols)
                    raw  = league_model.models[mfile_key].predict(dmat)
                    nc   = MARKET_META[mfile_key][0]
                    if nc > 2:
                        preds = np.argmax(raw.reshape(-1, nc), axis=1)
                    else:
                        preds = (raw >= 0.5).astype(int)

                    acc = accuracy_score(y_true[:len(preds)], preds[:len(y_true)])
                    mkt_results[sig_key] = {"accuracy": round(float(acc), 4), "n": len(preds), "source": "league_xgb"}
                except Exception as e:
                    mkt_results[sig_key] = {"error": str(e)}

            # Fill missing markets with general V25
            missing_mkts_df = df_league.sort_values("mst_utc").tail(n_test)
            gen_results = backtest_with_general_v25(missing_mkts_df, feature_cols)
            for k, v in gen_results.items():
                if k not in mkt_results:
                    mkt_results[k] = {**v, "source": "general_v25_fallback"}
        else:
            # No league model — use general V25
            df_test = df_league.sort_values("mst_utc").tail(n_test)
            mkt_results = backtest_with_general_v25(df_test, feature_cols)
            for k in mkt_results:
                mkt_results[k]["source"] = "general_v25"

        n_used = min(n_test, len(df_league))

        # Print row
        accs = []
        for m in markets_order:
            r = mkt_results.get(m, {})
            if "accuracy" in r:
                accs.append(f"{r['accuracy']*100:>6.1f}%")
            else:
                accs.append(f"{'—':>7}")
        print(f"{name:<35} {n_used:>5} | " + " | ".join(accs))

        all_results.append({
            "league_id": league_id,
            "league_name": name,
            "n_tested": n_used,
            "markets": mkt_results,
        })

    # ── Özet ──────────────────────────────────────────────────────
    print("\n" + "=" * len(header))
    print("ORTALAMA DOĞRULUK (tüm ligler):")
    for m in markets_order:
        accs = [r["markets"][m]["accuracy"] for r in all_results if m in r["markets"] and "accuracy" in r["markets"][m]]
        if accs:
            print(f"  {m:<10}: {np.mean(accs)*100:.1f}%  (min={min(accs)*100:.1f}%  max={max(accs)*100:.1f}%  n_leagues={len(accs)})")

    # En iyi / en kötü MS ligleri
    ms_sorted = sorted(
        [(r["league_name"], r["markets"].get("MS",{}).get("accuracy",0), r["n_tested"])
         for r in all_results if "MS" in r["markets"] and "accuracy" in r["markets"]["MS"]],
        key=lambda x: x[1], reverse=True
    )
    print("\nEN İYİ MS (Top 10):")
    for name, acc, n in ms_sorted[:10]:
        print(f"  {name:<35} {acc*100:.1f}%  ({n} maç)")
    print("\nEN KÖTÜ MS (Bottom 10):")
    for name, acc, n in ms_sorted[-10:]:
        print(f"  {name:<35} {acc*100:.1f}%  ({n} maç)")

    # Save
    report = {"generated_at": pd.Timestamp.now().isoformat(), "n_test_per_league": n_test, "results": all_results}
    out_path = os.path.join(REPORTS_DIR, "backtest_league_results.json")
    with open(out_path, "w") as f:
        json.dump(report, f, indent=2)
    print(f"\nRapor: {out_path}")


if __name__ == "__main__":
    main()
