"""
Tests for the MinimaxTranslationProvider.

Validates Bug 1 fix:
- default base_url is the public ``api.minimax.io`` host (NOT ``api.minimax.chat``)
- default model is ``MiniMax-M3``
- ``is_available()`` / ``health_check()`` tolerate a missing ``/models`` path
  (Minimax does not document it) and only mark the provider down on 401/network error
- translation success, 429 retry, 401 handling
"""

import pytest
from unittest.mock import patch, MagicMock
from requests.exceptions import Timeout

from services.providers.minimax_provider import (
    MinimaxTranslationProvider,
    MinimaxProviderError,
    MINIMAX_RATE_LIMITED,
    MINIMAX_INVALID_KEY,
    MINIMAX_TIMEOUT,
    MINIMAX_SERVICE_ERROR,
)
from services.providers.schemas import TranslationRequest


class TestMinimaxProviderConfig:
    """Defaults must point at the real public endpoint."""

    def test_default_base_url_is_public_host(self):
        provider = MinimaxTranslationProvider(api_key="k", max_retries=0)
        assert provider._base_url == "https://api.minimax.io/v1"
        # Regression guard: the old broken host must never come back.
        assert "minimax.chat" not in provider._base_url

    def test_default_model_is_m3(self):
        provider = MinimaxTranslationProvider(api_key="k", max_retries=0)
        assert provider._model == "MiniMax-M3"

    def test_custom_base_url_respected(self):
        provider = MinimaxTranslationProvider(
            api_key="k", base_url="https://proxy.example.com/v1", max_retries=0
        )
        assert provider._base_url == "https://proxy.example.com/v1"

    def test_get_name(self):
        provider = MinimaxTranslationProvider(api_key="k", max_retries=0)
        assert provider.get_name() == "minimax"


class TestMinimaxAvailabilityProbe:
    """is_available/health_check must not fail just because /models 404s."""

    @pytest.fixture
    def provider(self):
        return MinimaxTranslationProvider(api_key="k", max_retries=0)

    def _mock_get(self, status_code: int):
        mock_response = MagicMock()
        mock_response.status_code = status_code
        return mock_response

    @patch("requests.get")
    def test_available_when_models_returns_200(self, mock_get, provider):
        mock_get.return_value = self._mock_get(200)
        assert provider.is_available() is True

    @patch("requests.get")
    def test_available_when_models_404(self, mock_get, provider):
        # Minimax does not document /models; a 404 must NOT mark it unavailable.
        mock_get.return_value = self._mock_get(404)
        assert provider.is_available() is True

    @patch("requests.get")
    def test_unavailable_on_401(self, mock_get, provider):
        mock_get.return_value = self._mock_get(401)
        assert provider.is_available() is False

    @patch("requests.get")
    def test_unavailable_on_network_error(self, _mock_get, provider):
        def _raise(*a, **kw):
            raise Timeout("boom")

        with patch("requests.get", side_effect=_raise):
            assert provider.is_available() is False

    @patch("requests.get")
    def test_health_check_tolerates_404(self, mock_get, provider):
        mock_get.return_value = self._mock_get(404)
        status = provider.health_check()
        assert status.available is True
        assert status.name == "minimax"

    @patch("requests.get")
    def test_health_check_marks_down_on_401(self, mock_get, provider):
        mock_get.return_value = self._mock_get(401)
        status = provider.health_check()
        assert status.available is False


class TestMinimaxTranslateText:
    @pytest.fixture
    def provider(self):
        return MinimaxTranslationProvider(api_key="k", model="MiniMax-M3", max_retries=0)

    def _mock_post(self, payload, status_code=200):
        mock_response = MagicMock()
        mock_response.status_code = status_code
        mock_response.json.return_value = payload
        mock_response.text = ""
        return mock_response

    @patch("requests.post")
    def test_success(self, mock_post, provider):
        mock_post.return_value = self._mock_post(
            {"choices": [{"message": {"content": "Bonjour"}}], "usage": {}}
        )
        resp = provider.translate_text(TranslationRequest(text="Hello", target_language="fr"))
        assert resp.translated_text == "Bonjour"
        assert resp.provider_name == "minimax"
        # Verify we hit the public host on the OpenAI-compatible path.
        called_url = mock_post.call_args[0][0]
        assert called_url == "https://api.minimax.io/v1/chat/completions"

    def test_empty_text_short_circuits(self, provider):
        resp = provider.translate_text(TranslationRequest(text="", target_language="fr"))
        assert resp.translated_text == ""

    @patch("requests.post")
    def test_invalid_key_returns_error(self, mock_post, provider):
        mock_post.return_value = self._mock_post({"error": "bad key"}, status_code=401)
        resp = provider.translate_text(TranslationRequest(text="Hello", target_language="fr"))
        assert resp.error_code == MINIMAX_INVALID_KEY
        # Original text returned on failure.
        assert resp.translated_text == "Hello"

    @patch("time.sleep")
    @patch("requests.post")
    def test_rate_limit_then_success(self, mock_post, mock_sleep):
        provider = MinimaxTranslationProvider(api_key="k", max_retries=2, retry_delay=0.01)
        mock_post.side_effect = [
            self._mock_post({"error": "slow down"}, status_code=429),
            self._mock_post({"choices": [{"message": {"content": "Hola"}}], "usage": {}}),
        ]
        resp = provider.translate_text(TranslationRequest(text="Hello", target_language="es"))
        assert resp.translated_text == "Hola"
        assert mock_sleep.called  # backoff happened

    @patch("requests.post")
    def test_service_error_when_empty_choices(self, mock_post, provider):
        mock_post.return_value = self._mock_post({"choices": []})
        resp = provider.translate_text(TranslationRequest(text="Hello", target_language="fr"))
        assert resp.error_code == MINIMAX_SERVICE_ERROR


class TestMinimaxProviderError:
    def test_error_carries_code_and_message(self):
        err = MinimaxProviderError(MINIMAX_TIMEOUT, "timed out", details={"wait": 1})
        assert err.code == MINIMAX_TIMEOUT
        assert err.message == "timed out"
        assert err.details == {"wait": 1}
