"""
Integration tests for GoogleTranslationProvider.

Tests for error handling, retry logic, and health checks.
Uses mocking to simulate various API error scenarios.
"""

import pytest
from unittest.mock import patch, MagicMock
import time

from services.providers.google_provider import (
    GoogleTranslationProvider,
    GoogleProviderError,
    GOOGLE_QUOTA_EXCEEDED,
    GOOGLE_INVALID_KEY,
    GOOGLE_NETWORK_ERROR,
    GOOGLE_UNSUPPORTED_LANGUAGE,
    GOOGLE_TEXT_TOO_LONG,
)
from services.providers.schemas import TranslationRequest


class TestGoogleProviderHealthCheck:
    """Tests for health check functionality."""

    @pytest.fixture
    def provider(self):
        return GoogleTranslationProvider(use_cache=False)

    def test_health_check_returns_status(self, provider):
        """Test health check returns ProviderHealthStatus."""
        status = provider.health_check()

        assert status.name == "google"
        assert isinstance(status.available, bool)
        assert status.latency_ms is not None

    def test_health_check_includes_last_check_timestamp(self, provider):
        """Test health check includes last_check timestamp."""
        status = provider.health_check()

        assert status.last_check is not None
        assert "T" in status.last_check  # ISO format

    def test_health_check_caches_result(self, provider):
        """Test health check result is cached for 60 seconds."""
        status1 = provider.health_check()
        status2 = provider.health_check()

        assert status1.last_check == status2.last_check

    def test_health_check_cache_ttl(self, provider):
        """Test health check cache expires after TTL."""
        provider._health_cache_ttl = 0.1  # 100ms TTL for testing

        status1 = provider.health_check()
        time.sleep(0.15)
        status2 = provider.health_check()

        assert status1.last_check != status2.last_check


class TestGoogleProviderErrorCodes:
    """Tests for specific Google error codes."""

    @pytest.fixture
    def provider(self):
        return GoogleTranslationProvider(use_cache=False)

    def test_quota_exceeded_error(self, provider):
        """Test GOOGLE_QUOTA_EXCEEDED error on 429 response."""
        request = TranslationRequest(text="Hello", target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_QUOTA_EXCEEDED,
                message="Quota Google Translate dépassé. Réessayez demain.",
                details={"reset_at": "2024-01-16T00:00:00Z"},
            )

            response = provider.translate_text(request)

            assert response.error is not None
            assert response.error_code == GOOGLE_QUOTA_EXCEEDED
            assert (
                "quota" in response.error.lower() or "dépassé" in response.error.lower()
            )

    def test_invalid_key_error(self, provider):
        """Test GOOGLE_INVALID_KEY error on 401 response."""
        request = TranslationRequest(text="Hello", target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_INVALID_KEY,
                message="Clé API Google invalide. Contactez l'administrateur.",
            )

            response = provider.translate_text(request)

            assert response.error is not None
            assert response.error_code == GOOGLE_INVALID_KEY

    def test_network_error(self, provider):
        """Test GOOGLE_NETWORK_ERROR on timeout/connection error."""
        request = TranslationRequest(text="Hello", target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_NETWORK_ERROR,
                message="Service Google Translate indisponible. Réessayez.",
            )

            response = provider.translate_text(request)

            assert response.error is not None
            assert response.error_code == GOOGLE_NETWORK_ERROR

    def test_unsupported_language_error(self, provider):
        """Test GOOGLE_UNSUPPORTED_LANGUAGE for invalid language."""
        request = TranslationRequest(text="Hello", target_language="xx")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_UNSUPPORTED_LANGUAGE,
                message="Langue 'xx' non supportée par Google.",
                details={"unsupported_language": "xx"},
            )

            response = provider.translate_text(request)

            assert response.error is not None
            assert response.error_code == GOOGLE_UNSUPPORTED_LANGUAGE

    def test_text_too_long_error(self, provider):
        """Test GOOGLE_TEXT_TOO_LONG for text exceeding limit."""
        long_text = "x" * 5001
        request = TranslationRequest(text=long_text, target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_TEXT_TOO_LONG,
                message="Texte trop long (max 5000 caractères par requête).",
                details={"text_length": 5001, "max_length": 5000},
            )

            response = provider.translate_text(request)

            assert response.error is not None
            assert response.error_code == GOOGLE_TEXT_TOO_LONG


class TestGoogleProviderRetryLogic:
    """Tests for retry logic with exponential backoff."""

    @pytest.fixture
    def provider(self):
        return GoogleTranslationProvider(use_cache=False)

    def test_retry_on_transient_error(self, provider):
        """Test that transient errors trigger retry."""
        request = TranslationRequest(text="Hello", target_language="fr")

        call_count = 0

        def mock_api_call(*args, **kwargs):
            nonlocal call_count
            call_count += 1
            if call_count < 3:
                raise GoogleProviderError(
                    code=GOOGLE_NETWORK_ERROR, message="Temporary network error"
                )
            return "Bonjour"

        with patch.object(provider, "_make_api_request", side_effect=mock_api_call):
            response = provider.translate_text(request)

            assert call_count == 3
            assert response.translated_text == "Bonjour"
            assert response.error is None

    def test_max_retries_exceeded(self, provider):
        """Test that max retries is respected."""
        request = TranslationRequest(text="Hello", target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_NETWORK_ERROR, message="Network error"
            )

            response = provider.translate_text(request)

            assert response.error is not None
            assert mock_api.call_count == 4  # Initial + 3 retries

    def test_no_retry_on_invalid_key(self, provider):
        """Test that invalid key errors don't retry."""
        request = TranslationRequest(text="Hello", target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_INVALID_KEY, message="Invalid key"
            )

            response = provider.translate_text(request)

            assert response.error is not None
            assert mock_api.call_count == 1  # No retries for auth errors


class TestGoogleProviderTimeout:
    """Tests for timeout configuration."""

    @pytest.fixture
    def provider(self):
        return GoogleTranslationProvider(use_cache=False)

    def test_default_timeout(self, provider):
        """Test default timeout is 30 seconds."""
        assert provider.timeout == 30

    def test_custom_timeout(self):
        """Test custom timeout configuration."""
        provider = GoogleTranslationProvider(use_cache=False, timeout=60)
        assert provider.timeout == 60

    def test_timeout_raises_network_error(self, provider):
        """Test that timeout raises GOOGLE_NETWORK_ERROR."""
        request = TranslationRequest(text="Hello", target_language="fr")

        import socket

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = socket.timeout("Request timed out")

            response = provider.translate_text(request)

            assert response.error is not None
            assert response.error_code == GOOGLE_NETWORK_ERROR


class TestGoogleProviderErrorFormat:
    """Tests for JSON error format compliance."""

    @pytest.fixture
    def provider(self):
        return GoogleTranslationProvider(use_cache=False)

    def test_error_response_format(self, provider):
        """Test that errors return JSON: {error, message, details?} format."""
        request = TranslationRequest(text="Hello", target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_QUOTA_EXCEEDED,
                message="Quota exceeded",
                details={"reset_at": "2024-01-16T00:00:00Z"},
            )

            response = provider.translate_text(request)

            assert response.error is not None
            assert response.error_code is not None
            error_dict = response.to_error_dict()
            assert "error" in error_dict
            assert "message" in error_dict

    def test_error_no_document_content_in_response(self, provider):
        """Test that error response never contains document content."""
        sensitive_text = "SENSITIVE_DATA_12345"
        request = TranslationRequest(text=sensitive_text, target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_NETWORK_ERROR, message="Network error"
            )

            response = provider.translate_text(request)

            assert sensitive_text not in str(response.error)
            assert sensitive_text not in str(response.to_error_dict())


class TestGoogleProviderLogging:
    """Tests for structlog logging."""

    @pytest.fixture
    def provider(self):
        return GoogleTranslationProvider(use_cache=False)

    def test_error_logged_with_structlog(self, provider):
        """Test errors are logged with structlog (no document content)."""
        request = TranslationRequest(text="Hello", target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_QUOTA_EXCEEDED, message="Quota exceeded"
            )

            with patch("services.providers.google_provider.logger") as mock_logger:
                response = provider.translate_text(request)

                assert mock_logger.error.called or mock_logger.warning.called

    def test_log_contains_metadata_not_content(self, provider):
        """Test logs contain metadata (text_length) not content."""
        request = TranslationRequest(text="Secret content", target_language="fr")

        with patch.object(provider, "_make_api_request") as mock_api:
            mock_api.side_effect = GoogleProviderError(
                code=GOOGLE_NETWORK_ERROR, message="Network error"
            )

            with patch("services.providers.google_provider.logger") as mock_logger:
                response = provider.translate_text(request)

                if mock_logger.error.called:
                    call_args = str(mock_logger.error.call_args)
                    assert "Secret content" not in call_args


@pytest.mark.integration
class TestGoogleProviderRealAPI:
    """Integration tests with real Google Translate API (via deep_translator)."""

    @pytest.fixture
    def provider(self):
        return GoogleTranslationProvider(use_cache=False)

    def test_real_translation_en_to_fr(self, provider):
        """Test real translation from English to French."""
        request = TranslationRequest(text="Hello", target_language="fr")
        response = provider.translate_text(request)

        assert response.error is None
        assert response.translated_text.lower() in ["bonjour", "salut", "hello"]
        assert response.provider_name == "google"

    def test_real_translation_with_auto_detect(self, provider):
        """Test translation with automatic language detection."""
        request = TranslationRequest(
            text="Bonjour le monde", target_language="en", source_language="auto"
        )
        response = provider.translate_text(request)

        assert response.error is None
        assert (
            "world" in response.translated_text.lower()
            or "hello" in response.translated_text.lower()
        )

    def test_real_health_check(self, provider):
        """Test real health check."""
        status = provider.health_check()

        assert status.name == "google"
        assert status.available is True
        assert status.latency_ms is not None
        assert status.last_check is not None

    def test_real_batch_translation(self, provider):
        """Test real batch translation."""
        requests = [
            TranslationRequest(text="Hello", target_language="es"),
            TranslationRequest(text="World", target_language="es"),
        ]
        responses = provider.translate_batch(requests)

        assert len(responses) == 2
        assert all(r.error is None for r in responses)
        assert "hola" in responses[0].translated_text.lower()
        assert "mundo" in responses[1].translated_text.lower()


class TestGoogleProviderOptimization:
    """Tests for API usage optimization."""

    @pytest.fixture
    def provider(self):
        return GoogleTranslationProvider(use_cache=False)

    def test_skip_translation_same_language(self, provider):
        """Test translation is skipped when source == target."""
        request = TranslationRequest(
            text="Hello World", target_language="en", source_language="en"
        )
        response = provider.translate_text(request)

        assert response.translated_text == "Hello World"
        assert response.from_cache is False

    def test_translation_not_skipped_auto_detect(self, provider):
        """Test translation is not skipped with auto-detect."""
        request = TranslationRequest(
            text="Bonjour", target_language="en", source_language="auto"
        )

        with patch.object(provider, "_make_api_request", return_value="Hello"):
            response = provider.translate_text(request)

            assert response.translated_text == "Hello"
            provider._make_api_request.assert_called_once()

    def test_usage_metrics_logged(self, provider):
        """Test that usage metrics are logged on success."""
        request = TranslationRequest(text="Hello", target_language="fr")

        with patch.object(provider, "_make_api_request", return_value="Bonjour"):
            with patch("services.providers.google_provider.logger") as mock_logger:
                response = provider.translate_text(request)

                # Check that success was logged with metrics
                success_calls = [
                    call
                    for call in mock_logger.info.call_args_list
                    if "google_translation_success" in str(call)
                ]
                assert len(success_calls) > 0
                log_msg = str(success_calls[0])
                assert "chars=" in log_msg
                assert "source_lang=" in log_msg
