"""
Unit tests for Prediction Service.
"""

import pytest
from datetime import datetime, timezone
from sqlalchemy.orm import Session

from app.database import Base, engine, SessionLocal
from app.models.match import Match
from app.models.prediction import Prediction
from app.services.prediction_service import PredictionService


@pytest.fixture(scope="function")
def db_session():
    """Create a fresh database session for each test."""
    Base.metadata.create_all(bind=engine)
    session = SessionLocal()
    try:
        yield session
        session.rollback()
    finally:
        session.close()
        Base.metadata.drop_all(bind=engine)


@pytest.fixture
def sample_match(db_session: Session) -> Match:
    """Create a sample match for testing."""
    match = Match(
        home_team="PSG",
        away_team="Olympique de Marseille",
        date=datetime.now(timezone.utc),
        league="Ligue 1",
        status="scheduled"
    )
    db_session.add(match)
    db_session.commit()
    db_session.refresh(match)
    return match


class TestPredictionServiceCreate:
    """Test prediction service creation methods."""
    
    def test_create_prediction_home_win(self, db_session: Session, sample_match: Match):
        """Test creating a prediction where home team wins."""
        service = PredictionService(db_session)
        
        prediction = service.create_prediction_for_match(
            match_id=sample_match.id,
            home_energy=65.0,
            away_energy=45.0,
            energy_score_label="high"
        )
        
        assert prediction.id is not None
        assert prediction.match_id == sample_match.id
        assert prediction.predicted_winner == "PSG"
        assert prediction.energy_score == "high"
        assert "%" in prediction.confidence
    
    def test_create_prediction_away_win(self, db_session: Session, sample_match: Match):
        """Test creating a prediction where away team wins."""
        service = PredictionService(db_session)
        
        prediction = service.create_prediction_for_match(
            match_id=sample_match.id,
            home_energy=35.0,
            away_energy=70.0
        )
        
        assert prediction.predicted_winner == "Olympique de Marseille"
        assert prediction.id is not None
    
    def test_create_prediction_draw(self, db_session: Session, sample_match: Match):
        """Test creating a prediction for draw."""
        service = PredictionService(db_session)
        
        prediction = service.create_prediction_for_match(
            match_id=sample_match.id,
            home_energy=50.0,
            away_energy=50.0
        )
        
        assert prediction.predicted_winner == "Draw"
        assert prediction.confidence == "0.0%"
    
    def test_create_prediction_with_default_energy_label(self, db_session: Session, sample_match: Match):
        """Test creating prediction without energy score label."""
        service = PredictionService(db_session)
        
        # High energy prediction
        prediction1 = service.create_prediction_for_match(
            match_id=sample_match.id,
            home_energy=80.0,
            away_energy=70.0
        )
        assert prediction1.energy_score in ["high", "very_high"]
        
        # Low energy prediction
        prediction2 = service.create_prediction_for_match(
            match_id=sample_match.id,
            home_energy=20.0,
            away_energy=10.0
        )
        assert prediction2.energy_score == "low"
    
    def test_create_prediction_nonexistent_match(self, db_session: Session):
        """Test creating prediction for non-existent match raises error."""
        service = PredictionService(db_session)
        
        with pytest.raises(ValueError, match="Match with id 999 not found"):
            service.create_prediction_for_match(
                match_id=999,
                home_energy=65.0,
                away_energy=45.0
            )
    
    def test_create_prediction_negative_energy(self, db_session: Session, sample_match: Match):
        """Test creating prediction with negative energy raises error."""
        service = PredictionService(db_session)
        
        with pytest.raises(ValueError, match="cannot be negative"):
            service.create_prediction_for_match(
                match_id=sample_match.id,
                home_energy=-10.0,
                away_energy=45.0
            )
    
    def test_create_prediction_invalid_energy_type(self, db_session: Session, sample_match: Match):
        """Test creating prediction with invalid energy type raises error."""
        service = PredictionService(db_session)
        
        with pytest.raises(ValueError, match="must be numeric"):
            service.create_prediction_for_match(
                match_id=sample_match.id,
                home_energy="invalid",
                away_energy=45.0
            )


class TestPredictionServiceRetrieve:
    """Test prediction service retrieval methods."""
    
    def test_get_prediction_by_id(self, db_session: Session, sample_match: Match):
        """Test retrieving a prediction by ID."""
        service = PredictionService(db_session)
        
        created = service.create_prediction_for_match(
            match_id=sample_match.id,
            home_energy=65.0,
            away_energy=45.0
        )
        
        retrieved = service.get_prediction_by_id(created.id)
        
        assert retrieved is not None
        assert retrieved.id == created.id
        assert retrieved.match_id == created.match_id
    
    def test_get_prediction_by_id_not_found(self, db_session: Session):
        """Test retrieving non-existent prediction returns None."""
        service = PredictionService(db_session)
        
        retrieved = service.get_prediction_by_id(999)
        
        assert retrieved is None
    
    def test_get_predictions_for_match(self, db_session: Session, sample_match: Match):
        """Test retrieving all predictions for a match."""
        service = PredictionService(db_session)
        
        # Create multiple predictions
        for i in range(3):
            service.create_prediction_for_match(
                match_id=sample_match.id,
                home_energy=50.0 + i * 10,
                away_energy=40.0 + i * 5
            )
        
        predictions = service.get_predictions_for_match(sample_match.id)
        
        assert len(predictions) == 3
        assert all(p.match_id == sample_match.id for p in predictions)
    
    def test_get_predictions_for_empty_match(self, db_session: Session, sample_match: Match):
        """Test retrieving predictions for match with no predictions."""
        service = PredictionService(db_session)
        
        predictions = service.get_predictions_for_match(sample_match.id)
        
        assert len(predictions) == 0
    
    def test_get_latest_prediction_for_match(self, db_session: Session, sample_match: Match):
        """Test retrieving latest prediction for a match."""
        service = PredictionService(db_session)
        
        # Create predictions at different times
        prediction1 = service.create_prediction_for_match(
            match_id=sample_match.id,
            home_energy=50.0,
            away_energy=40.0
        )
        
        # Small delay to ensure different timestamps
        import time
        time.sleep(0.01)
        
        prediction2 = service.create_prediction_for_match(
            match_id=sample_match.id,
            home_energy=60.0,
            away_energy=35.0
        )
        
        latest = service.get_latest_prediction_for_match(sample_match.id)
        
        assert latest.id == prediction2.id
        assert latest.id != prediction1.id
    
    def test_get_latest_prediction_for_empty_match(self, db_session: Session, sample_match: Match):
        """Test retrieving latest prediction for match with no predictions."""
        service = PredictionService(db_session)
        
        latest = service.get_latest_prediction_for_match(sample_match.id)
        
        assert latest is None


class TestPredictionServiceDelete:
    """Test prediction service delete methods."""
    
    def test_delete_prediction(self, db_session: Session, sample_match: Match):
        """Test deleting a prediction."""
        service = PredictionService(db_session)
        
        prediction = service.create_prediction_for_match(
            match_id=sample_match.id,
            home_energy=65.0,
            away_energy=45.0
        )
        
        deleted = service.delete_prediction(prediction.id)
        
        assert deleted is True
        
        # Verify prediction is gone
        retrieved = service.get_prediction_by_id(prediction.id)
        assert retrieved is None
    
    def test_delete_prediction_not_found(self, db_session: Session):
        """Test deleting non-existent prediction."""
        service = PredictionService(db_session)
        
        deleted = service.delete_prediction(999)
        
        assert deleted is False
