"""Tests for CompressionStrategy Protocol and create_compressor factory"""

from unittest.mock import MagicMock, patch

import pytest

from agentkit.core.compressor import CompressionStrategy, ContextCompressor, create_compressor


# ── CompressionStrategy Protocol Tests ────────────────


class TestCompressionStrategyProtocol:
    """CompressionStrategy 协议满足性测试"""

    def test_context_compressor_satisfies_protocol(self):
        """ContextCompressor 实现了 CompressionStrategy 协议"""
        compressor = ContextCompressor()
        assert isinstance(compressor, CompressionStrategy)

    def test_protocol_requires_compress_method(self):
        """协议要求 compress 方法"""

        class MissingCompress:
            async def compress_tool_result(self, tool_name: str, result) -> str:
                return str(result)

            def is_available(self) -> bool:
                return True

        assert not isinstance(MissingCompress(), CompressionStrategy)

    def test_protocol_requires_compress_tool_result_method(self):
        """协议要求 compress_tool_result 方法"""

        class MissingCompressToolResult:
            async def compress(self, messages: list[dict]) -> list[dict]:
                return messages

            def is_available(self) -> bool:
                return True

        assert not isinstance(MissingCompressToolResult(), CompressionStrategy)

    def test_protocol_requires_is_available_method(self):
        """协议要求 is_available 方法"""

        class MissingIsAvailable:
            async def compress(self, messages: list[dict]) -> list[dict]:
                return messages

            async def compress_tool_result(self, tool_name: str, result) -> str:
                return str(result)

        assert not isinstance(MissingIsAvailable(), CompressionStrategy)


# ── create_compressor Factory Tests ───────────────────


class TestCreateCompressor:
    """create_compressor 工厂函数测试"""

    def test_none_config_returns_none(self):
        """config 为 None 时返回 None"""
        assert create_compressor(None) is None

    def test_empty_config_returns_none(self):
        """空 config 时返回 None"""
        assert create_compressor({}) is None

    def test_disabled_config_returns_none(self):
        """enabled=False 时返回 None"""
        assert create_compressor({"enabled": False}) is None

    def test_summary_provider_returns_context_compressor(self):
        """provider=summary 返回 ContextCompressor"""
        compressor = create_compressor({"enabled": True, "provider": "summary"})
        assert isinstance(compressor, ContextCompressor)

    def test_default_provider_returns_context_compressor(self):
        """不指定 provider 默认返回 ContextCompressor"""
        compressor = create_compressor({"enabled": True})
        assert isinstance(compressor, ContextCompressor)

    def test_headroom_provider_falls_back_when_not_installed(self):
        """provider=headroom 但未安装时回退到 ContextCompressor"""
        compressor = create_compressor({"enabled": True, "provider": "headroom"})
        assert isinstance(compressor, ContextCompressor)

    def test_summary_config_passed_to_context_compressor(self):
        """max_tokens 和 keep_recent 传递给 ContextCompressor"""
        compressor = create_compressor({
            "enabled": True,
            "provider": "summary",
            "max_tokens": 8000,
            "keep_recent": 5,
        })
        assert isinstance(compressor, ContextCompressor)
        assert compressor._max_tokens == 8000
        assert compressor._keep_recent == 5

    def test_headroom_fallback_config_passed_to_context_compressor(self):
        """headroom 回退时配置也传递给 ContextCompressor"""
        compressor = create_compressor({
            "enabled": True,
            "provider": "headroom",
            "max_tokens": 6000,
            "keep_recent": 4,
        })
        assert isinstance(compressor, ContextCompressor)
        assert compressor._max_tokens == 6000
        assert compressor._keep_recent == 4

    def test_default_config_values(self):
        """默认 max_tokens=4000, keep_recent=3"""
        compressor = create_compressor({"enabled": True})
        assert isinstance(compressor, ContextCompressor)
        assert compressor._max_tokens == 4000
        assert compressor._keep_recent == 3


# ── ContextCompressor New Methods Tests ───────────────


class TestContextCompressorNewMethods:
    """ContextCompressor 新增方法测试"""

    async def test_compress_tool_result_default(self):
        """compress_tool_result 默认返回 str(result)"""
        compressor = ContextCompressor()
        result = await compressor.compress_tool_result("search", {"key": "value"})
        assert result == str({"key": "value"})

    async def test_compress_tool_result_string_input(self):
        """compress_tool_result 对字符串输入直接返回"""
        compressor = ContextCompressor()
        result = await compressor.compress_tool_result("search", "hello world")
        assert result == "hello world"

    async def test_compress_tool_result_numeric_input(self):
        """compress_tool_result 对数字输入返回字符串表示"""
        compressor = ContextCompressor()
        result = await compressor.compress_tool_result("calculator", 42)
        assert result == "42"

    def test_is_available(self):
        """ContextCompressor 始终可用"""
        compressor = ContextCompressor()
        assert compressor.is_available() is True

    def test_is_available_with_gateway(self):
        """即使有 LLMGateway，ContextCompressor 也可用"""
        gateway = MagicMock()
        compressor = ContextCompressor(llm_gateway=gateway)
        assert compressor.is_available() is True


# ── Headroom Import Mock Tests ────────────────────────


class TestHeadroomImportMock:
    """模拟 HeadroomCompressor 导入成功/失败的场景"""

    def test_headroom_available_returns_headroom_instance(self):
        """HeadroomCompressor 可用时返回其实例"""
        mock_compressor = MagicMock()
        mock_compressor.is_available.return_value = True

        mock_module = MagicMock()
        mock_module.HeadroomCompressor.return_value = mock_compressor

        with patch.dict("sys.modules", {"agentkit.core.headroom_compressor": mock_module}):
            compressor = create_compressor({"enabled": True, "provider": "headroom"})
            assert compressor is mock_compressor

    def test_headroom_not_available_falls_back(self):
        """HeadroomCompressor is_available()=False 时回退到 ContextCompressor"""
        mock_compressor = MagicMock()
        mock_compressor.is_available.return_value = False

        mock_module = MagicMock()
        mock_module.HeadroomCompressor.return_value = mock_compressor

        with patch.dict("sys.modules", {"agentkit.core.headroom_compressor": mock_module}):
            compressor = create_compressor({"enabled": True, "provider": "headroom"})
            assert isinstance(compressor, ContextCompressor)
