"""
Odds Predictor Engine - V20 Ensemble Component
Uses market odds and Poisson mathematics for predictions.

Weight: 30% in ensemble
"""

import os
import sys
from typing import Dict, Optional
from dataclasses import dataclass

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

from features.poisson_engine import get_poisson_engine
from features.value_calculator import get_value_calculator


@dataclass
class OddsPrediction:
    """Odds engine prediction output."""
    # Market-implied probabilities
    market_home_prob: float = 0.33
    market_draw_prob: float = 0.33
    market_away_prob: float = 0.33
    
    # Poisson xG
    poisson_home_xg: float = 1.3
    poisson_away_xg: float = 1.1
    
    # Over/Under probabilities
    over_15_prob: float = 0.75
    over_25_prob: float = 0.55
    over_35_prob: float = 0.30
    
    # BTTS
    btts_yes_prob: float = 0.50
    
    # Most likely scores
    most_likely_score: str = "1-1"
    second_likely_score: str = "1-0"
    third_likely_score: str = "2-1"
    
    # Value bet opportunities
    value_bets: list = None
    
    confidence: float = 0.0
    
    def __post_init__(self):
        if self.value_bets is None:
            self.value_bets = []
    
    def to_dict(self) -> dict:
        return {
            "market_home_prob": round(self.market_home_prob * 100, 1),
            "market_draw_prob": round(self.market_draw_prob * 100, 1),
            "market_away_prob": round(self.market_away_prob * 100, 1),
            "poisson_home_xg": round(self.poisson_home_xg, 2),
            "poisson_away_xg": round(self.poisson_away_xg, 2),
            "over_15_prob": round(self.over_15_prob * 100, 1),
            "over_25_prob": round(self.over_25_prob * 100, 1),
            "over_35_prob": round(self.over_35_prob * 100, 1),
            "btts_yes_prob": round(self.btts_yes_prob * 100, 1),
            "most_likely_score": self.most_likely_score,
            "second_likely_score": self.second_likely_score,
            "third_likely_score": self.third_likely_score,
            "value_bets": self.value_bets,
            "confidence": round(self.confidence, 1)
        }


class OddsPredictorEngine:
    """
    Odds-based prediction engine.
    
    Uses:
    - Market odds to extract implied probabilities
    - Poisson distribution for mathematical xG
    - Value calculator for EV+ opportunities
    """
    
    def __init__(self):
        self.poisson_engine = get_poisson_engine()
        try:
            self.value_calc = get_value_calculator()
        except Exception:
            self.value_calc = None
        self.default_ms_h = 2.65
        self.default_ms_d = 3.20
        self.default_ms_a = 2.65
        print("✅ OddsPredictorEngine initialized")
    
    def _odds_to_prob(self, odds: float) -> float:
        """Convert decimal odds to probability."""
        try:
            odds = float(odds)
        except (TypeError, ValueError):
            return 0.0
        if odds <= 1.0:
            return 0.0
        return 1.0 / odds
    
    def predict(self,
                odds_data: Dict[str, float],
                home_goals_avg: float = 1.5,
                home_conceded_avg: float = 1.2,
                away_goals_avg: float = 1.2,
                away_conceded_avg: float = 1.4) -> OddsPrediction:
        """
        Generate odds-based prediction.
        
        Args:
            odds_data: Dict with keys like 'ms_h', 'ms_d', 'ms_a', 'ou25_o', 'btts_y'
            home_goals_avg: Home team's average goals scored
            home_conceded_avg: Home team's average goals conceded
            away_goals_avg: Away team's average goals scored
            away_conceded_avg: Away team's average goals conceded
            
        Returns:
            OddsPrediction with market and Poisson analysis
        """
        
        # 1. Extract market probabilities from odds
        ms_h = odds_data.get("ms_h", self.default_ms_h)
        ms_d = odds_data.get("ms_d", self.default_ms_d)
        ms_a = odds_data.get("ms_a", self.default_ms_a)
        
        # Remove vig to get fair probabilities
        raw_probs = [
            self._odds_to_prob(ms_h),
            self._odds_to_prob(ms_d),
            self._odds_to_prob(ms_a)
        ]
        total = sum(raw_probs) or 1
        
        market_home = raw_probs[0] / total
        market_draw = raw_probs[1] / total
        market_away = raw_probs[2] / total
        
        # 2. Poisson prediction
        poisson_pred = self.poisson_engine.predict(
            home_goals_avg, home_conceded_avg,
            away_goals_avg, away_conceded_avg
        )
        
        # 3. Get most likely scores
        likely_scores = poisson_pred.most_likely_scores[:3] if poisson_pred.most_likely_scores else []
        score_1 = likely_scores[0]["score"] if len(likely_scores) > 0 else "1-1"
        score_2 = likely_scores[1]["score"] if len(likely_scores) > 1 else "1-0"
        score_3 = likely_scores[2]["score"] if len(likely_scores) > 2 else "2-1"
        
        # 4. Value bet detection
        value_bets = []
        
        # Check if our Poisson model disagrees with market significantly
        if abs(poisson_pred.home_win_prob - market_home) > 0.10:
            if poisson_pred.home_win_prob > market_home:
                value_bets.append({
                    "market": "MS 1",
                    "edge": round((poisson_pred.home_win_prob - market_home) * 100, 1),
                    "confidence": "medium"
                })
            else:
                value_bets.append({
                    "market": "MS 2",
                    "edge": round((poisson_pred.away_win_prob - market_away) * 100, 1),
                    "confidence": "medium"
                })
        
        # O/U value check
        ou25_o = odds_data.get("ou25_o", 1.9)
        market_over25 = self._odds_to_prob(ou25_o)
        if abs(poisson_pred.over_25_prob - market_over25) > 0.08:
            pick = "2.5 Üst" if poisson_pred.over_25_prob > market_over25 else "2.5 Alt"
            edge = abs(poisson_pred.over_25_prob - market_over25) * 100
            value_bets.append({
                "market": pick,
                "edge": round(edge, 1),
                "confidence": "high" if edge > 10 else "medium"
            })
        
        # Calculate confidence
        # Higher when market and Poisson agree
        agreement = 1.0 - abs(poisson_pred.home_win_prob - market_home)
        confidence = 50.0 + (agreement * 40) + (len(value_bets) * 5)
        
        return OddsPrediction(
            market_home_prob=market_home,
            market_draw_prob=market_draw,
            market_away_prob=market_away,
            poisson_home_xg=poisson_pred.home_xg,
            poisson_away_xg=poisson_pred.away_xg,
            over_15_prob=poisson_pred.over_15_prob,
            over_25_prob=poisson_pred.over_25_prob,
            over_35_prob=poisson_pred.over_35_prob,
            btts_yes_prob=poisson_pred.btts_yes_prob,
            most_likely_score=score_1,
            second_likely_score=score_2,
            third_likely_score=score_3,
            value_bets=value_bets,
            confidence=min(99.9, confidence)
        )


# Singleton
_engine: Optional[OddsPredictorEngine] = None


def get_odds_predictor() -> OddsPredictorEngine:
    global _engine
    if _engine is None:
        _engine = OddsPredictorEngine()
    return _engine


if __name__ == "__main__":
    engine = get_odds_predictor()
    
    print("\n🧪 Odds Predictor Engine Test")
    print("=" * 50)
    
    pred = engine.predict(
        odds_data={
            "ms_h": 1.85,
            "ms_d": 3.40,
            "ms_a": 4.20,
            "ou25_o": 1.90
        },
        home_goals_avg=1.8,
        home_conceded_avg=1.0,
        away_goals_avg=1.2,
        away_conceded_avg=1.5
    )
    
    print(f"\n📊 Prediction:")
    for k, v in pred.to_dict().items():
        print(f"   {k}: {v}")
