# test_weather.py
#
# author: deng
# date: 20251129

from unittest.mock import Mock, patch

import pytest
import requests


class TestWeatherFetcher:
    """Test cases for WeatherFetcher class."""

    @pytest.fixture
    def weather_fetcher(self):
        """Create a WeatherFetcher instance."""
        from hiking_assistant.weather import WeatherFetcher

        return WeatherFetcher()

    @pytest.fixture
    def mock_weather_response(self):
        """Mock weather API response data."""
        return {
            'current': {
                'relative_humidity_2m': 75,
                'apparent_temperature': 18.5,
                'precipitation_probability': 30,
                'precipitation': 0.5,
                'wind_speed_10m': 15.2,
                'wind_direction_10m': 180,
            },
            'daily': {
                'temperature_2m_max': [22.0, 23.5, 21.0],
                'temperature_2m_min': [15.0, 16.0, 14.5],
                'apparent_temperature_max': [20.0, 21.5, 19.0],
                'apparent_temperature_min': [13.0, 14.0, 12.5],
                'relative_humidity_2m_mean': [70, 75, 68],
                'precipitation_sum': [0.0, 2.5, 0.0],
                'precipitation_probability_max': [20, 60, 15],
                'wind_speed_10m_max': [25.0, 35.0, 20.0],
                'wind_direction_10m_dominant': [180, 90, 270],
                'sunrise': ['2024-01-01T06:00:00', '2024-01-02T06:01:00', '2024-01-03T06:02:00'],
                'sunset': ['2024-01-01T18:00:00', '2024-01-02T18:01:00', '2024-01-03T18:02:00'],
                'uv_index_max': [5.0, 7.0, 3.0],
                'time': ['2024-01-01', '2024-01-02', '2024-01-03'],
            },
        }

    def test_init_default_values(self):
        """Test WeatherFetcher initialization with defaults."""
        from hiking_assistant.weather import WeatherFetcher

        fetcher = WeatherFetcher()

        assert fetcher.api_url == 'https://api.open-meteo.com/v1/forecast'
        assert fetcher.request_timeout == 8
        assert fetcher.forecast_days == 7

    def test_init_custom_values(self):
        """Test WeatherFetcher initialization with custom values."""
        from hiking_assistant.weather import WeatherFetcher

        fetcher = WeatherFetcher(api_url='https://custom.api.com', request_timeout=10, forecast_days=3)

        assert fetcher.api_url == 'https://custom.api.com'
        assert fetcher.request_timeout == 10
        assert fetcher.forecast_days == 3

    def test_convert_wind_degrees_to_flow_direction_north(self, weather_fetcher):
        """Test wind direction conversion for north."""
        result = weather_fetcher.convert_wind_degrees_to_flow_direction(0)
        assert result in ['⬇️', '⬆️']  # 0 and 360 degrees

    def test_convert_wind_degrees_to_flow_direction_east(self, weather_fetcher):
        """Test wind direction conversion for east."""
        result = weather_fetcher.convert_wind_degrees_to_flow_direction(90)
        assert result == '⬅️'  # 90 degrees = east wind blowing west

    def test_convert_wind_degrees_to_flow_direction_south(self, weather_fetcher):
        """Test wind direction conversion for south."""
        result = weather_fetcher.convert_wind_degrees_to_flow_direction(180)
        assert result == '⬆️'

    def test_convert_wind_degrees_to_flow_direction_west(self, weather_fetcher):
        """Test wind direction conversion for west."""
        result = weather_fetcher.convert_wind_degrees_to_flow_direction(270)
        assert result == '➡️'  # 270 degrees = west wind blowing east

    def test_convert_wind_degrees_to_flow_direction_none(self, weather_fetcher):
        """Test wind direction conversion with None input."""
        result = weather_fetcher.convert_wind_degrees_to_flow_direction(None)
        assert result == 'N/A'

    def test_get_wind_speed_indicator_safe(self, weather_fetcher):
        """Test wind speed indicator for safe level."""
        result = weather_fetcher.get_wind_speed_indicator(15)
        assert result == '🟢'

    def test_get_wind_speed_indicator_caution(self, weather_fetcher):
        """Test wind speed indicator for caution level."""
        result = weather_fetcher.get_wind_speed_indicator(30)
        assert result == '🟡'

    def test_get_wind_speed_indicator_alert(self, weather_fetcher):
        """Test wind speed indicator for alert level."""
        result = weather_fetcher.get_wind_speed_indicator(50)
        assert result == '🟠'

    def test_get_wind_speed_indicator_dangerous(self, weather_fetcher):
        """Test wind speed indicator for dangerous level."""
        result = weather_fetcher.get_wind_speed_indicator(65)
        assert result == '🔴'

    def test_get_wind_speed_indicator_none(self, weather_fetcher):
        """Test wind speed indicator with None input."""
        result = weather_fetcher.get_wind_speed_indicator(None)
        assert result == ''

    def test_get_uv_index_indicator_low(self, weather_fetcher):
        """Test UV index indicator for low level."""
        result = weather_fetcher.get_uv_index_indicator(2)
        assert result == '🟢'

    def test_get_uv_index_indicator_moderate(self, weather_fetcher):
        """Test UV index indicator for moderate level."""
        result = weather_fetcher.get_uv_index_indicator(4)
        assert result == '🟡'

    def test_get_uv_index_indicator_high(self, weather_fetcher):
        """Test UV index indicator for high level."""
        result = weather_fetcher.get_uv_index_indicator(6)
        assert result == '🟠'

    def test_get_uv_index_indicator_very_high(self, weather_fetcher):
        """Test UV index indicator for very high level."""
        result = weather_fetcher.get_uv_index_indicator(9)
        assert result == '🔴'

    def test_get_uv_index_indicator_extreme(self, weather_fetcher):
        """Test UV index indicator for extreme level."""
        result = weather_fetcher.get_uv_index_indicator(11)
        assert result == '🟣'

    def test_get_uv_index_indicator_none(self, weather_fetcher):
        """Test UV index indicator with None input."""
        result = weather_fetcher.get_uv_index_indicator(None)
        assert result == ''

    @patch('hiking_assistant.weather.requests.get')
    def test_get_weather_success(self, mock_get, weather_fetcher, mock_weather_response):
        """Test successful weather data retrieval."""
        mock_response = Mock()
        mock_response.json.return_value = mock_weather_response
        mock_get.return_value = mock_response

        result = weather_fetcher.get_weather(25.0, 121.0)

        assert result is not None
        assert 'current_humidity' in result
        assert 'daily_temp_max' in result
        assert result['current_humidity'] == 75
        assert len(result['daily_temp_max']) == 3

    @patch('hiking_assistant.weather.requests.get')
    def test_get_weather_request_exception(self, mock_get, weather_fetcher):
        """Test weather retrieval with request exception."""
        mock_get.side_effect = requests.exceptions.RequestException('Connection error')

        result = weather_fetcher.get_weather(25.0, 121.0)

        assert result is None

    @patch('hiking_assistant.weather.requests.get')
    def test_get_weather_json_parsing_error(self, mock_get, weather_fetcher):
        """Test weather retrieval with JSON parsing error."""
        mock_response = Mock()
        mock_response.json.side_effect = ValueError('Invalid JSON')
        mock_get.return_value = mock_response

        result = weather_fetcher.get_weather(25.0, 121.0)

        assert result is None

    @patch('hiking_assistant.weather.requests.get')
    def test_get_weather_api_parameters(self, mock_get, weather_fetcher, mock_weather_response):
        """Test that correct API parameters are sent."""
        mock_response = Mock()
        mock_response.json.return_value = mock_weather_response
        mock_get.return_value = mock_response

        weather_fetcher.get_weather(25.123, 121.456)

        # Verify the call was made with correct parameters
        mock_get.assert_called_once()
        call_args = mock_get.call_args
        params = call_args[1]['params']

        assert params['latitude'] == 25.123
        assert params['longitude'] == 121.456
        assert params['timezone'] == 'auto'
        assert params['forecast_days'] == 7

    @patch('hiking_assistant.weather.requests.get')
    def test_get_weather_timeout_parameter(self, mock_get, weather_fetcher, mock_weather_response):
        """Test that timeout parameter is used."""
        mock_response = Mock()
        mock_response.json.return_value = mock_weather_response
        mock_get.return_value = mock_response

        weather_fetcher.get_weather(25.0, 121.0)

        # Verify timeout was passed
        call_args = mock_get.call_args
        assert call_args[1]['timeout'] == 8


class TestWeatherFetcherEdgeCases:
    """Test edge cases for WeatherFetcher."""

    def test_forecast_days_minimum_is_one(self):
        """Test that forecast_days is at least 1."""
        from hiking_assistant.weather import WeatherFetcher

        fetcher = WeatherFetcher(forecast_days=0)
        assert fetcher.forecast_days == 1

        fetcher = WeatherFetcher(forecast_days=-5)
        assert fetcher.forecast_days == 1

    def test_wind_direction_boundary_values(self):
        """Test wind direction conversion at boundary values."""
        from hiking_assistant.weather import WeatherFetcher

        fetcher = WeatherFetcher()

        # Test 360 degrees (same as 0)
        result_0 = fetcher.convert_wind_degrees_to_flow_direction(0)
        result_360 = fetcher.convert_wind_degrees_to_flow_direction(360)
        assert result_0 == result_360

    def test_wind_speed_boundary_values(self):
        """Test wind speed indicators at exact boundary values."""
        from hiking_assistant.weather import WeatherFetcher

        fetcher = WeatherFetcher()

        assert fetcher.get_wind_speed_indicator(20) == '🟡'  # Exactly 20
        assert fetcher.get_wind_speed_indicator(40) == '🟠'  # Exactly 40
        assert fetcher.get_wind_speed_indicator(60) == '🔴'  # Exactly 60

    def test_uv_index_boundary_values(self):
        """Test UV index indicators at exact boundary values."""
        from hiking_assistant.weather import WeatherFetcher

        fetcher = WeatherFetcher()

        assert fetcher.get_uv_index_indicator(2) == '🟢'  # Exactly 2
        assert fetcher.get_uv_index_indicator(5) == '🟡'  # Exactly 5
        assert fetcher.get_uv_index_indicator(7) == '🟠'  # Exactly 7
        assert fetcher.get_uv_index_indicator(10) == '🔴'  # Exactly 10
