"""Tests for textual_mcp.session — TDD Red phase."""

from pathlib import Path

import pytest
from textual.app import App
from textual.pilot import Pilot

APPS_DIR = Path(__file__).parent / "apps"


@pytest.fixture
def simple_app():
    from textual_mcp.app_loader import load_app
    return load_app(str(APPS_DIR / "simple_app.py"))


@pytest.fixture
def session():
    from textual_mcp.session import AppSession
    return AppSession(session_id="test-session-1")


class TestAppSessionLaunch:
    """Test launching apps headlessly."""

    @pytest.mark.asyncio
    async def test_launch_sets_is_running(self, session, simple_app):
        """Test 2.1: session.launch(app) starts app headlessly, is_running == True."""
        await session.launch(simple_app)
        try:
            assert session.is_running is True
        finally:
            await session.stop()

    @pytest.mark.asyncio
    async def test_pilot_is_pilot_instance(self, session, simple_app):
        """Test 2.2: session.pilot is a Pilot instance after launch."""
        await session.launch(simple_app)
        try:
            assert isinstance(session.pilot, Pilot)
        finally:
            await session.stop()

    @pytest.mark.asyncio
    async def test_app_is_app_instance(self, session, simple_app):
        """Test 2.3: session.app is the App instance after launch."""
        await session.launch(simple_app)
        try:
            assert isinstance(session.app, App)
            assert type(session.app).__name__ == "SimpleApp"
        finally:
            await session.stop()

    @pytest.mark.asyncio
    async def test_launch_with_custom_size(self, session, simple_app):
        """Test 2.5: session.launch() with custom size sets terminal dimensions."""
        await session.launch(simple_app, size=(120, 40))
        try:
            assert session.app.size.width == 120
            assert session.app.size.height == 40
        finally:
            await session.stop()


class TestAppSessionStop:
    """Test stopping app sessions."""

    @pytest.mark.asyncio
    async def test_stop_sets_not_running(self, session, simple_app):
        """Test 2.4: session.stop() shuts down cleanly, is_running == False."""
        await session.launch(simple_app)
        await session.stop()
        assert session.is_running is False

    @pytest.mark.asyncio
    async def test_stop_idempotent(self, session, simple_app):
        """Test 2.6: session.stop() on already-stopped session is a no-op."""
        await session.launch(simple_app)
        await session.stop()
        assert session.is_running is False
        # Second stop should not raise
        await session.stop()
        assert session.is_running is False


class TestAppSessionErrors:
    """Test error collection during app operation."""

    @pytest.mark.asyncio
    async def test_launch_captures_exceptions(self, session):
        """Test 2.7: session.launch() captures app exceptions in error_collector."""
        from textual_mcp.app_loader import load_app

        error_app = load_app(str(APPS_DIR / "error_app.py"))
        await session.launch(error_app)
        try:
            # error_app should produce errors that error_collector captures
            assert session.error_collector is not None
        finally:
            await session.stop()


class TestAppSessionIdentity:
    """Test session identity preservation."""

    @pytest.mark.asyncio
    async def test_session_id_preserved(self, session, simple_app):
        """Test 2.8: session preserves session_id throughout lifecycle."""
        assert session.session_id == "test-session-1"
        await session.launch(simple_app)
        assert session.session_id == "test-session-1"
        await session.stop()
        assert session.session_id == "test-session-1"


class TestAppSessionMessageHook:
    """Test message hook integration."""

    @pytest.mark.asyncio
    async def test_message_hook_receives_messages(self, session, simple_app):
        """Test 2.9: message_hook receives messages during app operation."""
        await session.launch(simple_app)
        try:
            # Interact to generate messages
            await session.pilot.pause()
            assert session.error_collector.messages_seen > 0
        finally:
            await session.stop()
