import pytest

from .test_helpers import DummyContext, DummyMCP


def setup_console_tools():
    """Setup console-related tools for testing."""
    mcp = DummyMCP()
    import services.tools.read_console
    from services.registry import get_registered_tools
    for tool_info in get_registered_tools():
        tool_name = tool_info['name']
        if any(keyword in tool_name for keyword in ['read_console', 'console']):
            mcp.tools[tool_name] = tool_info['func']
    return mcp.tools


@pytest.mark.asyncio
async def test_read_console_full_default(monkeypatch):
    tools = setup_console_tools()
    read_console = tools["read_console"]

    captured = {}

    async def fake_send(_cmd, params, **_kwargs):
        captured["params"] = params
        return {
            "success": True,
            "data": {"lines": [{"level": "error", "message": "oops", "stacktrace": "trace", "time": "t"}]},
        }

    # Patch the send_command_with_retry function in the tools module
    import services.tools.read_console
    monkeypatch.setattr(
        services.tools.read_console,
        "async_send_command_with_retry",
        fake_send,
    )

    resp = await read_console(ctx=DummyContext(), action="get", count=10)
    assert resp == {
        "success": True,
        "data": {"lines": [{"level": "error", "message": "oops", "time": "t"}]},
    }
    assert captured["params"]["count"] == 10
    assert captured["params"]["includeStacktrace"] is False


@pytest.mark.asyncio
async def test_read_console_truncated(monkeypatch):
    tools = setup_console_tools()
    read_console = tools["read_console"]

    captured = {}

    async def fake_send(_cmd, params, **_kwargs):
        captured["params"] = params
        return {
            "success": True,
            "data": {"lines": [{"level": "error", "message": "oops", "stacktrace": "trace"}]},
        }

    # Patch the send_command_with_retry function in the tools module
    import services.tools.read_console
    monkeypatch.setattr(
        services.tools.read_console,
        "async_send_command_with_retry",
        fake_send,
    )

    resp = await read_console(ctx=DummyContext(), action="get", count=10, include_stacktrace=False)
    assert resp == {"success": True, "data": {
        "lines": [{"level": "error", "message": "oops"}]}}
    assert captured["params"]["includeStacktrace"] is False


@pytest.mark.asyncio
async def test_read_console_default_count(monkeypatch):
    """Test that read_console defaults to count=10 when not specified."""
    tools = setup_console_tools()
    read_console = tools["read_console"]

    captured = {}

    async def fake_send(_cmd, params, **_kwargs):
        captured["params"] = params
        return {
            "success": True,
            "data": {"lines": [{"level": "error", "message": f"error {i}"} for i in range(15)]},
        }

    # Patch the send_command_with_retry function in the tools module
    import services.tools.read_console
    monkeypatch.setattr(
        services.tools.read_console,
        "async_send_command_with_retry",
        fake_send,
    )

    # Call without specifying count - should default to 10
    resp = await read_console(ctx=DummyContext(), action="get")
    assert resp["success"] is True
    # Verify that the default count of 10 was used
    assert captured["params"]["count"] == 10


@pytest.mark.asyncio
async def test_read_console_paging(monkeypatch):
    """Test that read_console paging works with page_size and cursor."""
    tools = setup_console_tools()
    read_console = tools["read_console"]

    captured = {}

    async def fake_send(_cmd, params, **_kwargs):
        captured["params"] = params
        # Simulate Unity returning paging info matching C# structure
        page_size = params.get("pageSize", 10)
        cursor = params.get("cursor", 0)
        # Simulate 25 total messages
        all_messages = [{"level": "error", "message": f"error {i}"} for i in range(25)]
        
        # Return a page of results
        start = cursor
        end = min(start + page_size, len(all_messages))
        messages = all_messages[start:end]
        
        return {
            "success": True,
            "data": {
                "items": messages,
                "cursor": cursor,
                "pageSize": page_size,
                "nextCursor": str(end) if end < len(all_messages) else None,
                "truncated": end < len(all_messages),
                "total": len(all_messages),
            },
        }

    # Patch the send_command_with_retry function in the tools module
    import services.tools.read_console
    monkeypatch.setattr(
        services.tools.read_console,
        "async_send_command_with_retry",
        fake_send,
    )

    # First page - get first 5 entries
    resp = await read_console(ctx=DummyContext(), action="get", page_size=5, cursor=0)
    assert resp["success"] is True
    assert captured["params"]["pageSize"] == 5
    assert captured["params"]["cursor"] == 0
    assert len(resp["data"]["items"]) == 5
    assert resp["data"]["truncated"] is True
    assert resp["data"]["nextCursor"] == "5"
    assert resp["data"]["total"] == 25
    
    # Second page - get next 5 entries
    resp = await read_console(ctx=DummyContext(), action="get", page_size=5, cursor=5)
    assert resp["success"] is True
    assert captured["params"]["cursor"] == 5
    assert len(resp["data"]["items"]) == 5
    assert resp["data"]["truncated"] is True
    assert resp["data"]["nextCursor"] == "10"
    
    # Last page - get remaining entries
    resp = await read_console(ctx=DummyContext(), action="get", page_size=5, cursor=20)
    assert resp["success"] is True
    assert len(resp["data"]["items"]) == 5
    assert resp["data"]["truncated"] is False
    assert resp["data"]["nextCursor"] is None


@pytest.mark.asyncio
async def test_read_console_types_json_string(monkeypatch):
    """Test that read_console handles types parameter as JSON string (fixes issue #561)."""
    tools = setup_console_tools()
    read_console = tools["read_console"]

    captured = {}

    async def fake_send_with_unity_instance(_send_fn, _unity_instance, _command_type, params, **_kwargs):
        captured["params"] = params
        return {
            "success": True,
            "data": {"lines": [{"level": "error", "message": "test error"}]},
        }

    import services.tools.read_console as read_console_mod
    monkeypatch.setattr(
        read_console_mod,
        "send_with_unity_instance",
        fake_send_with_unity_instance,
    )

    # Test with types as JSON string (the problematic case from issue #561)
    resp = await read_console(ctx=DummyContext(), action="get", types='["error", "warning", "all"]')
    assert resp["success"] is True
    # Verify types was parsed correctly and sent as a list
    assert isinstance(captured["params"]["types"], list)
    assert captured["params"]["types"] == ["error", "warning", "all"]
    
    # Test case normalization to lowercase
    captured.clear()
    resp = await read_console(ctx=DummyContext(), action="get", types='["ERROR", "Warning", "LOG"]')
    assert resp["success"] is True
    assert captured["params"]["types"] == ["error", "warning", "log"]

    # Test with types as actual list (should still work)
    captured.clear()
    resp = await read_console(ctx=DummyContext(), action="get", types=["error", "warning"])
    assert resp["success"] is True
    assert isinstance(captured["params"]["types"], list)
    assert captured["params"]["types"] == ["error", "warning"]


@pytest.mark.asyncio
async def test_read_console_types_validation(monkeypatch):
    """Test that read_console validates types entries and rejects invalid values."""
    tools = setup_console_tools()
    read_console = tools["read_console"]

    captured = {}

    async def fake_send_with_unity_instance(_send_fn, _unity_instance, _command_type, params, **_kwargs):
        captured["params"] = params
        return {"success": True, "data": {"lines": []}}

    import services.tools.read_console as read_console_mod
    monkeypatch.setattr(
        read_console_mod,
        "send_with_unity_instance",
        fake_send_with_unity_instance,
    )

    # Invalid entry in list should return a clear error and not send.
    captured.clear()
    resp = await read_console(ctx=DummyContext(), action="get", types='["error", "nope"]')
    assert resp["success"] is False
    assert "invalid types entry" in resp["message"]
    assert captured == {}

    # Non-string entry should return a clear error and not send.
    captured.clear()
    resp = await read_console(ctx=DummyContext(), action="get", types='[1, "error"]')
    assert resp["success"] is False
    assert "types entries must be strings" in resp["message"]
    assert captured == {}