#!/usr/bin/env python3
"""Train a goal-number model for Russia Premier League (R1) using historical data.

This script trains a model specifically for R1 league using:
1. Historical match results (FTHG, FTAG)
2. Team performance statistics
3. Rolling averages for goals and over/under rates
"""

from __future__ import annotations

import json
import math
from collections import defaultdict
from pathlib import Path
from typing import List

import joblib
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

REPO_ROOT = Path(__file__).resolve().parents[1]
HISTORICAL_DIR = REPO_ROOT / 'historical_data'
OUTPUT_DIR = REPO_ROOT / 'experiments' / 'goal_number_r1_only'


def mean_or_default(values: List[float], default: float) -> float:
    return float(np.mean(values)) if values else float(default)


def build_features(df: pd.DataFrame) -> pd.DataFrame:
    """Build features from match history."""
    team_hist = defaultdict(lambda: {
        "gf": [], "ga": [], "tot": [], "over": [],
        "home_gf": [], "home_ga": [], "home_over": [],
        "away_gf": [], "away_ga": [], "away_over": [],
        "gf_last5": [], "ga_last5": [], "over_last5": [],
    })
    
    rows = []
    
    for _, match in df.iterrows():
        home = str(match["HomeTeam"])
        away = str(match["AwayTeam"])
        home_hist = team_hist[home]
        away_hist = team_hist[away]
        
        total_goals = float(match.get('FTHG', 0) or 0) + float(match.get('FTAG', 0) or 0)
        is_over = 1 if total_goals > 2.5 else 0
        
        row = {
            "home_gf_avg": mean_or_default(home_hist["gf"], 1.4),
            "home_ga_avg": mean_or_default(home_hist["ga"], 1.4),
            "away_gf_avg": mean_or_default(away_hist["gf"], 1.2),
            "away_ga_avg": mean_or_default(away_hist["ga"], 1.2),
            "home_tot_avg": mean_or_default(home_hist["tot"], 2.6),
            "away_tot_avg": mean_or_default(away_hist["tot"], 2.6),
            "home_over_rate": mean_or_default(home_hist["over"], 0.5),
            "away_over_rate": mean_or_default(away_hist["over"], 0.5),
            "home_home_gf_avg": mean_or_default(home_hist["home_gf"], mean_or_default(home_hist["gf"], 1.4)),
            "home_home_ga_avg": mean_or_default(home_hist["home_ga"], mean_or_default(home_hist["ga"], 1.4)),
            "away_away_gf_avg": mean_or_default(away_hist["away_gf"], mean_or_default(away_hist["gf"], 1.2)),
            "away_away_ga_avg": mean_or_default(away_hist["away_ga"], mean_or_default(away_hist["ga"], 1.2)),
            "home_home_over_rate": mean_or_default(home_hist["home_over"], mean_or_default(home_hist["over"], 0.5)),
            "away_away_over_rate": mean_or_default(away_hist["away_over"], mean_or_default(away_hist["over"], 0.5)),
            "home_gf_last5": mean_or_default(home_hist["gf_last5"], mean_or_default(home_hist["gf"], 1.4)),
            "home_ga_last5": mean_or_default(home_hist["ga_last5"], mean_or_default(home_hist["ga"], 1.4)),
            "away_gf_last5": mean_or_default(away_hist["gf_last5"], mean_or_default(away_hist["gf"], 1.2)),
            "away_ga_last5": mean_or_default(away_hist["ga_last5"], mean_or_default(away_hist["ga"], 1.2)),
            "home_over_last5": mean_or_default(home_hist["over_last5"], mean_or_default(home_hist["over"], 0.5)),
            "away_over_last5": mean_or_default(away_hist["over_last5"], mean_or_default(away_hist["over"], 0.5)),
            "expected_goals_proxy": float(np.nanmean([
                mean_or_default(home_hist["gf"], 1.4),
                mean_or_default(home_hist["home_gf"], mean_or_default(home_hist["gf"], 1.4)),
                mean_or_default(away_hist["ga"], 1.2),
                mean_or_default(away_hist["away_ga"], mean_or_default(away_hist["ga"], 1.2)),
                mean_or_default(away_hist["gf"], 1.2),
                mean_or_default(away_hist["away_gf"], mean_or_default(away_hist["gf"], 1.2)),
                mean_or_default(home_hist["ga"], 1.4),
                mean_or_default(home_hist["home_ga"], mean_or_default(home_hist["ga"], 1.4)),
            ])),
            "combined_over_rate": float(np.nanmean([mean_or_default(home_hist["over"], 0.5), mean_or_default(away_hist["over"], 0.5)])),
            "is_over": is_over,
        }
        rows.append(row)
        
        # Update team history
        home_hist["gf"].append(total_goals)
        home_hist["ga"].append(total_goals)
        home_hist["tot"].append(total_goals)
        home_hist["over"].append(is_over)
        home_hist["gf_last5"].append(total_goals)
        home_hist["ga_last5"].append(total_goals)
        home_hist["over_last5"].append(is_over)
        if pd.notna(match.get('FTHG')) and pd.notna(match.get('FTAG')):
            home_hist["home_gf"].append(float(match['FTHG']))
            home_hist["home_ga"].append(float(match['FTAG']))
            home_hist["home_over"].append(is_over)
        
        away_hist["gf"].append(total_goals)
        away_hist["ga"].append(total_goals)
        away_hist["tot"].append(total_goals)
        away_hist["over"].append(is_over)
        away_hist["gf_last5"].append(total_goals)
        away_hist["ga_last5"].append(total_goals)
        away_hist["over_last5"].append(is_over)
        if pd.notna(match.get('FTHG')) and pd.notna(match.get('FTAG')):
            away_hist["away_gf"].append(float(match['FTHG']))
            away_hist["away_ga"].append(float(match['FTAG']))
            away_hist["away_over"].append(is_over)
    
    return pd.DataFrame(rows)


def train_r1_model() -> None:
    """Train a goal-number model specifically for Russia Premier League."""
    print("Loading R1 historical data...")
    
    # Load R1 historical data
    r1_path = HISTORICAL_DIR / 'r1_history.csv'
    if not r1_path.exists():
        print(f"ERROR: R1 historical data not found at {r1_path}")
        return
    
    df = pd.read_csv(r1_path)
    print(f"Loaded {len(df)} R1 matches")
    
    # Build features
    print("Building features...")
    features_df = build_features(df)
    print(f"Built {len(features_df)} feature rows")
    
    # Prepare data for training
    feature_cols = [col for col in features_df.columns if col != 'is_over']
    X = features_df[feature_cols].fillna(0)
    y = features_df['is_over'].astype(int)
    
    print(f"Features: {len(feature_cols)}")
    print(f"Over rate: {y.mean():.2%}")
    
    # Split data (80% train, 20% test)
    split_idx = int(len(X) * 0.8)
    X_train, X_test = X.iloc[:split_idx], X.iloc[split_idx:]
    y_train, y_test = y.iloc[:split_idx], y.iloc[split_idx:]
    
    print(f"Train: {len(X_train)}, Test: {len(X_test)}")
    
    # Test different models
    models = {
        'LogisticRegression': Pipeline([
            ('imputer', SimpleImputer(strategy='mean')),
            ('scaler', StandardScaler()),
            ('clf', LogisticRegression(max_iter=1000, random_state=42))
        ]),
        'RandomForest': Pipeline([
            ('imputer', SimpleImputer(strategy='mean')),
            ('scaler', StandardScaler()),
            ('clf', RandomForestClassifier(n_estimators=200, max_depth=10, random_state=42, n_jobs=-1))
        ]),
    }
    
    best_model = None
    best_config = None
    best_test_acc = 0.0
    
    print("\nTraining models...")
    for model_name, pipeline in models.items():
        print(f"\n{model_name}:")
        pipeline.fit(X_train, y_train)
        
        # Overall accuracy
        y_pred = pipeline.predict(X_test)
        overall_acc = accuracy_score(y_test, y_pred)
        print(f"  Overall accuracy: {overall_acc:.2%}")
        
        # Test different thresholds
        probs = pipeline.predict_proba(X_test)[:, 1]
        thresholds = np.arange(0.45, 0.81, 0.01)
        
        for threshold in thresholds:
            mask = (probs >= threshold) | (probs <= (1.0 - threshold))
            bet_count = int(mask.sum())
            
            if bet_count < 25:
                continue
            
            preds = (probs >= threshold).astype(int)
            bet_acc = accuracy_score(y_test[mask], preds[mask])
            
            if bet_acc > best_test_acc or (bet_acc == best_test_acc and bet_count > best_config.get('bet_count', 0)):
                best_test_acc = bet_acc
                best_model = pipeline
                best_config = {
                    'league': 'r1',
                    'league_name': '俄超',
                    'model': model_name,
                    'threshold': float(threshold),
                    'bet_count': bet_count,
                    'bet_acc': float(bet_acc),
                    'overall_acc': float(overall_acc),
                    'rows': int(len(features_df)),
                    'train_rows': int(len(X_train)),
                    'test_rows': int(len(X_test)),
                    'features': feature_cols,
                }
    
    if best_model is None or best_config is None:
        print("\nERROR: No model met the minimum bet count requirement (25)")
        return
    
    print(f"\n{'='*60}")
    print("BEST MODEL")
    print(f"{'='*60}")
    print(f"Model: {best_config['model']}")
    print(f"Threshold: {best_config['threshold']:.2f}")
    print(f"Bet count: {best_config['bet_count']}")
    print(f"Bet accuracy: {best_config['bet_acc']:.2%}")
    print(f"Overall accuracy: {best_config['overall_acc']:.2%}")
    print(f"{'='*60}\n")
    
    # Save model
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    model_path = OUTPUT_DIR / 'r1_goal_number_pipeline.pkl'
    config_path = OUTPUT_DIR / 'r1_goal_number_config.json'
    
    joblib.dump(best_model, model_path)
    
    config = {
        **best_config,
        'task': 'goal-number',
        'target': 'over_under_2_5',
        'model_path': str(model_path),
    }
    
    with open(config_path, 'w', encoding='utf-8') as f:
        json.dump(config, f, ensure_ascii=False, indent=2)
    
    print(f"Model saved to: {model_path}")
    print(f"Config saved to: {config_path}")
    
    # Copy to goal_number_api_dynamic directory for use
    goal_number_dir = REPO_ROOT / 'artifacts' / 'goal_number_api_dynamic_current'
    goal_number_dir.mkdir(parents=True, exist_ok=True)
    
    import shutil
    shutil.copy(model_path, goal_number_dir / 'goal_number_api_dynamic_pipeline.pkl')
    shutil.copy(config_path, goal_number_dir / 'goal_number_api_dynamic_config.json')
    
    print(f"\nModel copied to goal_number_api_dynamic directory")
    print("Ready for use with R1 (Russia Premier League)")


if __name__ == '__main__':
    train_r1_model()
