# tests/test_keyring_security.py
"""Tests for keyring security checks and API key validation."""
import os
import logging
import pytest
from unittest.mock import patch, MagicMock


# Create proper mock classes that work with str(type())
class MockPlaintextKeyring:
    """Mock class with PlaintextKeyring in the type string."""
    pass


class MockNullKeyring:
    """Mock class with NullKeyring in the type string."""
    pass


class MockSecureKeyring:
    """Mock class representing a secure keyring backend."""
    pass


class TestKeyringSecurityCheck:
    """Tests for _check_keyring_security() function."""

    def test_warns_on_plaintext_keyring(self, caplog):
        """Should log warning when PlaintextKeyring is detected."""
        from second_opinion_mcp.server import _check_keyring_security, logger

        # Need to enable propagation for caplog to capture
        logger.propagate = True
        try:
            with patch("second_opinion_mcp.server.keyring.get_keyring", return_value=MockPlaintextKeyring()):
                with caplog.at_level(logging.WARNING, logger="second-opinion"):
                    _check_keyring_security()

            assert "SECURITY WARNING" in caplog.text
            assert "PlaintextKeyring" in caplog.text
        finally:
            logger.propagate = False

    def test_warns_on_null_keyring(self, caplog):
        """Should log warning when NullKeyring is detected."""
        from second_opinion_mcp.server import _check_keyring_security, logger

        logger.propagate = True
        try:
            with patch("second_opinion_mcp.server.keyring.get_keyring", return_value=MockNullKeyring()):
                with caplog.at_level(logging.WARNING, logger="second-opinion"):
                    _check_keyring_security()

            assert "SECURITY WARNING" in caplog.text
        finally:
            logger.propagate = False

    def test_fails_on_insecure_backend_in_strict_mode(self):
        """Should raise RuntimeError when strict mode is enabled."""
        from second_opinion_mcp.server import _check_keyring_security

        with patch("second_opinion_mcp.server.keyring.get_keyring", return_value=MockPlaintextKeyring()):
            with patch.dict(os.environ, {"SECOND_OPINION_STRICT_KEYRING": "1"}):
                with pytest.raises(RuntimeError) as exc_info:
                    _check_keyring_security()

        assert "insecure keyring backend" in str(exc_info.value)

    def test_no_warning_on_secure_backend(self, caplog):
        """Should not warn when using a secure backend."""
        from second_opinion_mcp.server import _check_keyring_security, logger

        logger.propagate = True
        try:
            with patch("second_opinion_mcp.server.keyring.get_keyring", return_value=MockSecureKeyring()):
                with caplog.at_level(logging.WARNING, logger="second-opinion"):
                    _check_keyring_security()

            assert "SECURITY WARNING" not in caplog.text
        finally:
            logger.propagate = False


class TestApiKeyValidation:
    """Tests for _validate_api_key() function."""

    def test_rejects_empty_key(self):
        """Should reject empty API keys."""
        from second_opinion_mcp.server import _validate_api_key

        valid, error = _validate_api_key("deepseek", "")
        assert not valid
        assert "Empty API key" in error

    def test_rejects_whitespace_only_key(self):
        """Should reject whitespace-only API keys."""
        from second_opinion_mcp.server import _validate_api_key

        valid, error = _validate_api_key("deepseek", "   ")
        assert not valid
        assert "Empty API key" in error

    def test_validates_deepseek_key_format(self):
        """Should validate DeepSeek key starts with sk- and has min length."""
        from second_opinion_mcp.server import _validate_api_key

        # Valid key
        valid, error = _validate_api_key("deepseek", "sk-1234567890abcdef1234")
        assert valid
        assert error == ""

        # Invalid - wrong prefix
        valid, error = _validate_api_key("deepseek", "1234567890abcdef12345678")
        assert not valid
        assert "Invalid deepseek API key format" in error

        # Invalid - too short
        valid, error = _validate_api_key("deepseek", "sk-short")
        assert not valid
        assert "Invalid deepseek API key format" in error

    def test_validates_moonshot_key_format(self):
        """Should validate Moonshot key is alphanumeric with min length."""
        from second_opinion_mcp.server import _validate_api_key

        # Valid key (32+ alphanumeric chars)
        valid, error = _validate_api_key("moonshot", "abcdefghijklmnopqrstuvwxyz123456")
        assert valid
        assert error == ""

        # Invalid - too short
        valid, error = _validate_api_key("moonshot", "short")
        assert not valid
        assert "Invalid moonshot API key format" in error

        # Invalid - special characters
        valid, error = _validate_api_key("moonshot", "invalid key with spaces!!!")
        assert not valid

    def test_validates_openrouter_key_format(self):
        """Should validate OpenRouter key starts with sk-or- and has min length."""
        from second_opinion_mcp.server import _validate_api_key

        # Valid key
        valid, error = _validate_api_key("openrouter", "sk-or-1234567890abcdef")
        assert valid
        assert error == ""

        # Invalid - wrong prefix
        valid, error = _validate_api_key("openrouter", "sk-1234567890abcdef12345")
        assert not valid
        assert "Invalid openrouter API key format" in error

    def test_accepts_unknown_provider_keys(self):
        """Should accept any non-empty key for unknown providers."""
        from second_opinion_mcp.server import _validate_api_key

        valid, error = _validate_api_key("unknown_provider", "any-key-format")
        assert valid
        assert error == ""
