"""Tests for MCP Server lifecycle tools — TDD Red phase."""

import json
from pathlib import Path

import pytest

APPS_DIR = Path(__file__).parent / "apps"
SIMPLE_APP_PATH = str(APPS_DIR / "simple_app.py")


@pytest.fixture
def server():
    """Create a fresh MCP server + SessionManager for each test."""
    from mcp.server.fastmcp import FastMCP
    from mcp.types import ToolAnnotations

    from textual_mcp.app_loader import load_app
    from textual_mcp.session_manager import SessionManager

    mcp = FastMCP("textual_mcp_test")
    manager = SessionManager()

    # Re-register tools with this test's manager instance
    from textual_mcp.server import _register_tools
    _register_tools(mcp, manager)

    return mcp, manager


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)


class TestTextualLaunch:
    """Test the textual_launch tool."""

    @pytest.mark.asyncio
    async def test_launch_returns_session_id(self, server):
        """Test 7.1: textual_launch with valid app_path returns session_id."""
        result = await _call(server, "textual_launch", {"app_path": SIMPLE_APP_PATH})
        assert "session_id" in result
        assert "error" not in result
        # Clean up
        await _call(server, "textual_stop", {"session_id": result["session_id"]})

    @pytest.mark.asyncio
    async def test_launch_returns_app_metadata(self, server):
        """Test 7.1b: textual_launch returns app metadata."""
        result = await _call(server, "textual_launch", {"app_path": SIMPLE_APP_PATH})
        assert "app_title" in result
        assert "screen_name" in result
        await _call(server, "textual_stop", {"session_id": result["session_id"]})

    @pytest.mark.asyncio
    async def test_launch_invalid_path_returns_error(self, server):
        """Test 7.2: textual_launch with invalid app_path returns error."""
        result = await _call(server, "textual_launch", {"app_path": "/tmp/nonexistent_abc.py"})
        assert "error" in result

    @pytest.mark.asyncio
    async def test_launch_custom_size(self, server):
        """Test 7.3: textual_launch with custom size."""
        result = await _call(server, "textual_launch", {
            "app_path": SIMPLE_APP_PATH,
            "size": [100, 50],
        })
        assert "session_id" in result
        _, manager = server
        session = manager.get_session(result["session_id"])
        assert session.app.size.width == 100
        assert session.app.size.height == 50
        await _call(server, "textual_stop", {"session_id": result["session_id"]})

    @pytest.mark.asyncio
    async def test_launch_custom_session_id(self, server):
        """Test 7.4: textual_launch with custom session_id uses that ID."""
        result = await _call(server, "textual_launch", {
            "app_path": SIMPLE_APP_PATH,
            "session_id": "my-custom-id",
        })
        assert result["session_id"] == "my-custom-id"
        await _call(server, "textual_stop", {"session_id": "my-custom-id"})


class TestTextualStop:
    """Test the textual_stop tool."""

    @pytest.mark.asyncio
    async def test_stop_valid_session(self, server):
        """Test 7.5: textual_stop with valid session_id stops the app."""
        launch = await _call(server, "textual_launch", {"app_path": SIMPLE_APP_PATH})
        result = await _call(server, "textual_stop", {"session_id": launch["session_id"]})
        assert result["stopped"] is True

    @pytest.mark.asyncio
    async def test_stop_invalid_session_returns_error(self, server):
        """Test 7.6: textual_stop with invalid session_id returns error."""
        result = await _call(server, "textual_stop", {"session_id": "no-such-session"})
        assert "error" in result

    @pytest.mark.asyncio
    async def test_stop_returns_errors(self, server):
        """Test 7.7: textual_stop returns accumulated errors."""
        launch = await _call(server, "textual_launch", {"app_path": SIMPLE_APP_PATH})
        result = await _call(server, "textual_stop", {"session_id": launch["session_id"]})
        assert "errors" in result
        assert isinstance(result["errors"], list)


class TestToolDiscovery:
    """Test tool listing and annotations."""

    @pytest.mark.asyncio
    async def test_tool_list_includes_lifecycle_tools(self, server):
        """Test 7.8: tool list includes textual_launch and textual_stop."""
        mcp, _ = server
        tools = await mcp.list_tools()
        names = {t.name for t in tools}
        assert "textual_launch" in names
        assert "textual_stop" in names

    @pytest.mark.asyncio
    async def test_launch_annotations(self, server):
        """Test 7.9: textual_launch annotations."""
        mcp, _ = server
        tools = await mcp.list_tools()
        launch = next(t for t in tools if t.name == "textual_launch")
        assert launch.annotations is not None
        assert launch.annotations.destructiveHint is False
        assert launch.annotations.openWorldHint is False

    @pytest.mark.asyncio
    async def test_stop_annotations(self, server):
        """Test 7.10: textual_stop annotations: destructiveHint=true."""
        mcp, _ = server
        tools = await mcp.list_tools()
        stop = next(t for t in tools if t.name == "textual_stop")
        assert stop.annotations is not None
        assert stop.annotations.destructiveHint is True
