"""
Poisson Engine - Matematiksel Gol Modeli
V9 Model için Poisson dağılımı ile gol olasılıkları hesaplar.

Özellikler:
1. Exact score olasılıkları (0-0, 1-0, 1-1, 2-1, vb.)
2. Over/Under olasılıkları (matematiksel)
3. BTTS (Karşılıklı Gol) olasılıkları
4. Expected Goals (xG) tahmini
"""

import math
from typing import Dict, Tuple, Optional
from dataclasses import dataclass, field


def poisson_prob(lam: float, k: int) -> float:
    """
    Poisson olasılık formülü.
    P(X = k) = (λ^k * e^(-λ)) / k!
    """
    if lam <= 0:
        return 1.0 if k == 0 else 0.0
    return (math.pow(lam, k) * math.exp(-lam)) / math.factorial(k)


@dataclass
class PoissonPrediction:
    """Poisson tahmin sonuçları"""
    home_xg: float = 0.0           # Ev sahibi beklenen gol
    away_xg: float = 0.0           # Deplasman beklenen gol
    total_xg: float = 0.0          # Toplam beklenen gol
    
    # Maç sonucu olasılıkları
    home_win_prob: float = 0.0
    draw_prob: float = 0.0
    away_win_prob: float = 0.0
    
    # Alt/Üst olasılıkları
    over_15_prob: float = 0.0
    over_25_prob: float = 0.0
    over_35_prob: float = 0.0
    under_15_prob: float = 0.0
    under_25_prob: float = 0.0
    under_35_prob: float = 0.0
    
    # BTTS
    btts_yes_prob: float = 0.0
    btts_no_prob: float = 0.0
    
    # En olası skorlar
    most_likely_scores: list = field(default_factory=list)


class PoissonEngine:
    """
    Poisson dağılımı ile gol olasılıkları hesaplar.
    İstatistiksel bir yaklaşım - machine learning'den bağımsız.
    """
    
    # Lig bazlı ortalama gol verileri (varsayılan değerler)
    DEFAULT_HOME_XG = 1.45
    DEFAULT_AWAY_XG = 1.15
    DEFAULT_LEAGUE_AVG = 2.60
    
    def __init__(self):
        self.max_goals = 7  # Hesaplama için maksimum gol sayısı
    
    def calculate_xg(
        self,
        home_goals_avg: float,
        home_conceded_avg: float,
        away_goals_avg: float,
        away_conceded_avg: float,
        league_home_avg: float = None,
        league_away_avg: float = None,
        league_total_avg: float = None
    ) -> Tuple[float, float]:
        """
        Beklenen gol (xG) hesapla.
        
        Attack strength * Defense weakness * League average
        """
        # Varsayılan lig ortalamaları
        if league_home_avg is None:
            league_home_avg = self.DEFAULT_HOME_XG
        if league_away_avg is None:
            league_away_avg = self.DEFAULT_AWAY_XG
        if league_total_avg is None:
            league_total_avg = self.DEFAULT_LEAGUE_AVG
        
        # Güç hesaplamaları
        # Ev sahibi saldırı gücü = Ev gol ortalaması / Lig ev gol ortalaması
        home_attack = home_goals_avg / league_home_avg if league_home_avg > 0 else 1.0
        # Deplasman savunma zayıflığı = Deplasman yenilen gol / Lig deplasman yenilen
        away_defense = away_conceded_avg / league_away_avg if league_away_avg > 0 else 1.0
        
        # Deplasman saldırı gücü
        away_attack = away_goals_avg / league_away_avg if league_away_avg > 0 else 1.0
        # Ev sahibi savunma zayıflığı
        home_defense = home_conceded_avg / league_home_avg if league_home_avg > 0 else 1.0
        
        # Expected Goals
        home_xg = home_attack * away_defense * league_home_avg
        away_xg = away_attack * home_defense * league_away_avg
        
        # Aşırı değerleri sınırla
        home_xg = max(0.3, min(home_xg, 4.0))
        away_xg = max(0.2, min(away_xg, 3.5))
        
        return home_xg, away_xg
    
    def calculate_score_matrix(
        self, 
        home_xg: float, 
        away_xg: float
    ) -> Dict[Tuple[int, int], float]:
        """
        Tüm skor kombinasyonlarının olasılıklarını hesapla.
        
        Returns:
            Dict[(home_goals, away_goals)] = probability
        """
        matrix = {}
        
        for home_goals in range(self.max_goals + 1):
            for away_goals in range(self.max_goals + 1):
                prob = poisson_prob(home_xg, home_goals) * poisson_prob(away_xg, away_goals)
                matrix[(home_goals, away_goals)] = prob
        
        return matrix
    
    def calculate_match_odds(
        self,
        home_xg: float,
        away_xg: float
    ) -> Tuple[float, float, float]:
        """
        1X2 olasılıklarını hesapla.
        
        Returns:
            (home_win, draw, away_win) probabilities
        """
        matrix = self.calculate_score_matrix(home_xg, away_xg)
        
        home_win = 0.0
        draw = 0.0
        away_win = 0.0
        
        for (h, a), prob in matrix.items():
            if h > a:
                home_win += prob
            elif h == a:
                draw += prob
            else:
                away_win += prob
        
        # Normalize (toplam 1 olmalı)
        total = home_win + draw + away_win
        if total > 0:
            home_win /= total
            draw /= total
            away_win /= total
        
        return home_win, draw, away_win
    
    def calculate_over_under(
        self,
        home_xg: float,
        away_xg: float
    ) -> Dict[str, float]:
        """
        Alt/Üst olasılıklarını hesapla.
        """
        matrix = self.calculate_score_matrix(home_xg, away_xg)
        
        over_15 = 0.0
        over_25 = 0.0
        over_35 = 0.0
        
        for (h, a), prob in matrix.items():
            total = h + a
            if total > 1.5:
                over_15 += prob
            if total > 2.5:
                over_25 += prob
            if total > 3.5:
                over_35 += prob
        
        return {
            "over_15": over_15,
            "over_25": over_25,
            "over_35": over_35,
            "under_15": 1 - over_15,
            "under_25": 1 - over_25,
            "under_35": 1 - over_35,
        }
    
    def calculate_btts(
        self,
        home_xg: float,
        away_xg: float
    ) -> Tuple[float, float]:
        """
        Karşılıklı Gol (Both Teams To Score) olasılığı.
        """
        # P(Home scores at least 1) = 1 - P(Home scores 0)
        home_scores = 1 - poisson_prob(home_xg, 0)
        # P(Away scores at least 1) = 1 - P(Away scores 0)
        away_scores = 1 - poisson_prob(away_xg, 0)
        
        # P(BTTS) = P(Home scores) * P(Away scores)
        btts_yes = home_scores * away_scores
        btts_no = 1 - btts_yes
        
        return btts_yes, btts_no
    
    def get_most_likely_scores(
        self,
        home_xg: float,
        away_xg: float,
        top_n: int = 5
    ) -> list:
        """
        En olası skorları getir.
        """
        matrix = self.calculate_score_matrix(home_xg, away_xg)
        
        # Olasılığa göre sırala
        sorted_scores = sorted(matrix.items(), key=lambda x: x[1], reverse=True)
        
        return [
            {"score": f"{h}-{a}", "probability": round(prob * 100, 1)}
            for (h, a), prob in sorted_scores[:top_n]
        ]
    
    def predict(
        self,
        home_goals_avg: float,
        home_conceded_avg: float,
        away_goals_avg: float,
        away_conceded_avg: float,
        league_home_avg: float = None,
        league_away_avg: float = None,
        league_total_avg: float = None
    ) -> PoissonPrediction:
        """
        Tam Poisson tahmini.
        """
        prediction = PoissonPrediction()
        
        # 1. xG hesapla
        home_xg, away_xg = self.calculate_xg(
            home_goals_avg, home_conceded_avg,
            away_goals_avg, away_conceded_avg,
            league_home_avg, league_away_avg, league_total_avg
        )
        
        prediction.home_xg = round(home_xg, 2)
        prediction.away_xg = round(away_xg, 2)
        prediction.total_xg = round(home_xg + away_xg, 2)
        
        # 2. Maç sonucu
        hw, d, aw = self.calculate_match_odds(home_xg, away_xg)
        prediction.home_win_prob = round(hw, 3)
        prediction.draw_prob = round(d, 3)
        prediction.away_win_prob = round(aw, 3)
        
        # 3. Alt/Üst
        ou = self.calculate_over_under(home_xg, away_xg)
        prediction.over_15_prob = round(ou["over_15"], 3)
        prediction.over_25_prob = round(ou["over_25"], 3)
        prediction.over_35_prob = round(ou["over_35"], 3)
        prediction.under_15_prob = round(ou["under_15"], 3)
        prediction.under_25_prob = round(ou["under_25"], 3)
        prediction.under_35_prob = round(ou["under_35"], 3)
        
        # 4. BTTS
        btts_yes, btts_no = self.calculate_btts(home_xg, away_xg)
        prediction.btts_yes_prob = round(btts_yes, 3)
        prediction.btts_no_prob = round(btts_no, 3)
        
        # 5. En olası skorlar
        prediction.most_likely_scores = self.get_most_likely_scores(home_xg, away_xg)
        
        return prediction
    
    def get_features(
        self,
        home_goals_avg: float,
        home_conceded_avg: float,
        away_goals_avg: float,
        away_conceded_avg: float,
        league_home_avg: float = None,
        league_away_avg: float = None,
        league_total_avg: float = None
    ) -> Dict[str, float]:
        """
        Model için feature dict.
        """
        pred = self.predict(
            home_goals_avg, home_conceded_avg,
            away_goals_avg, away_conceded_avg,
            league_home_avg, league_away_avg, league_total_avg
        )
        
        return {
            "poisson_home_xg": pred.home_xg,
            "poisson_away_xg": pred.away_xg,
            "poisson_total_xg": pred.total_xg,
            "poisson_home_win": pred.home_win_prob,
            "poisson_draw": pred.draw_prob,
            "poisson_away_win": pred.away_win_prob,
            "poisson_over_15": pred.over_15_prob,
            "poisson_over_25": pred.over_25_prob,
            "poisson_over_35": pred.over_35_prob,
            "poisson_btts_yes": pred.btts_yes_prob,
        }


# Singleton
_engine_instance = None

def get_poisson_engine() -> PoissonEngine:
    """Singleton pattern"""
    global _engine_instance
    if _engine_instance is None:
        _engine_instance = PoissonEngine()
    return _engine_instance


# Test
if __name__ == "__main__":
    engine = get_poisson_engine()
    
    # Örnek: Güçlü ev sahibi vs zayıf deplasman
    print("=" * 60)
    print("POISSON ENGINE TEST")
    print("Galatasaray (ev) vs Antalyaspor (deplasman)")
    print("=" * 60)
    
    pred = engine.predict(
        home_goals_avg=2.1,      # GS ev ortalaması
        home_conceded_avg=0.8,   # GS ev yenilen
        away_goals_avg=0.9,      # Antalya deplasman gol
        away_conceded_avg=1.8,   # Antalya deplasman yenilen
        league_home_avg=1.5,
        league_away_avg=1.1
    )
    
    print(f"\n📊 Expected Goals:")
    print(f"   Ev Sahibi xG: {pred.home_xg}")
    print(f"   Deplasman xG: {pred.away_xg}")
    print(f"   Toplam xG: {pred.total_xg}")
    
    print(f"\n🎯 Maç Sonucu:")
    print(f"   1 (Ev): {pred.home_win_prob*100:.1f}%")
    print(f"   X (Beraberlik): {pred.draw_prob*100:.1f}%")
    print(f"   2 (Deplasman): {pred.away_win_prob*100:.1f}%")
    
    print(f"\n⚽ Alt/Üst:")
    print(f"   2.5 Üst: {pred.over_25_prob*100:.1f}%")
    print(f"   2.5 Alt: {pred.under_25_prob*100:.1f}%")
    
    print(f"\n🤝 Karşılıklı Gol:")
    print(f"   KG Var: {pred.btts_yes_prob*100:.1f}%")
    print(f"   KG Yok: {pred.btts_no_prob*100:.1f}%")
    
    print(f"\n📈 En Olası Skorlar:")
    for score_data in pred.most_likely_scores:
        print(f"   {score_data['score']}: {score_data['probability']}%")
