"""
Extended tests for metadata_tools module to increase test coverage.

These tests focus on the untested paths in metadata_tools.py:
- Rate limit error handling for all three tools
- General exception handling
- Direct tool invocation
"""

from unittest.mock import AsyncMock, MagicMock

import pytest

from openzim_mcp.config import CacheConfig, OpenZimMcpConfig
from openzim_mcp.exceptions import OpenZimMcpRateLimitError
from openzim_mcp.server import OpenZimMcpServer


class TestGetZimMetadataToolInvocation:
    """Test get_zim_metadata tool by invoking the actual registered handler."""

    @pytest.fixture
    def server(self, temp_dir):
        """Create a test server."""
        config = OpenZimMcpConfig(
            allowed_directories=[str(temp_dir)],
            tool_mode="advanced",
            cache=CacheConfig(enabled=False),
        )
        return OpenZimMcpServer(config)

    @pytest.mark.asyncio
    async def test_get_zim_metadata_success(self, server, temp_dir):
        """Test successful metadata retrieval through tool handler."""
        server.async_zim_operations.get_zim_metadata_data = AsyncMock(
            return_value={"title": "Test", "language": "en"}
        )

        tools = server.mcp._tool_manager._tools
        if "get_zim_metadata" in tools:
            tool_handler = tools["get_zim_metadata"].fn
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            assert isinstance(result, dict)
            assert result.get("title") == "Test"

    @pytest.mark.asyncio
    async def test_get_zim_metadata_rate_limit_error(self, server, temp_dir):
        """Test rate limit error handling in get_zim_metadata."""
        server.rate_limiter.check_rate_limit = MagicMock(
            side_effect=OpenZimMcpRateLimitError("Rate limit exceeded")
        )

        tools = server.mcp._tool_manager._tools
        if "get_zim_metadata" in tools:
            tool_handler = tools["get_zim_metadata"].fn
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            # Should return a structured error envelope, not raise
            assert isinstance(result, dict)
            assert result.get("error") is True
            assert result.get("operation") == "get ZIM metadata"

    @pytest.mark.asyncio
    async def test_get_zim_metadata_generic_exception(self, server, temp_dir):
        """Test generic exception handling in get_zim_metadata."""
        server.async_zim_operations.get_zim_metadata_data = AsyncMock(
            side_effect=RuntimeError("Metadata retrieval failed")
        )

        tools = server.mcp._tool_manager._tools
        if "get_zim_metadata" in tools:
            tool_handler = tools["get_zim_metadata"].fn
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            # Should return a structured error envelope, not raise
            assert isinstance(result, dict)
            assert result.get("error") is True
            assert "message" in result


class TestGetMainPageToolInvocation:
    """Test get_main_page tool by invoking the actual registered handler."""

    @pytest.fixture
    def server(self, temp_dir):
        """Create a test server."""
        config = OpenZimMcpConfig(
            allowed_directories=[str(temp_dir)],
            tool_mode="advanced",
            cache=CacheConfig(enabled=False),
        )
        return OpenZimMcpServer(config)

    @pytest.mark.asyncio
    async def test_get_main_page_success(self, server, temp_dir):
        """Test successful main page retrieval through tool handler."""
        server.async_zim_operations.get_main_page_data = AsyncMock(
            return_value={
                "path": "A/Main_Page",
                "title": "Main Page",
                "content": "Main page content here",
                "_meta": {"tokens_est": 5, "chars": 22, "truncated": False},
            }
        )

        tools = server.mcp._tool_manager._tools
        if "get_main_page" in tools:
            tool_handler = tools["get_main_page"].fn
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            assert isinstance(result, dict)
            assert result.get("title") == "Main Page"
            assert "Main page content" in result.get("content", "")

    @pytest.mark.asyncio
    async def test_get_main_page_rate_limit_error(self, server, temp_dir):
        """Test rate limit error handling in get_main_page."""
        server.rate_limiter.check_rate_limit = MagicMock(
            side_effect=OpenZimMcpRateLimitError("Rate limit exceeded")
        )

        tools = server.mcp._tool_manager._tools
        if "get_main_page" in tools:
            tool_handler = tools["get_main_page"].fn
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            # Should return a structured error envelope, not raise
            assert isinstance(result, dict)
            assert result.get("error") is True
            assert result.get("operation") == "get main page"

    @pytest.mark.asyncio
    async def test_get_main_page_generic_exception(self, server, temp_dir):
        """Test generic exception handling in get_main_page."""
        server.async_zim_operations.get_main_page_data = AsyncMock(
            side_effect=RuntimeError("Main page retrieval failed")
        )

        tools = server.mcp._tool_manager._tools
        if "get_main_page" in tools:
            tool_handler = tools["get_main_page"].fn
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            # Should return a structured error envelope, not raise
            assert isinstance(result, dict)
            assert result.get("error") is True
            assert "message" in result


class TestListNamespacesToolInvocation:
    """Test list_namespaces tool by invoking the actual registered handler."""

    @pytest.fixture
    def server(self, temp_dir):
        """Create a test server."""
        config = OpenZimMcpConfig(
            allowed_directories=[str(temp_dir)],
            tool_mode="advanced",
            cache=CacheConfig(enabled=False),
        )
        return OpenZimMcpServer(config)

    @pytest.mark.asyncio
    async def test_list_namespaces_success(self, server, temp_dir):
        """Test successful namespace listing through tool handler."""
        server.async_zim_operations.list_namespaces_data = AsyncMock(
            return_value={"namespaces": {"A": {}, "C": {}, "M": {}, "W": {}}}
        )

        tools = server.mcp._tool_manager._tools
        if "list_namespaces" in tools:
            tool_handler = tools["list_namespaces"].fn
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            assert isinstance(result, dict)
            assert "namespaces" in result

    @pytest.mark.asyncio
    async def test_list_namespaces_rate_limit_error(self, server, temp_dir):
        """Test rate limit error handling in list_namespaces."""
        server.rate_limiter.check_rate_limit = MagicMock(
            side_effect=OpenZimMcpRateLimitError("Rate limit exceeded")
        )

        tools = server.mcp._tool_manager._tools
        if "list_namespaces" in tools:
            tool_handler = tools["list_namespaces"].fn
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            # Should return a structured error envelope, not raise
            assert isinstance(result, dict)
            assert result.get("error") is True
            assert result.get("operation") == "list namespaces"

    @pytest.mark.asyncio
    async def test_list_namespaces_generic_exception(self, server, temp_dir):
        """Test generic exception handling in list_namespaces."""
        server.async_zim_operations.list_namespaces_data = AsyncMock(
            side_effect=RuntimeError("Namespace listing failed")
        )

        tools = server.mcp._tool_manager._tools
        if "list_namespaces" in tools:
            tool_handler = tools["list_namespaces"].fn
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            # Should return a structured error envelope, not raise
            assert isinstance(result, dict)
            assert result.get("error") is True
            assert "message" in result


class TestMetadataToolsInputSanitization:
    """Test input sanitization in metadata tools."""

    @pytest.fixture
    def server(self, temp_dir):
        """Create a test server."""
        config = OpenZimMcpConfig(
            allowed_directories=[str(temp_dir)],
            tool_mode="advanced",
            cache=CacheConfig(enabled=False),
        )
        return OpenZimMcpServer(config)

    @pytest.mark.asyncio
    async def test_get_zim_metadata_input_sanitization(self, server, temp_dir):
        """Test that get_zim_metadata sanitizes file path input."""
        server.async_zim_operations.get_zim_metadata_data = AsyncMock(
            return_value={"title": "Test"}
        )

        tools = server.mcp._tool_manager._tools
        if "get_zim_metadata" in tools:
            tool_handler = tools["get_zim_metadata"].fn
            # Normal path should work
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            assert isinstance(result, dict)
            assert result.get("title") == "Test"

    @pytest.mark.asyncio
    async def test_get_main_page_input_sanitization(self, server, temp_dir):
        """Test that get_main_page sanitizes file path input."""
        server.async_zim_operations.get_main_page_data = AsyncMock(
            return_value={
                "path": "A/Main_Page",
                "title": "Main Page",
                "content": "Main page content",
                "_meta": {"tokens_est": 5, "chars": 18, "truncated": False},
            }
        )

        tools = server.mcp._tool_manager._tools
        if "get_main_page" in tools:
            tool_handler = tools["get_main_page"].fn
            # Normal path should work
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            assert isinstance(result, dict)
            assert "Main page" in result.get("content", "")

    @pytest.mark.asyncio
    async def test_list_namespaces_input_sanitization(self, server, temp_dir):
        """Test that list_namespaces sanitizes file path input."""
        server.async_zim_operations.list_namespaces_data = AsyncMock(
            return_value={"namespaces": {"A": {}}}
        )

        tools = server.mcp._tool_manager._tools
        if "list_namespaces" in tools:
            tool_handler = tools["list_namespaces"].fn
            # Normal path should work
            result = await tool_handler(zim_file_path=str(temp_dir / "test.zim"))
            assert isinstance(result, dict)
            assert "namespaces" in result
