"""Unit tests for MiniMax client."""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from letta.llm_api.minimax_client import MiniMaxClient
from letta.schemas.enums import AgentType
from letta.schemas.llm_config import LLMConfig

# MiniMax API base URL
MINIMAX_BASE_URL = "https://api.minimax.io/anthropic"


class TestMiniMaxClient:
    """Tests for MiniMaxClient."""

    def setup_method(self):
        """Set up test fixtures."""
        self.client = MiniMaxClient(put_inner_thoughts_first=True)
        self.llm_config = LLMConfig(
            model="MiniMax-M2.1",
            model_endpoint_type="minimax",
            model_endpoint=MINIMAX_BASE_URL,
            context_window=200000,
        )

    def test_is_reasoning_model_always_true(self):
        """All MiniMax models support native interleaved thinking."""
        assert self.client.is_reasoning_model(self.llm_config) is True

        # Test with different models
        for model_name in ["MiniMax-M2.1", "MiniMax-M2.1-lightning", "MiniMax-M2"]:
            config = LLMConfig(
                model=model_name,
                model_endpoint_type="minimax",
                model_endpoint=MINIMAX_BASE_URL,
                context_window=200000,
            )
            assert self.client.is_reasoning_model(config) is True

    def test_requires_auto_tool_choice(self):
        """MiniMax supports all tool choice modes."""
        assert self.client.requires_auto_tool_choice(self.llm_config) is False

    def test_supports_structured_output(self):
        """MiniMax doesn't currently advertise structured output support."""
        assert self.client.supports_structured_output(self.llm_config) is False

    @patch("letta.llm_api.minimax_client.model_settings")
    def test_get_anthropic_client_with_api_key(self, mock_settings):
        """Test client creation with API key."""
        mock_settings.minimax_api_key = "test-api-key"

        with patch("letta.llm_api.minimax_client.anthropic") as mock_anthropic:
            mock_anthropic.Anthropic.return_value = MagicMock()

            # Mock BYOK to return no override
            self.client.get_byok_overrides = MagicMock(return_value=(None, None, None))

            self.client._get_anthropic_client(self.llm_config, async_client=False)

            mock_anthropic.Anthropic.assert_called_once_with(
                api_key="test-api-key",
                base_url=MINIMAX_BASE_URL,
            )

    @patch("letta.llm_api.minimax_client.model_settings")
    def test_get_anthropic_client_async(self, mock_settings):
        """Test async client creation."""
        mock_settings.minimax_api_key = "test-api-key"

        with patch("letta.llm_api.minimax_client.anthropic") as mock_anthropic:
            mock_anthropic.AsyncAnthropic.return_value = MagicMock()

            # Mock BYOK to return no override
            self.client.get_byok_overrides = MagicMock(return_value=(None, None, None))

            self.client._get_anthropic_client(self.llm_config, async_client=True)

            mock_anthropic.AsyncAnthropic.assert_called_once_with(
                api_key="test-api-key",
                base_url=MINIMAX_BASE_URL,
            )


class TestMiniMaxClientTemperatureClamping:
    """Tests for temperature clamping in build_request_data."""

    def setup_method(self):
        """Set up test fixtures."""
        self.client = MiniMaxClient(put_inner_thoughts_first=True)
        self.llm_config = LLMConfig(
            model="MiniMax-M2.1",
            model_endpoint_type="minimax",
            model_endpoint=MINIMAX_BASE_URL,
            context_window=200000,
            temperature=0.7,
        )

    @patch.object(MiniMaxClient, "build_request_data")
    def test_temperature_clamping_is_applied(self, mock_build):
        """Verify build_request_data is called for temperature clamping."""
        # This is a basic test to ensure the method exists and can be called
        mock_build.return_value = {"temperature": 0.7}
        self.client.build_request_data(
            agent_type=AgentType.letta_v1_agent,
            messages=[],
            llm_config=self.llm_config,
        )
        mock_build.assert_called_once()

    def test_temperature_zero_clamped(self):
        """Test that temperature=0 is clamped to 0.01."""
        config = LLMConfig(
            model="MiniMax-M2.1",
            model_endpoint_type="minimax",
            model_endpoint=MINIMAX_BASE_URL,
            context_window=200000,
            temperature=0,
        )

        # Mock the parent class method to return a basic dict
        with patch.object(MiniMaxClient.__bases__[0], "build_request_data") as mock_parent:
            mock_parent.return_value = {"temperature": 0, "model": "MiniMax-M2.1"}

            result = self.client.build_request_data(
                agent_type=AgentType.letta_v1_agent,
                messages=[],
                llm_config=config,
            )

            # Temperature should be clamped to 0.01
            assert result["temperature"] == 0.01

    def test_temperature_negative_clamped(self):
        """Test that negative temperature is clamped to 0.01."""
        config = LLMConfig(
            model="MiniMax-M2.1",
            model_endpoint_type="minimax",
            model_endpoint=MINIMAX_BASE_URL,
            context_window=200000,
            temperature=-0.5,
        )

        with patch.object(MiniMaxClient.__bases__[0], "build_request_data") as mock_parent:
            mock_parent.return_value = {"temperature": -0.5, "model": "MiniMax-M2.1"}

            result = self.client.build_request_data(
                agent_type=AgentType.letta_v1_agent,
                messages=[],
                llm_config=config,
            )

            assert result["temperature"] == 0.01

    def test_temperature_above_one_clamped(self):
        """Test that temperature > 1.0 is clamped to 1.0."""
        config = LLMConfig(
            model="MiniMax-M2.1",
            model_endpoint_type="minimax",
            model_endpoint=MINIMAX_BASE_URL,
            context_window=200000,
            temperature=1.5,
        )

        with patch.object(MiniMaxClient.__bases__[0], "build_request_data") as mock_parent:
            mock_parent.return_value = {"temperature": 1.5, "model": "MiniMax-M2.1"}

            result = self.client.build_request_data(
                agent_type=AgentType.letta_v1_agent,
                messages=[],
                llm_config=config,
            )

            assert result["temperature"] == 1.0

    def test_temperature_valid_not_modified(self):
        """Test that valid temperature values are not modified."""
        config = LLMConfig(
            model="MiniMax-M2.1",
            model_endpoint_type="minimax",
            model_endpoint=MINIMAX_BASE_URL,
            context_window=200000,
            temperature=0.7,
        )

        with patch.object(MiniMaxClient.__bases__[0], "build_request_data") as mock_parent:
            mock_parent.return_value = {"temperature": 0.7, "model": "MiniMax-M2.1"}

            result = self.client.build_request_data(
                agent_type=AgentType.letta_v1_agent,
                messages=[],
                llm_config=config,
            )

            assert result["temperature"] == 0.7


class TestMiniMaxClientUsesNonBetaAPI:
    """Tests to verify MiniMax client uses non-beta API."""

    def test_request_uses_messages_not_beta(self):
        """Verify request() uses client.messages.create, not client.beta.messages.create."""
        client = MiniMaxClient(put_inner_thoughts_first=True)
        llm_config = LLMConfig(
            model="MiniMax-M2.1",
            model_endpoint_type="minimax",
            model_endpoint=MINIMAX_BASE_URL,
            context_window=200000,
        )

        with patch.object(client, "_get_anthropic_client") as mock_get_client:
            mock_anthropic_client = MagicMock()
            mock_response = MagicMock()
            mock_response.model_dump.return_value = {"content": [{"type": "text", "text": "Hello"}]}
            mock_anthropic_client.messages.create.return_value = mock_response
            mock_get_client.return_value = mock_anthropic_client

            client.request({"model": "MiniMax-M2.1"}, llm_config)

            # Verify messages.create was called (not beta.messages.create)
            mock_anthropic_client.messages.create.assert_called_once()
            # Verify beta was NOT accessed
            assert not hasattr(mock_anthropic_client, "beta") or not mock_anthropic_client.beta.messages.create.called

    @pytest.mark.asyncio
    async def test_request_async_uses_messages_not_beta(self):
        """Verify request_async() uses client.messages.create, not client.beta.messages.create."""
        client = MiniMaxClient(put_inner_thoughts_first=True)
        llm_config = LLMConfig(
            model="MiniMax-M2.1",
            model_endpoint_type="minimax",
            model_endpoint=MINIMAX_BASE_URL,
            context_window=200000,
        )

        with patch.object(client, "_get_anthropic_client_async") as mock_get_client:
            mock_anthropic_client = AsyncMock()
            mock_response = MagicMock()
            mock_response.model_dump.return_value = {"content": [{"type": "text", "text": "Hello"}]}
            mock_anthropic_client.messages.create.return_value = mock_response
            mock_get_client.return_value = mock_anthropic_client

            await client.request_async({"model": "MiniMax-M2.1"}, llm_config)

            # Verify messages.create was called (not beta.messages.create)
            mock_anthropic_client.messages.create.assert_called_once()

    @pytest.mark.asyncio
    async def test_stream_async_uses_messages_not_beta(self):
        """Verify stream_async() uses client.messages.create, not client.beta.messages.create."""
        client = MiniMaxClient(put_inner_thoughts_first=True)
        llm_config = LLMConfig(
            model="MiniMax-M2.1",
            model_endpoint_type="minimax",
            model_endpoint=MINIMAX_BASE_URL,
            context_window=200000,
        )

        with patch.object(client, "_get_anthropic_client_async") as mock_get_client:
            mock_anthropic_client = AsyncMock()
            mock_stream = AsyncMock()
            mock_anthropic_client.messages.create.return_value = mock_stream
            mock_get_client.return_value = mock_anthropic_client

            await client.stream_async({"model": "MiniMax-M2.1"}, llm_config)

            # Verify messages.create was called (not beta.messages.create)
            mock_anthropic_client.messages.create.assert_called_once()
            # Verify stream=True was set
            call_kwargs = mock_anthropic_client.messages.create.call_args[1]
            assert call_kwargs.get("stream") is True
