"""Unit tests for get_custom_report_summary MCP tool.

Tests the tool wrapper layer: input validation and error conversion.
"""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from fastmcp.exceptions import ToolError

from testio_mcp.tools.custom_report_summary_tool import (
    get_custom_report_summary as get_custom_report_summary_tool,
)

# Extract actual function from FastMCP FunctionTool wrapper
get_custom_report_summary = get_custom_report_summary_tool.fn  # type: ignore[attr-defined]


def _make_mock_ctx() -> MagicMock:
    """Create a mock Context with server_ctx containing cache."""
    mock_ctx = MagicMock()
    mock_cache = MagicMock()
    mock_cache.customer_id = 1

    # async_session_maker returns an async context manager
    mock_session = AsyncMock()
    mock_cache.async_session_maker.return_value.__aenter__ = AsyncMock(return_value=mock_session)
    mock_cache.async_session_maker.return_value.__aexit__ = AsyncMock(return_value=False)

    mock_ctx.request_context.lifespan_context = {
        "testio_client": MagicMock(),
        "cache": mock_cache,
    }
    return mock_ctx


@pytest.mark.unit
@pytest.mark.asyncio
async def test_rejects_empty_test_ids() -> None:
    """Verify ToolError when test_ids is empty."""
    mock_ctx = _make_mock_ctx()

    with pytest.raises(ToolError, match="test_ids is required"):
        await get_custom_report_summary(ctx=mock_ctx, test_ids=[])


@pytest.mark.unit
@pytest.mark.asyncio
async def test_rejects_too_many_group_by() -> None:
    """Verify ToolError when group_by has more than 2 dimensions."""
    mock_ctx = _make_mock_ctx()

    with pytest.raises(ToolError, match="at most 2 dimensions"):
        await get_custom_report_summary(
            ctx=mock_ctx,
            test_ids=[147162],
            group_by=["course", "intent", "pillar"],
        )


@pytest.mark.unit
@pytest.mark.asyncio
async def test_delegates_to_service() -> None:
    """Verify tool delegates to CustomReportService.get_summary."""
    mock_ctx = _make_mock_ctx()

    expected_result = {
        "test_ids": [147162],
        "total_reports": 76,
        "field_summary": {"accuracy": {"type": "numeric", "count": 76}},
    }

    with patch("testio_mcp.tools.custom_report_summary_tool.CustomReportService") as MockService:
        mock_service_instance = AsyncMock()
        mock_service_instance.get_summary.return_value = expected_result
        MockService.return_value = mock_service_instance

        result = await get_custom_report_summary(
            ctx=mock_ctx,
            test_ids=[147162],
            group_by=["course"],
            fields=["accuracy"],
        )

    assert result == expected_result
    mock_service_instance.get_summary.assert_called_once_with(
        test_ids=[147162],
        group_by=["course"],
        fields=["accuracy"],
    )


@pytest.mark.unit
@pytest.mark.asyncio
async def test_converts_exception_to_tool_error() -> None:
    """Verify unexpected exceptions are converted to ToolError."""
    mock_ctx = _make_mock_ctx()

    with patch("testio_mcp.tools.custom_report_summary_tool.CustomReportService") as MockService:
        mock_service_instance = AsyncMock()
        mock_service_instance.get_summary.side_effect = RuntimeError("DB connection lost")
        MockService.return_value = mock_service_instance

        with pytest.raises(ToolError, match="Unexpected error"):
            await get_custom_report_summary(ctx=mock_ctx, test_ids=[147162])


@pytest.mark.unit
@pytest.mark.asyncio
async def test_parses_string_test_ids() -> None:
    """Verify string test_ids are parsed correctly."""
    mock_ctx = _make_mock_ctx()

    expected_result = {"test_ids": [147162, 147163], "total_reports": 0, "field_summary": {}}

    with patch("testio_mcp.tools.custom_report_summary_tool.CustomReportService") as MockService:
        mock_service_instance = AsyncMock()
        mock_service_instance.get_summary.return_value = expected_result
        MockService.return_value = mock_service_instance

        result = await get_custom_report_summary(
            ctx=mock_ctx,
            test_ids="147162,147163",
        )

    assert result == expected_result
    mock_service_instance.get_summary.assert_called_once_with(
        test_ids=[147162, 147163],
        group_by=None,
        fields=None,
    )
