import math
from dataclasses import dataclass
from .base_calculator import BaseCalculator, CalculationContext
from .confidence import calc_confidence_3way, calc_confidence_2way


@dataclass
class HalfTimePrediction:
    ht_home_prob: float
    ht_draw_prob: float
    ht_away_prob: float
    ht_pick: str
    ht_confidence: float
    
    ht_over_05_prob: float
    ht_under_05_prob: float
    ht_over_15_prob: float
    ht_under_15_prob: float
    ht_ou_pick: str
    ht_ou15_pick: str
    
    ht_home_xg: float
    ht_away_xg: float


class HalfTimeCalculator(BaseCalculator):
    
    def _poisson_pmf(self, k, lam):
        """Poisson probability mass function."""
        if lam <= 0:
            return 1.0 if k == 0 else 0.0
        return (lam ** k) * math.exp(-lam) / math.factorial(k)
        
    def calculate(self, ctx: CalculationContext) -> HalfTimePrediction:  # type: ignore[override]
        team_pred = ctx.team_pred
        odds_pred = ctx.odds_pred
        
        # Config
        ft_to_ht_ratio = self.config.get("half_time.ft_to_ht_ratio", 0.42)
        grid_max = self.config.get("half_time.poisson_grid_max", 5)
        draw_floor = self.config.get("half_time.ht_draw_floor", 0.35)
        low_xg_thr = self.config.get("half_time.low_xg_threshold", 2.0)
        low_xg_adj = self.config.get("half_time.low_xg_ratio_adjust", 0.85)
        
        # FT xG (blended team + odds)
        ft_home_xg = (team_pred.home_xg + odds_pred.poisson_home_xg) / 2
        ft_away_xg = (team_pred.away_xg + odds_pred.poisson_away_xg) / 2
        total_ft_xg = ft_home_xg + ft_away_xg
        
        # Dynamic HT ratio: düşük xG maçlarda ratio'yu küçült
        # Çünkü düşük gollü maçlarda ilk yarıda gol olma ihtimali daha da düşük
        effective_ratio = ft_to_ht_ratio
        if total_ft_xg < low_xg_thr:
            effective_ratio *= low_xg_adj
        
        # HT xG
        ht_home_xg = ft_home_xg * effective_ratio
        ht_away_xg = ft_away_xg * effective_ratio
        ht_total_xg = ht_home_xg + ht_away_xg
        
        # Compute HT 1X2 via bivariate Poisson grid
        ht_home = 0.0
        ht_away = 0.0
        ht_draw = 0.0
        
        # Also compute O/U while iterating
        total_goals_prob = {}
        
        for i in range(grid_max):
            for j in range(grid_max):
                p = self._poisson_pmf(i, ht_home_xg) * self._poisson_pmf(j, ht_away_xg)
                if i > j:
                    ht_home += p
                elif i < j:
                    ht_away += p
                else:
                    ht_draw += p
                
                total = i + j
                total_goals_prob[total] = total_goals_prob.get(total, 0.0) + p
        
        # Draw floor: düşük xG maçlarda beraberlik olasılığını minimum seviyeye çek
        if ht_draw < draw_floor:
            deficit = draw_floor - ht_draw
            ht_draw = draw_floor
            # Deficit'i home ve away'den orantılı düş
            total_ha = ht_home + ht_away
            if total_ha > 0:
                ht_home -= deficit * (ht_home / total_ha)
                ht_away -= deficit * (ht_away / total_ha)
        
        # Normalize
        total_prob = ht_home + ht_draw + ht_away
        if total_prob > 0:
            ht_home /= total_prob
            ht_draw /= total_prob
            ht_away /= total_prob
            
        # XGBoost Integration (HT 1X2 and HT/FT Models)
        w_xgb = self.config.get("xgboost.weight_ht", 0.60)
        xgb_ht_home, xgb_ht_draw, xgb_ht_away = None, None, None
        
        if "ht_result" in ctx.xgboost_preds:
            probs = ctx.xgboost_preds["ht_result"]
            xgb_ht_home, xgb_ht_draw, xgb_ht_away = probs["home"], probs["draw"], probs["away"]
        elif "ht_ft" in ctx.xgboost_preds:
            # Fallback to HT/FT marginals
            htft_payload = ctx.xgboost_preds.get("ht_ft", {})
            probs = None
            if isinstance(htft_payload, dict):
                labels = ("1/1", "1/X", "1/2", "X/1", "X/X", "X/2", "2/1", "2/X", "2/2")
                if all(label in htft_payload for label in labels):
                    probs = [float(htft_payload[label]) for label in labels]

            if probs is None:
                probs = ctx.xgboost_preds.get("ht_ft_raw")
            if probs is not None and len(probs) == 9:
                xgb_ht_home = sum(probs[0:3])
                xgb_ht_draw = sum(probs[3:6])
                xgb_ht_away = sum(probs[6:9])

        if xgb_ht_home is not None:
            ht_home = ht_home * (1 - w_xgb) + xgb_ht_home * w_xgb
            ht_draw = ht_draw * (1 - w_xgb) + xgb_ht_draw * w_xgb
            ht_away = ht_away * (1 - w_xgb) + xgb_ht_away * w_xgb
            
            # Re-normalize
            total = ht_home + ht_draw + ht_away
            ht_home /= total
            ht_draw /= total
            ht_away /= total
                    
        # HT O/U 0.5
        ht_over_05 = 1.0 - math.exp(-ht_total_xg)
        if "ht_ou05" in ctx.xgboost_preds:
            w_xgb = self.config.get("xgboost.weight_ou", 0.60)
            xgb_ht_over_05 = float(ctx.xgboost_preds["ht_ou05"])
            ht_over_05 = ht_over_05 * (1 - w_xgb) + xgb_ht_over_05 * w_xgb

        ht_over_05_min = self.config.get("half_time.ht_over_05_min", 0.20)
        ht_over_05_max = self.config.get("half_time.ht_over_05_max", 0.95)
        ht_over_05 = max(ht_over_05_min, min(ht_over_05_max, ht_over_05))
        
        # HT O/U 1.5
        # P(total >= 2) = 1 - P(0) - P(1)
        ht_over_15 = sum(p for g, p in total_goals_prob.items() if g >= 2)
        if "ht_ou15" in ctx.xgboost_preds:
            w_xgb = self.config.get("xgboost.weight_ou", 0.60)
            xgb_ht_over_15 = float(ctx.xgboost_preds["ht_ou15"])
            ht_over_15 = ht_over_15 * (1 - w_xgb) + xgb_ht_over_15 * w_xgb
            
        ht_over_15 = max(0.02, min(0.95, ht_over_15))
        
        # Picks
        ht_probs = [(ht_home, "İY 1"), (ht_draw, "İY X"), (ht_away, "İY 2")]
        ht_sorted = sorted(ht_probs, key=lambda x: x[0], reverse=True)
        ht_pick = ht_sorted[0][1]
        ht_confidence = calc_confidence_3way(ht_sorted[0][0])
        
        # HT O/U picks
        ht_ou_thr = self.config.get("half_time.ht_ou_threshold", 0.55)
        ht_ou_pick = "İY 0.5 Üst" if ht_over_05 > ht_ou_thr else "İY 0.5 Alt"
        ht_ou15_pick = "İY 1.5 Üst" if ht_over_15 > 0.45 else "İY 1.5 Alt"
        
        return HalfTimePrediction(
            ht_home_prob=ht_home,
            ht_draw_prob=ht_draw,
            ht_away_prob=ht_away,
            ht_pick=ht_pick,
            ht_confidence=ht_confidence,
            ht_over_05_prob=ht_over_05,
            ht_under_05_prob=1.0 - ht_over_05,
            ht_over_15_prob=ht_over_15,
            ht_under_15_prob=1.0 - ht_over_15,
            ht_ou_pick=ht_ou_pick,
            ht_ou15_pick=ht_ou15_pick,
            ht_home_xg=ht_home_xg,
            ht_away_xg=ht_away_xg
        )
