"""Tests for MCP assertion/wait tools — TDD Red phase."""

import asyncio
import json
import sys
from pathlib import Path

import pytest

APPS_DIR = Path(__file__).parent / "apps"
sys.path.insert(0, str(APPS_DIR))


@pytest.fixture
async def server():
    """Create a fresh MCP server + SessionManager with assertion tools registered."""
    from mcp.server.fastmcp import FastMCP

    from textual_mcp.server import _register_tools
    from textual_mcp.session_manager import SessionManager

    mcp_server = FastMCP("textual_mcp_test")
    manager = SessionManager()
    _register_tools(mcp_server, manager)

    try:
        from textual_mcp.server import _register_assertion_tools

        _register_assertion_tools(mcp_server, manager)
    except ImportError:
        pass

    yield mcp_server, manager

    for sid in list(manager._sessions.keys()):
        try:
            await manager.remove_session(sid)
        except Exception:
            pass


async def _call(server_fixture, tool_name: str, args: dict) -> dict:
    """Helper: call a tool and parse the JSON response."""
    mcp, _ = server_fixture
    content_list, _raw = await mcp.call_tool(tool_name, args)
    return json.loads(content_list[0].text)


async def _launch(manager, app, session_id: str):
    """Launch an app directly into the session manager."""
    session = manager.create_session(session_id)
    await session.launch(app)
    return session


class TestTextualWaitFor:
    """Tests 10.1–10.5: textual_wait_for tool."""

    async def test_wait_for_idle(self, server):
        """Test 10.1: textual_wait_for condition='idle' waits for message processing."""
        from simple_app import SimpleApp

        _, manager = server
        await _launch(manager, SimpleApp(), "test-idle")

        result = await _call(server, "textual_wait_for", {
            "session_id": "test-idle",
            "condition": "idle",
        })
        assert result["met"] is True
        assert "elapsed" in result

    async def test_wait_for_animation(self, server):
        """Test 10.2: textual_wait_for condition='animation' waits for animations."""
        from simple_app import SimpleApp

        _, manager = server
        await _launch(manager, SimpleApp(), "test-anim")

        result = await _call(server, "textual_wait_for", {
            "session_id": "test-anim",
            "condition": "animation",
        })
        assert result["met"] is True
        assert "elapsed" in result

    async def test_wait_for_workers(self, server):
        """Test 10.3: textual_wait_for condition='workers' waits for worker completion."""
        from worker_app import WorkerApp

        _, manager = server
        session = await _launch(manager, WorkerApp(), "test-workers")

        # Start a worker (sleeps 0.1s)
        session.app.do_work()

        result = await _call(server, "textual_wait_for", {
            "session_id": "test-workers",
            "condition": "workers",
            "timeout": 5.0,
        })
        assert result["met"] is True

    async def test_wait_for_widget(self, server):
        """Test 10.4: textual_wait_for condition='widget' with selector waits for widget."""
        from simple_app import SimpleApp

        _, manager = server
        await _launch(manager, SimpleApp(), "test-widget")

        result = await _call(server, "textual_wait_for", {
            "session_id": "test-widget",
            "condition": "widget",
            "selector": "#greeting",
        })
        assert result["met"] is True

    async def test_wait_for_timeout(self, server):
        """Test 10.5: textual_wait_for with timeout returns met=false on timeout."""
        from simple_app import SimpleApp

        _, manager = server
        await _launch(manager, SimpleApp(), "test-timeout")

        result = await _call(server, "textual_wait_for", {
            "session_id": "test-timeout",
            "condition": "widget",
            "selector": "#nonexistent-widget",
            "timeout": 0.5,
        })
        assert result["met"] is False
        assert result["elapsed"] >= 0.4


class TestTextualCheckErrors:
    """Tests 10.6–10.10: textual_check_errors tool."""

    async def test_check_errors_empty(self, server):
        """Test 10.6: textual_check_errors returns empty when no errors."""
        from simple_app import SimpleApp

        _, manager = server
        await _launch(manager, SimpleApp(), "test-no-err")

        result = await _call(server, "textual_check_errors", {
            "session_id": "test-no-err",
        })
        assert result["errors"] == []
        assert result["app_exception"] is None

    async def test_check_errors_worker_error(self, server):
        """Test 10.7: textual_check_errors returns worker errors after failure."""
        from worker_app import WorkerApp

        _, manager = server
        session = await _launch(manager, WorkerApp(), "test-werr")

        # Trigger the failing worker
        session.app.do_failing_work()
        await asyncio.sleep(0.3)
        await session.pilot.pause()

        result = await _call(server, "textual_check_errors", {
            "session_id": "test-werr",
        })
        assert len(result["errors"]) > 0
        assert result["errors"][0]["type"] == "worker_error"
        assert "Worker failed" in result["errors"][0]["error"]

    async def test_check_errors_app_exception(self, server):
        """Test 10.8: textual_check_errors returns app_exception when app crashes."""
        from error_app import ErrorApp

        _, manager = server
        session = await _launch(manager, ErrorApp(), "test-crash")

        # Click the crash button to trigger RuntimeError
        await session.pilot.click("#crash-btn")
        await session.pilot.pause()

        result = await _call(server, "textual_check_errors", {
            "session_id": "test-crash",
        })
        assert result["app_exception"] is not None
        assert result["app_exception"]["type"] == "RuntimeError"
        assert "Intentional crash" in result["app_exception"]["message"]

    async def test_check_errors_clear_true(self, server):
        """Test 10.9: textual_check_errors with clear=true clears errors."""
        from worker_app import WorkerApp

        _, manager = server
        session = await _launch(manager, WorkerApp(), "test-clear")

        # Trigger the failing worker
        session.app.do_failing_work()
        await asyncio.sleep(0.3)
        await session.pilot.pause()

        # First call with clear=true (default)
        result1 = await _call(server, "textual_check_errors", {
            "session_id": "test-clear",
            "clear": True,
        })
        assert len(result1["errors"]) > 0

        # Second call — errors should be cleared
        result2 = await _call(server, "textual_check_errors", {
            "session_id": "test-clear",
        })
        assert result2["errors"] == []

    async def test_check_errors_clear_false(self, server):
        """Test 10.10: textual_check_errors with clear=false preserves errors."""
        from worker_app import WorkerApp

        _, manager = server
        session = await _launch(manager, WorkerApp(), "test-preserve")

        # Trigger the failing worker
        session.app.do_failing_work()
        await asyncio.sleep(0.3)
        await session.pilot.pause()

        # First call with clear=false
        result1 = await _call(server, "textual_check_errors", {
            "session_id": "test-preserve",
            "clear": False,
        })
        assert len(result1["errors"]) > 0

        # Second call — errors should still be there
        result2 = await _call(server, "textual_check_errors", {
            "session_id": "test-preserve",
            "clear": False,
        })
        assert len(result2["errors"]) > 0
        assert result2["errors"] == result1["errors"]
