#!/usr/bin/env python3
"""
训练 ah_1year_v3 缺失的联赛: r1 (俄超) 和 sc1 (苏超)
"""
import os, sys, json, warnings, joblib
from pathlib import Path
from collections import defaultdict
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import accuracy_score

warnings.filterwarnings('ignore')
np.random.seed(42)

REPO = Path('/root/.openclaw/workspace-flj_bot1/football-predictor-repro')
HIST_DIR = REPO / 'historical_data'
ODDS_DIR = REPO / 'artifacts' / 'odds_api_history'
AH1_MODEL_DIR = REPO / 'models' / 'ah_1year_v3'
BACKUP_DIR = Path('/root/backup_football_20260404/historical_data')

LEAGUE_NAMES = {
    'r1': '俄超', 'sc1': '苏超',
}

FEAT_COLS = ['home_gavg','home_gavga','home_tavg','home_winr','home_undr','home_gstd',
    'home_hgf','home_hga','home_hgc','home_agf','home_aga','home_agc','home_mom','home_rf',
    'home_gf3','home_ga3','home_gf5','home_ga5','home_gf10','home_ga10',
    'away_gavg','away_gavga','away_tavg','away_winr','away_undr','away_gstd',
    'away_hgf','away_hga','away_hgc','away_agf','away_aga','away_agc','away_mom','away_rf',
    'away_gf3','away_ga3','away_gf5','away_ga5','away_gf10','away_ga10',
    'hg_diff','ga_diff','tot_diff','win_diff','mom_diff',
    'B365AHH','B365AHA','AvgAHH','AvgAHA','PAHH','PAHA','AHh',
    'B365>2.5','B365<2.5','Avg>2.5','Avg<2.5','ah_ratio','ah_prob_home']

def make_hist():
    return defaultdict(lambda: {'gf':[],'ga':[],'tot':[],'win':[],'und':[],
            'hgf':[],'hga':[],'hgc':[],'agf':[],'aga':[],'agc':[],'mom':[],'rf':[]})

def build_features(df, team_hist):
    rows = []
    for _, m in df.iterrows():
        h, a = str(m['HomeTeam']), str(m['AwayTeam'])
        hh, aa = team_hist[h], team_hist[a]
        fthg = float(m.get('FTHG', 0) or 0)
        ftag = float(m.get('FTAG', 0) or 0)
        
        def mn(v, n):
            v = list(v)
            return float(np.mean(v[-n:])) if len(v) >= n else (float(np.mean(v)) if v else 0.0)
        def std(v):
            v = list(v)
            return float(np.std(v[-10:])) if len(v) >= 2 else 0.5
        
        d = {}
        for p, s in [('home', hh), ('away', aa)]:
            d[f'{p}_gavg'] = np.mean(s['gf']) if s['gf'] else 1.5
            d[f'{p}_gavga'] = np.mean(s['ga']) if s['ga'] else 1.5
            d[f'{p}_tavg'] = np.mean(s['tot']) if s['tot'] else 2.5
            d[f'{p}_winr'] = np.mean(s['win']) if s['win'] else 0.5
            d[f'{p}_undr'] = np.mean(s['und']) if s['und'] else 0.5
            d[f'{p}_gstd'] = std(s.get('gf',[]))
            d[f'{p}_hgf'] = np.mean(s['hgf']) if s['hgf'] else d[f'{p}_gavg']
            d[f'{p}_hga'] = np.mean(s['hga']) if s['hga'] else d[f'{p}_gavga']
            d[f'{p}_hgc'] = np.mean(s['hgc']) if s['hgc'] else 0.3
            d[f'{p}_agf'] = np.mean(s['agf']) if s['agf'] else d[f'{p}_gavg']
            d[f'{p}_aga'] = np.mean(s['aga']) if s['aga'] else d[f'{p}_gavga']
            d[f'{p}_agc'] = np.mean(s['agc']) if s['agc'] else 0.3
            d[f'{p}_mom'] = np.mean(s['mom'][-5:]) if len(s.get('mom',[])) >= 2 else 0.0
            d[f'{p}_rf'] = np.mean(s['rf'][-5:]) if len(s.get('rf',[])) >= 2 else 0.0
            for n in [3, 5, 10]:
                d[f'{p}_gf{n}'] = mn(s.get('gf',[]), n)
                d[f'{p}_ga{n}'] = mn(s.get('ga',[]), n)
        
        d['hg_diff'] = d['home_gavg'] - d['away_agf']
        d['ga_diff'] = d['home_gavga'] - d['away_aga']
        d['tot_diff'] = d['home_tavg'] - d['away_tavg']
        d['win_diff'] = d['home_winr'] - d['away_winr']
        d['mom_diff'] = d['home_mom'] - d['away_mom']
        
        for c in ['B365AHH','B365AHA','AvgAHH','AvgAHA','PAHH','PAHA','AHh','B365>2.5','B365<2.5','Avg>2.5','Avg<2.5']:
            v = m.get(c)
            if c == 'AHh':
                d[c] = float(v) if pd.notna(v) else 0.0
            else:
                d[c] = float(v) if pd.notna(v) and float(v) > 1 else 0.0
        
        b365h = d.get('B365AHH', 0)
        b365a = d.get('B365AHA', 0)
        if b365h > 1 and b365a > 1:
            d['ah_ratio'] = b365h / b365a
            d['ah_prob_home'] = b365a / (b365h + b365a)
        else:
            d['ah_ratio'] = 1.0
            d['ah_prob_home'] = 0.5
        
        rows.append(d)
        
        # Update history
        for arr in [hh, aa]:
            arr['gf'].append(fthg); arr['ga'].append(ftag); arr['tot'].append(fthg + ftag)
            arr['win'].append(1 if fthg > ftag else 0); arr['und'].append(1 if fthg < ftag else 0)
        hh['hgf'].append(fthg); hh['hga'].append(ftag); hh['hgc'].append(1 if ftag == 0 else 0)
        aa['agf'].append(ftag); aa['aga'].append(fthg); aa['agc'].append(1 if fthg == 0 else 0)
        for arr in [hh, aa]:
            g = arr['gf']
            if len(g) >= 2:
                arr['mom'].append(np.mean(g[-3:]) - np.mean(g[-6:-3]) if len(g) >= 6 else 0)
                arr['rf'].append(np.mean(arr['win'][-3:]) - np.mean(arr['win'][:-3]) if len(arr['win']) >= 6 else 0)
    
    return pd.DataFrame(rows)

def get_ah_outcome(row):
    fthg = float(row.get('FTHG', 0) or 0)
    ftag = float(row.get('FTAG', 0) or 0)
    ahh = float(row.get('AHh', 0) or 0)
    adj = fthg - ahh
    return 1 if adj > ftag else (0 if adj < ftag else 1)  # push -> home

def train_league(code):
    league_name = LEAGUE_NAMES.get(code, code)
    print(f"\n{'='*60}")
    print(f"训练 ah_1year_v3 - {code} ({league_name})")
    print(f"{'='*60}")
    
    # Load data
    if code == 'r1':
        # r1: use odds_api_merged
        odds_file = ODDS_DIR / f'{code}_odds_api_merged.csv'
        if not odds_file.exists():
            print(f"  找不到文件: {odds_file}")
            return False
        df = pd.read_csv(odds_file, low_memory=False)
    else:
        # sc1: restore from backup
        backup_file = BACKUP_DIR / f'{code}_history.csv'
        if not backup_file.exists():
            print(f"  找不到备份文件: {backup_file}")
            return False
        df = pd.read_csv(backup_file, low_memory=False)
    
    # Filter for valid odds
    df = df[df['B365AHH'].notna() & (df['B365AHH'] > 1)]
    df = df[df['B365AHA'].notna() & (df['B365AHA'] > 1)]
    df = df[df['FTHG'].notna() & df['FTAG'].notna()]
    # Try parsing dates - use dayfirst=False for YYYY-MM-DD format
    df['Date'] = pd.to_datetime(df['Date'], dayfirst=False, errors='coerce')
    if df['Date'].isna().all():
        # Fallback to dayfirst=True
        df['Date'] = pd.to_datetime(df['Date'], dayfirst=True, errors='coerce')
    df = df[df['Date'].notna()].sort_values('Date').reset_index(drop=True)
    
    print(f"  可用数据: {len(df)} 条")
    
    if len(df) < 30:
        print(f"  数据太少，跳过")
        return False
    
    # Build features
    th = make_hist()
    X = build_features(df, th).fillna(0).replace([np.inf, -np.inf], 0)
    y = np.array([get_ah_outcome(r) for _, r in df.iterrows()])
    
    print(f"  特征维度: {X.shape}, 正例比例: {y.mean():.3f}")
    
    # Train/test split (time-based: 80% train, 20% test)
    n = len(X)
    split = max(30, int(n * 0.8))
    X_tr, X_te = X.iloc[:split], X.iloc[split:]
    y_tr, y_te = y[:split], y[split:]
    
    # Train RandomForest
    model = RandomForestClassifier(n_estimators=200, max_depth=5, min_samples_leaf=10, random_state=42, n_jobs=-1)
    model.fit(X_tr, y_tr)
    acc = accuracy_score(y_te, model.predict(X_te))
    print(f"  准确率: {acc:.3f}")
    
    # Calibrate
    cal_model = CalibratedClassifierCV(model, cv=3, method='isotonic')
    cal_model.fit(X_tr, y_tr)
    cal_acc = accuracy_score(y_te, cal_model.predict(X_te))
    print(f"  校准后准确率: {cal_acc:.3f}")
    
    # Save
    out_dir = AH1_MODEL_DIR / code
    out_dir.mkdir(parents=True, exist_ok=True)
    
    cfg = {
        'league': code, 'league_name': league_name,
        'accuracy': float(cal_acc),
        'n_samples': int(n),
        'threshold': 0.5,
        'direction': 'over',
        'model_type': 'RF_calibrated',
    }
    with open(out_dir / 'optimized_config.json', 'w') as f:
        json.dump(cfg, f, indent=2)
    joblib.dump(cal_model, out_dir / 'optimized_pipeline.pkl')
    print(f"  ✅ 保存到 {out_dir}")
    return True

if __name__ == '__main__':
    results = {}
    for code in ['r1', 'sc1']:
        try:
            ok = train_league(code)
            results[code] = '✅ 成功' if ok else '⚠️ 跳过'
        except Exception as e:
            print(f"  ❌ 错误: {e}")
            import traceback
            traceback.print_exc()
            results[code] = f'❌ {e}'
    
    print(f"\n\n训练结果: {results}")
    
    # List final coverage
    ah1_leagues = sorted([d.name for d in AH1_MODEL_DIR.iterdir() if d.is_dir()])
    print(f"\nah_1year_v3 现在覆盖 {len(ah1_leagues)} 个联赛: {ah1_leagues}")