import asyncio
from unittest.mock import AsyncMock

import pytest

from windows_mcp.analytics import with_analytics


class TestWithAnalytics:
    async def test_success_path(self):
        mock_analytics = AsyncMock()

        @with_analytics(mock_analytics, "test_tool")
        async def my_tool():
            return "result"

        result = await my_tool()
        assert result == "result"
        mock_analytics.track_tool.assert_called_once()
        call_args = mock_analytics.track_tool.call_args
        assert call_args[0][0] == "test_tool"
        assert call_args[0][1]["success"] is True
        assert "duration_ms" in call_args[0][1]

    async def test_error_path(self):
        mock_analytics = AsyncMock()

        @with_analytics(mock_analytics, "test_tool")
        async def failing_tool():
            raise ValueError("something broke")

        with pytest.raises(ValueError, match="something broke"):
            await failing_tool()
        mock_analytics.track_error.assert_called_once()
        call_args = mock_analytics.track_error.call_args
        assert isinstance(call_args[0][0], ValueError)
        assert call_args[0][1]["tool_name"] == "test_tool"

    async def test_no_analytics_instance(self):
        @with_analytics(None, "test_tool")
        async def my_tool():
            return 42

        result = await my_tool()
        assert result == 42

    async def test_duration_measurement(self):
        mock_analytics = AsyncMock()

        @with_analytics(mock_analytics, "test_tool")
        async def slow_tool():
            await asyncio.sleep(0.1)
            return "done"

        await slow_tool()
        call_args = mock_analytics.track_tool.call_args
        duration_ms = call_args[0][1]["duration_ms"]
        assert duration_ms >= 50  # Should be ~100ms but allow margin

    async def test_error_still_tracks_duration(self):
        mock_analytics = AsyncMock()

        @with_analytics(mock_analytics, "test_tool")
        async def failing_tool():
            await asyncio.sleep(0.05)
            raise RuntimeError("fail")

        with pytest.raises(RuntimeError):
            await failing_tool()
        call_args = mock_analytics.track_error.call_args
        assert "duration_ms" in call_args[0][1]

    async def test_return_value_preserved(self):
        mock_analytics = AsyncMock()

        @with_analytics(mock_analytics, "test_tool")
        async def tool_with_complex_result():
            return {"key": "value", "count": 42}

        result = await tool_with_complex_result()
        assert result == {"key": "value", "count": 42}
