# ================================================================
# services/forecasting/tests/test_forecasting.py
# ================================================================
"""
Tests for forecasting service
"""

import pytest
import asyncio
from datetime import date, datetime, timedelta
from unittest.mock import Mock, AsyncMock, patch
import uuid

from app.services.forecasting_service import ForecastingService
from app.schemas.forecasts import ForecastRequest, BusinessType
from app.models.forecasts import Forecast


class TestForecastingService:
    """Test cases for ForecastingService"""
    
    @pytest.fixture
    def forecasting_service(self):
        return ForecastingService()
    
    @pytest.fixture
    def sample_forecast_request(self):
        return ForecastRequest(
            tenant_id=str(uuid.uuid4()),
            product_name="Pan Integral",
            location="madrid_centro",
            forecast_date=date.today() + timedelta(days=1),
            business_type=BusinessType.INDIVIDUAL,
            include_weather=True,
            include_traffic=True,
            confidence_level=0.8
        )
    
    @pytest.mark.asyncio
    async def test_generate_forecast_success(self, forecasting_service, sample_forecast_request):
        """Test successful forecast generation"""
        
        # Mock database session
        mock_db = AsyncMock()
        
        # Mock external dependencies
        with patch.object(forecasting_service, '_get_latest_model') as mock_get_model, \
             patch.object(forecasting_service, '_prepare_forecast_features') as mock_prepare_features, \
             patch.object(forecasting_service.prediction_service, 'predict') as mock_predict, \
             patch.object(forecasting_service, '_check_and_create_alerts') as mock_check_alerts:
            
            # Setup mocks
            mock_get_model.return_value = {
                "model_id": str(uuid.uuid4()),
                "version": "1.0.0",
                "algorithm": "prophet"
            }
            
            mock_prepare_features.return_value = {
                "date": "2024-01-16",
                "day_of_week": 1,
                "is_weekend": False,
                "is_holiday": False,
                "temperature": 15.0,
                "precipitation": 0.0
            }
            
            mock_predict.return_value = {
                "demand": 85.5,
                "lower_bound": 70.2,
                "upper_bound": 100.8
            }
            
            # Execute test
            result = await forecasting_service.generate_forecast(sample_forecast_request, mock_db)
            
            # Assertions
            assert isinstance(result, Forecast)
            assert result.product_name == "Pan Integral"
            assert result.predicted_demand == 85.5
            assert result.confidence_lower == 70.2
            assert result.confidence_upper == 100.8
            
            # Verify mocks were called
            mock_get_model.assert_called_once()
            mock_prepare_features.assert_called_once()
            mock_predict.assert_called_once()
            mock_check_alerts.assert_called_once()
    
    @pytest.mark.asyncio
    async def test_generate_forecast_no_model(self, forecasting_service, sample_forecast_request):
        """Test forecast generation when no model is found"""
        
        mock_db = AsyncMock()
        
        with patch.object(forecasting_service, '_get_latest_model') as mock_get_model:
            mock_get_model.return_value = None
            
            # Should raise ValueError
            with pytest.raises(ValueError, match="No trained model found"):
                await forecasting_service.generate_forecast(sample_forecast_request, mock_db)
    
    @pytest.mark.asyncio
    async def test_prepare_forecast_features(self, forecasting_service, sample_forecast_request):
        """Test feature preparation for forecasting"""
        
        with patch.object(forecasting_service, '_is_spanish_holiday') as mock_holiday, \
             patch.object(forecasting_service, '_get_weather_forecast') as mock_weather, \
             patch.object(forecasting_service, '_get_traffic_forecast') as mock_traffic:
            
            # Setup mocks
            mock_holiday.return_value = False
            mock_weather.return_value = {
                "temperature": 18.5,
                "precipitation": 0.0,
                "humidity": 65.0,
                "weather_description": "Clear"
            }
            mock_traffic.return_value = {
                "traffic_volume": 1200,
                "pedestrian_count": 850
            }
            
            # Execute test
            features = await forecasting_service._prepare_forecast_features(sample_forecast_request)
            
            # Assertions
            assert "date" in features
            assert "day_of_week" in features
            assert "is_weekend" in features
            assert "is_holiday" in features
            assert features["business_type"] == "individual"
            assert features["temperature"] == 18.5
            assert features["traffic_volume"] == 1200

