from dataclasses import dataclass
from typing import Dict, Any, List
from .base_calculator import BaseCalculator, CalculationContext
from .confidence import calc_confidence_3way_with_agreement, calc_confidence_dc

@dataclass
class MatchResultPrediction:
    ms_home_prob: float
    ms_draw_prob: float
    ms_away_prob: float
    ms_pick: str
    ms_confidence: float
    
    dc_1x_prob: float
    dc_x2_prob: float
    dc_12_prob: float
    dc_pick: str
    dc_confidence: float

class MatchResultCalculator(BaseCalculator):
    
    def _get_engine_winner(self, home_prob: float, draw_prob: float, away_prob: float) -> str:
        """Determine which outcome an engine favors."""
        probs = {"1": home_prob, "X": draw_prob, "2": away_prob}
        return max(probs, key=probs.__getitem__)
    
    def calculate(self, ctx: CalculationContext) -> MatchResultPrediction:  # type: ignore[override]
        # Weights
        w_team = ctx.weights["team"]
        w_player = ctx.weights["player"]
        w_odds = ctx.weights["odds"]
        w_referee = ctx.weights["referee"]
        
        # Engine predictions
        team_pred = ctx.team_pred
        odds_pred = ctx.odds_pred
        player_mods = ctx.player_mods
        referee_mods = ctx.referee_mods
        
        # Weighted ensemble for 1X2
        ms_home = (
            team_pred.home_win_prob * w_team +
            odds_pred.market_home_prob * w_odds +
            team_pred.home_win_prob * player_mods["home_modifier"] * w_player +
            odds_pred.market_home_prob * referee_mods["home_modifier"] * w_referee
        )
        
        ms_away = (
            team_pred.away_win_prob * w_team +
            odds_pred.market_away_prob * w_odds +
            team_pred.away_win_prob * player_mods["away_modifier"] * w_player +
            odds_pred.market_away_prob / referee_mods["home_modifier"] * w_referee
        )
        
        ms_draw = 1.0 - ms_home - ms_away
        
        # XGBoost Integration
        if "ms" in ctx.xgboost_preds:
            xgb_probs = ctx.xgboost_preds["ms"]
            w_xgb = self.config.get("xgboost.weight_ms", 0.70)
            w_heuristic = 1.0 - w_xgb
            
            ms_home = ms_home * w_heuristic + xgb_probs["home"] * w_xgb
            ms_draw = ms_draw * w_heuristic + xgb_probs["draw"] * w_xgb
            ms_away = ms_away * w_heuristic + xgb_probs["away"] * w_xgb
            
            # Re-normalize
            total = ms_home + ms_draw + ms_away
            ms_home /= total
            ms_draw /= total
            ms_away /= total
        
        # Min draw probability clamping
        min_draw = self.config.get("match_result.min_draw_prob", 0.15)
        if ms_draw < min_draw:
            ms_draw = min_draw
            total = ms_home + ms_away + ms_draw
            ms_home /= total
            ms_away /= total
            ms_draw /= total
            
        # Double Chance
        dc_1x = ms_home + ms_draw
        dc_x2 = ms_draw + ms_away
        dc_12 = ms_home + ms_away
        
        # MS pick
        ms_probs = [(ms_home, "1"), (ms_draw, "X"), (ms_away, "2")]
        ms_sorted = sorted(ms_probs, key=lambda x: x[0], reverse=True)
        ms_pick = ms_sorted[0][1]
        
        # === ENGINE AGREEMENT ===
        # Determine each engine's winner and calculate agreement ratio
        team_winner = self._get_engine_winner(
            team_pred.home_win_prob, team_pred.draw_prob, team_pred.away_win_prob
        )
        odds_winner = self._get_engine_winner(
            odds_pred.market_home_prob, odds_pred.market_draw_prob, odds_pred.market_away_prob
        )
        
        # Player-modified: team probs * player modifiers
        player_adj_home = team_pred.home_win_prob * player_mods["home_modifier"]
        player_adj_away = team_pred.away_win_prob * player_mods["away_modifier"]
        player_adj_draw = max(0.01, 1.0 - player_adj_home - player_adj_away)
        player_winner = self._get_engine_winner(player_adj_home, player_adj_draw, player_adj_away)
        
        # Referee-modified: odds probs * referee modifiers
        ref_adj_home = odds_pred.market_home_prob * referee_mods["home_modifier"]
        ref_adj_away = odds_pred.market_away_prob / referee_mods["home_modifier"]
        ref_adj_draw = max(0.01, 1.0 - ref_adj_home - ref_adj_away)
        referee_winner = self._get_engine_winner(ref_adj_home, ref_adj_draw, ref_adj_away)
        
        # Count how many engines agree with final pick
        engines = [team_winner, odds_winner, player_winner, referee_winner]
        agreement_count = sum(1 for e in engines if e == ms_pick)
        agreement_ratio = agreement_count / len(engines)
        
        # Confidence with agreement
        boost = self.config.get("confidence.agreement_boost", 1.3)
        penalty = self.config.get("confidence.disagreement_penalty", 0.7)
        ms_confidence = calc_confidence_3way_with_agreement(
            ms_sorted[0][0], agreement_ratio, boost, penalty
        )
        
        # DC pick
        dc_probs = [(dc_1x, "1X"), (dc_x2, "X2"), (dc_12, "12")]
        dc_sorted = sorted(dc_probs, key=lambda x: x[0], reverse=True)
        dc_pick = dc_sorted[0][1]
        dc_confidence = calc_confidence_dc(dc_sorted[0][0])
        
        return MatchResultPrediction(
            ms_home_prob=ms_home,
            ms_draw_prob=ms_draw,
            ms_away_prob=ms_away,
            ms_pick=ms_pick,
            ms_confidence=ms_confidence,
            dc_1x_prob=dc_1x,
            dc_x2_prob=dc_x2,
            dc_12_prob=dc_12,
            dc_pick=dc_pick,
            dc_confidence=dc_confidence
        )
