import pytest
from unittest.mock import AsyncMock, patch, MagicMock
import httpx

from app.services.key_verifier import KeyStatus
from app.services.key_verifier import (
    KeyVerifier,
    KeyVerifierFactory,
    ChatGPTKeyVerifier,
    DeepSeekKeyVerifier,
    KimiKeyVerifier,
    QwenKeyVerifier,
    PerplexityKeyVerifier,
    GeminiKeyVerifier,
    WenxinKeyVerifier,
    DoubaoKeyVerifier,
    YuanbaoKeyVerifier,
    DefaultKeyVerifier,
)


def create_mock_client(mock_response: MagicMock) -> AsyncMock:
    mock_client = AsyncMock()
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)
    mock_client.post = AsyncMock(return_value=mock_response)
    mock_client.get = AsyncMock(return_value=mock_response)
    return mock_client


class TestKeyVerifierFactory:
    def test_get_chatgpt_verifier(self):
        verifier = KeyVerifierFactory.get_verifier("chatgpt")
        assert isinstance(verifier, ChatGPTKeyVerifier)

    def test_get_deepseek_verifier(self):
        verifier = KeyVerifierFactory.get_verifier("deepseek")
        assert isinstance(verifier, DeepSeekKeyVerifier)

    def test_get_kimi_verifier(self):
        verifier = KeyVerifierFactory.get_verifier("kimi")
        assert isinstance(verifier, KimiKeyVerifier)

    def test_get_qwen_verifier(self):
        verifier = KeyVerifierFactory.get_verifier("qwen")
        assert isinstance(verifier, QwenKeyVerifier)

    def test_get_perplexity_verifier(self):
        verifier = KeyVerifierFactory.get_verifier("perplexity")
        assert isinstance(verifier, PerplexityKeyVerifier)

    def test_get_gemini_verifier(self):
        verifier = KeyVerifierFactory.get_verifier("gemini")
        assert isinstance(verifier, GeminiKeyVerifier)

    def test_get_unknown_verifier_returns_default(self):
        verifier = KeyVerifierFactory.get_verifier("unknown_engine")
        assert isinstance(verifier, DefaultKeyVerifier)

    @pytest.mark.asyncio
    async def test_verify_delegates_to_correct_verifier(self):
        with patch.object(ChatGPTKeyVerifier, 'verify', new_callable=AsyncMock) as mock_verify:
            mock_verify.return_value = KeyStatus.ACTIVE
            status = await KeyVerifierFactory.verify("chatgpt", "sk-test-key-12345")
            mock_verify.assert_called_once_with("sk-test-key-12345")
            assert status == KeyStatus.ACTIVE


class TestDefaultKeyVerifier:
    @pytest.mark.asyncio
    async def test_default_verifier_returns_unknown(self):
        verifier = DefaultKeyVerifier()
        status = await verifier.verify("any-key")
        assert status == KeyStatus.UNKNOWN


class TestChatGPTKeyVerifier:
    @pytest.mark.asyncio
    async def test_verify_active_key_returns_active(self):
        verifier = ChatGPTKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 200

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-test-key-12345")
            assert status == KeyStatus.ACTIVE
            mock_client.post.assert_called_once()

    @pytest.mark.asyncio
    async def test_verify_invalid_key_returns_invalid(self):
        verifier = ChatGPTKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 401
        mock_response.text = "Invalid API key"

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-invalid-key")
            assert status == KeyStatus.INVALID

    @pytest.mark.asyncio
    async def test_verify_rate_limited_key_returns_rate_limited(self):
        verifier = ChatGPTKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 429
        mock_response.text = "Rate limit exceeded"

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-rate-limited-key")
            assert status == KeyStatus.RATE_LIMITED

    @pytest.mark.asyncio
    async def test_verify_network_error_returns_unknown(self):
        verifier = ChatGPTKeyVerifier()

        mock_client = create_mock_client(MagicMock())
        mock_client.post = AsyncMock(side_effect=httpx.ConnectError("Connection failed"))

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-test-key")
            assert status == KeyStatus.UNKNOWN

    @pytest.mark.asyncio
    async def test_verify_timeout_returns_unknown(self):
        verifier = ChatGPTKeyVerifier()

        mock_client = create_mock_client(MagicMock())
        mock_client.post = AsyncMock(side_effect=httpx.TimeoutException("Request timeout"))

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-test-key")
            assert status == KeyStatus.UNKNOWN


class TestDeepSeekKeyVerifier:
    @pytest.mark.asyncio
    async def test_verify_active_key_returns_active(self):
        verifier = DeepSeekKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 200

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-test-key-12345")
            assert status == KeyStatus.ACTIVE
            mock_client.post.assert_called_once()

    @pytest.mark.asyncio
    async def test_verify_invalid_key_returns_invalid(self):
        verifier = DeepSeekKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 401

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-invalid-key")
            assert status == KeyStatus.INVALID

    @pytest.mark.asyncio
    async def test_verify_rate_limited_key_returns_rate_limited(self):
        verifier = DeepSeekKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 429

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-rate-limited-key")
            assert status == KeyStatus.RATE_LIMITED


class TestKimiKeyVerifier:
    @pytest.mark.asyncio
    async def test_verify_active_key_returns_active(self):
        verifier = KimiKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 200

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-test-key-12345")
            assert status == KeyStatus.ACTIVE
            mock_client.post.assert_called_once()

    @pytest.mark.asyncio
    async def test_verify_invalid_key_returns_invalid(self):
        verifier = KimiKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 401

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-invalid-key")
            assert status == KeyStatus.INVALID


class TestQwenKeyVerifier:
    @pytest.mark.asyncio
    async def test_verify_active_key_returns_active(self):
        verifier = QwenKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 200

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-test-key-12345")
            assert status == KeyStatus.ACTIVE

    @pytest.mark.asyncio
    async def test_verify_invalid_key_returns_invalid(self):
        verifier = QwenKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 401

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-invalid-key")
            assert status == KeyStatus.INVALID

    @pytest.mark.asyncio
    async def test_verify_forbidden_key_returns_expired(self):
        verifier = QwenKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 403

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-expired-key")
            assert status == KeyStatus.EXPIRED


class TestPerplexityKeyVerifier:
    @pytest.mark.asyncio
    async def test_verify_active_key_returns_active(self):
        verifier = PerplexityKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 200

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-test-key-12345")
            assert status == KeyStatus.ACTIVE

    @pytest.mark.asyncio
    async def test_verify_invalid_key_returns_invalid(self):
        verifier = PerplexityKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 401

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-invalid-key")
            assert status == KeyStatus.INVALID


class TestGeminiKeyVerifier:
    @pytest.mark.asyncio
    async def test_verify_active_key_returns_active(self):
        verifier = GeminiKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 200

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-test-key-12345")
            assert status == KeyStatus.ACTIVE

    @pytest.mark.asyncio
    async def test_verify_invalid_key_returns_invalid(self):
        verifier = GeminiKeyVerifier()
        mock_response = MagicMock()
        mock_response.status_code = 403

        mock_client = create_mock_client(mock_response)

        with patch('httpx.AsyncClient', return_value=mock_client):
            status = await verifier.verify("sk-invalid-key")
            assert status == KeyStatus.INVALID


class TestOtherEngineVerifiers:
    @pytest.mark.asyncio
    async def test_wenxin_verifier_returns_unknown(self):
        verifier = WenxinKeyVerifier()
        status = await verifier.verify("test-key")
        assert status == KeyStatus.UNKNOWN

    @pytest.mark.asyncio
    async def test_doubao_verifier_returns_unknown(self):
        verifier = DoubaoKeyVerifier()
        status = await verifier.verify("test-key")
        assert status == KeyStatus.UNKNOWN

    @pytest.mark.asyncio
    async def test_yuanbao_verifier_returns_unknown(self):
        verifier = YuanbaoKeyVerifier()
        status = await verifier.verify("test-key")
        assert status == KeyStatus.UNKNOWN


class TestKeyVerifierIntegrationWithAPIManager:
    @pytest.mark.asyncio
    async def test_api_key_manager_verify_uses_factory(self):
        from app.services.api_key_manager import APIKeyManager

        manager = APIKeyManager()

        with patch.object(KeyVerifierFactory, 'verify', new_callable=AsyncMock) as mock_verify:
            mock_verify.return_value = KeyStatus.ACTIVE
            status = await manager.verify_key("chatgpt", "sk-test-key-1234567890")
            assert status == KeyStatus.ACTIVE
            mock_verify.assert_called_once_with("chatgpt", "sk-test-key-1234567890")

    @pytest.mark.asyncio
    async def test_api_key_manager_verify_handles_factory_error(self):
        from app.services.api_key_manager import APIKeyManager

        manager = APIKeyManager()

        with patch.object(KeyVerifierFactory, 'verify', new_callable=AsyncMock) as mock_verify:
            mock_verify.side_effect = Exception("Network error")
            status = await manager.verify_key("chatgpt", "sk-test-key-1234567890")
            assert status == KeyStatus.UNKNOWN

    @pytest.mark.asyncio
    async def test_api_key_manager_verify_short_key_returns_invalid(self):
        from app.services.api_key_manager import APIKeyManager

        manager = APIKeyManager()
        status = await manager.verify_key("chatgpt", "short")
        assert status == KeyStatus.INVALID

    @pytest.mark.asyncio
    async def test_api_key_manager_verify_empty_key_returns_invalid(self):
        from app.services.api_key_manager import APIKeyManager

        manager = APIKeyManager()
        status = await manager.verify_key("chatgpt", "")
        assert status == KeyStatus.INVALID
