"""Unit tests for version checking functionality.

Tests cover:
- Version check with outdated version
- Version check with current version
- Version check with network failures
- Version check timeout handling
- Graceful failure handling
"""

import pytest
from unittest.mock import Mock, patch
from datetime import datetime, timezone
from mca_sdk.utils.version_check import check_version


class TestVersionCheck:
    """Test version checking functionality."""

    def test_version_check_outdated_warning(self):
        """Test warning is logged when version is outdated (>30 days)."""
        # Mock PyPI response for outdated version
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "info": {"version": "1.0.1"},
            "releases": {"1.0.1": [{"upload_time": "2025-11-01T10:00:00"}]},  # >30 days ago
        }

        with patch("requests.get", return_value=mock_response):
            with patch("logging.Logger.warning") as mock_warning:
                result = check_version("0.3.0", timeout=2.0)

                # Verify result contains expected data
                assert result is not None
                assert result["latest_version"] == "1.0.1"
                assert result["current_version"] == "0.3.0"
                assert result["is_outdated"] is True
                assert result["age_days"] > 30

                # Verify warning was logged
                mock_warning.assert_called_once()
                warning_msg = str(mock_warning.call_args)
                assert "outdated" in warning_msg.lower()
                assert "1.0.1" in warning_msg
                assert "pip install --upgrade" in warning_msg

    def test_version_check_current_no_warning(self):
        """Test no warning when version is current."""
        # Mock PyPI response for current version
        mock_response = Mock()
        mock_response.status_code = 200
        current_time = datetime.now(timezone.utc).isoformat()
        mock_response.json.return_value = {
            "info": {"version": "0.3.0"},
            "releases": {"0.3.0": [{"upload_time": current_time}]},  # Recent release
        }

        with patch("requests.get", return_value=mock_response):
            with patch("logging.Logger.warning") as mock_warning:
                result = check_version("0.3.0", timeout=2.0)

                # Verify result
                assert result is not None
                assert result["latest_version"] == "0.3.0"
                assert result["current_version"] == "0.3.0"
                assert result["is_outdated"] is False

                # Verify no warning was logged
                mock_warning.assert_not_called()

    def test_version_check_network_timeout(self):
        """Test version check handles timeout gracefully."""
        import requests

        with patch("requests.get", side_effect=requests.exceptions.Timeout):
            with patch("logging.Logger.debug") as mock_debug:
                result = check_version("0.3.0", timeout=2.0)

                # Verify returns None on timeout
                assert result is None

                # Verify debug message logged
                mock_debug.assert_called()
                debug_msg = str(mock_debug.call_args_list)
                assert (
                    "timed out" in debug_msg.lower() or "network unavailable" in debug_msg.lower()
                )

    def test_version_check_network_error(self):
        """Test version check handles network errors gracefully."""
        import requests

        with patch("requests.get", side_effect=requests.exceptions.ConnectionError):
            with patch("logging.Logger.debug") as mock_debug:
                result = check_version("0.3.0", timeout=2.0)

                # Verify returns None on network error
                assert result is None

                # Verify debug message logged (not error/warning)
                mock_debug.assert_called()

    def test_version_check_http_error(self):
        """Test version check handles HTTP errors gracefully."""
        mock_response = Mock()
        mock_response.status_code = 404

        with patch("requests.get", return_value=mock_response):
            with patch("logging.Logger.debug") as mock_debug:
                result = check_version("0.3.0", timeout=2.0)

                # Verify returns None on HTTP error
                assert result is None

                # Verify debug message logged
                mock_debug.assert_called()
                debug_msg = str(mock_debug.call_args_list)
                assert "404" in debug_msg

    def test_version_check_malformed_response(self):
        """Test version check handles malformed PyPI responses."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "info": {"version": "1.0.1"},
            "releases": {},  # Missing release data
        }

        with patch("requests.get", return_value=mock_response):
            with patch("logging.Logger.debug") as mock_debug:
                result = check_version("0.3.0", timeout=2.0)

                # Verify returns None on malformed data
                assert result is None

                # Verify debug message logged
                mock_debug.assert_called()

    def test_version_check_respects_timeout(self):
        """Test version check respects timeout parameter."""
        import requests

        with patch("requests.get") as mock_get:
            mock_get.side_effect = requests.exceptions.Timeout

            result = check_version("0.3.0", timeout=0.5)

            # Verify timeout was passed to requests
            assert result is None
            mock_get.assert_called_once()
            call_kwargs = mock_get.call_args[1]
            assert call_kwargs["timeout"] == 0.5

    def test_version_check_never_raises_exception(self):
        """Test version check never raises exceptions (fails gracefully)."""
        # Test with various exception types
        exceptions = [
            Exception("Unexpected error"),
            ValueError("Invalid data"),
            KeyError("Missing key"),
        ]

        for exc in exceptions:
            with patch("requests.get", side_effect=exc):
                # Should not raise - should return None
                result = check_version("0.3.0")
                assert result is None

    def test_version_check_old_version_recent_release(self):
        """Test old version with recent release doesn't trigger warning."""
        # If current version != latest but release is recent (<30 days), no warning
        mock_response = Mock()
        mock_response.status_code = 200
        recent_time = datetime.now(timezone.utc).isoformat()
        mock_response.json.return_value = {
            "info": {"version": "1.0.1"},
            "releases": {"1.0.1": [{"upload_time": recent_time}]},  # Recent release
        }

        with patch("requests.get", return_value=mock_response):
            with patch("logging.Logger.warning") as mock_warning:
                result = check_version("0.3.0", timeout=2.0)

                # Verify result shows outdated but recent
                assert result is not None
                assert result["latest_version"] == "1.0.1"
                assert result["current_version"] == "0.3.0"
                assert result["age_days"] < 30
                # is_outdated should be False because age < 30 days
                assert result["is_outdated"] is False

                # Verify no warning (version is different but release is recent)
                mock_warning.assert_not_called()

    def test_check_version_when_requests_not_available(self):
        """Test check_version handles missing requests library."""
        original_import = __import__

        def mock_import(name, *args, **kwargs):
            if name == "requests":
                raise ImportError("No module named 'requests'")
            return original_import(name, *args, **kwargs)

        with patch("builtins.__import__", side_effect=mock_import):
            with patch("mca_sdk.utils.version_check.logger") as mock_logger:
                result = check_version("1.0.0")

                # Should return None
                assert result is None

                # Should log debug message
                mock_logger.debug.assert_called_once()
                assert "requests library not available" in str(mock_logger.debug.call_args)

    def test_check_version_no_upload_time_in_release(self):
        """Test check_version handles missing upload_time in release data."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "info": {"version": "1.0.1"},
            "releases": {
                "1.0.1": [
                    {
                        # Missing upload_time field
                        "filename": "mca_sdk-1.0.1.tar.gz"
                    }
                ]
            },
        }

        with patch("requests.get", return_value=mock_response):
            with patch("mca_sdk.utils.version_check.logger") as mock_logger:
                result = check_version("1.0.0")

                # Should return None
                assert result is None

                # Should log debug message
                mock_logger.debug.assert_called()
                assert "No upload time available" in str(mock_logger.debug.call_args)
