"""Backtest agent"""
from sqlalchemy.orm import Session
from datetime import datetime, timedelta
import logging
import numpy as np
from app.agents.base import BaseAgent
from app.core.db.repositories.match_repository import MatchRepository
from app.core.db.repositories.model_repository import ModelRepository
from app.core.db.models.match import MatchStatus
from app.core.db.models.feature import Feature
from app.ml.models import MatchPredictor, prepare_target
from app.ml.features import FeatureBuilder
from sklearn.metrics import log_loss, brier_score_loss, accuracy_score

logger = logging.getLogger(__name__)


class BacktestAgent(BaseAgent):
    """Agent for backtesting models"""
    
    def __init__(self, db: Session):
        super().__init__(db, "BacktestAgent")
        self.match_repo = MatchRepository(db)
        self.model_repo = ModelRepository(db)
        self.feature_builder = FeatureBuilder(db)
    
    def run(self) -> bool:
        """Run backtest"""
        run_id = self.start_run()
        
        try:
            # Get latest model
            model_record = self.model_repo.get_latest()
            if not model_record:
                error_msg = "No active model found"
                logger.warning(error_msg)
                self.finish_run(success=False, error=error_msg)
                return False
            
            # Load model
            try:
                predictor = MatchPredictor.load(model_record.artifact_path)
            except Exception as e:
                error_msg = f"Failed to load model: {e}"
                logger.error(error_msg)
                self.finish_run(success=False, error=error_msg)
                return False
            
            # Get finished matches for backtesting (last 3 months)
            from_date = datetime.now() - timedelta(days=90)
            to_date = datetime.now() - timedelta(days=1)
            
            matches = self.match_repo.get_finished(
                from_date=from_date,
                to_date=to_date,
                limit=1000
            )
            
            logger.info(f"Backtesting on {len(matches)} matches")
            
            # Get features
            match_ids = [m.id for m in matches]
            features = self.db.query(Feature).filter(
                Feature.match_id.in_(match_ids)
            ).all()
            
            feature_dict = {f.match_id: f for f in features}
            
            # Prepare data
            X = []
            y = []
            predictions = []
            probabilities_list = []
            
            for match in matches:
                if match.id not in feature_dict:
                    continue
                if match.home_score is None or match.away_score is None:
                    continue
                
                feature = feature_dict[match.id]
                feature_vector = self.feature_builder.build_feature_vector(feature)
                X.append(feature_vector)
                y.append(prepare_target(match))
                
                # Predict
                X_single = np.array([feature_vector])
                proba = predictor.predict_proba(X_single)[0]
                probabilities_list.append(proba)
                predictions.append(np.argmax(proba))
            
            if len(X) == 0:
                error_msg = "No matches with features for backtesting"
                logger.warning(error_msg)
                self.finish_run(success=False, error=error_msg)
                return False
            
            X = np.array(X)
            y = np.array(y)
            predictions = np.array(predictions)
            probabilities = np.array(probabilities_list)
            
            # Calculate metrics
            accuracy = float(accuracy_score(y, predictions))
            logloss = float(log_loss(y, probabilities))
            brier = float(brier_score_loss(y == 0, probabilities[:, 0]))
            
            # Simple ROI simulation (for demonstration only)
            # This is NOT financial advice and should not be used for actual betting
            roi_simulation = self._simulate_roi(y, probabilities)
            
            results = {
                "accuracy": accuracy,
                "logloss": logloss,
                "brier": brier,
                "roi_simulation": roi_simulation,
                "matches_tested": len(y)
            }
            
            logger.info(f"Backtest results: {results}")
            
            # Store results in logs
            logs = f"Backtest Results:\nAccuracy: {accuracy:.4f}\nLogLoss: {logloss:.4f}\nBrier: {brier:.4f}\nROI Simulation: {roi_simulation:.2f}%"
            
            self.finish_run(success=True, logs=logs)
            return True
            
        except Exception as e:
            error_msg = str(e)
            logger.error(f"Backtest failed: {error_msg}", exc_info=True)
            self.finish_run(success=False, error=error_msg)
            return False
    
    def _simulate_roi(self, y_true: np.ndarray, y_proba: np.ndarray) -> float:
        """Simple ROI simulation (for demonstration only)"""
        # This is a simplified simulation - NOT for actual betting
        # Assumes equal odds of 3.0 for each outcome
        total_bet = 0.0
        total_return = 0.0
        
        for i in range(len(y_true)):
            # Bet on predicted outcome with confidence > 0.4
            predicted = np.argmax(y_proba[i])
            confidence = y_proba[i][predicted]
            
            if confidence > 0.4:
                bet_amount = 1.0
                total_bet += bet_amount
                
                # If correct, return 3.0 (odds 3.0)
                if predicted == y_true[i]:
                    total_return += bet_amount * 3.0
        
        if total_bet == 0:
            return 0.0
        
        roi = ((total_return - total_bet) / total_bet) * 100
        return roi

