"""Tests for IF mode integration with player model and server shell."""

from unittest.mock import AsyncMock, MagicMock

import pytest

from mudlib.if_session import IFResponse, IFSession
from mudlib.player import Player


@pytest.fixture
def mock_writer():
    writer = MagicMock()
    writer.write = MagicMock()
    writer.drain = AsyncMock()
    return writer


@pytest.fixture
def mock_reader():
    return MagicMock()


@pytest.fixture
def player(mock_reader, mock_writer):
    return Player(name="TestPlayer", x=5, y=5, reader=mock_reader, writer=mock_writer)


def test_player_has_if_session_attribute(player):
    """Player has if_session attribute that defaults to None."""
    assert hasattr(player, "if_session")
    assert player.if_session is None


def test_player_if_session_can_be_set(player):
    """Player.if_session can be set to an IFSession instance."""
    session = IFSession(player, "/path/to/story.z5", "story")
    player.if_session = session
    assert player.if_session == session


@pytest.mark.asyncio
async def test_if_mode_routes_input_to_if_session(player):
    """Input routes to if_session.handle_input in IF mode."""
    # Create a mock IF session
    mock_session = MagicMock()
    mock_session.handle_input = AsyncMock(
        return_value=IFResponse(output="You see a room.\r\n", done=False)
    )

    player.if_session = mock_session
    player.mode_stack.append("if")

    # Simulate input routing (what shell loop should do)
    if player.mode == "if" and player.if_session:
        response = await player.if_session.handle_input("look")
        assert response.output == "You see a room.\r\n"
        assert response.done is False

    mock_session.handle_input.assert_called_once_with("look")


@pytest.mark.asyncio
async def test_if_mode_done_clears_session_and_pops_mode(player):
    """When handle_input returns done=True, mode pops and if_session is cleared."""
    # Create a mock IF session that returns done=True
    mock_session = MagicMock()
    mock_session.handle_input = AsyncMock(
        return_value=IFResponse(output="Goodbye.\r\n", done=True)
    )
    mock_session.stop = AsyncMock()

    player.if_session = mock_session
    player.mode_stack.append("if")

    # Simulate shell loop handling done response
    if player.mode == "if" and player.if_session:
        response = await player.if_session.handle_input("::quit")
        if response.done:
            await player.if_session.stop()
            player.if_session = None
            player.mode_stack.pop()

    assert player.mode == "normal"
    assert player.if_session is None
    mock_session.stop.assert_called_once()


@pytest.mark.asyncio
async def test_mode_stack_push_and_pop_for_if(player):
    """Test mode stack mechanics for IF mode."""
    assert player.mode_stack == ["normal"]
    assert player.mode == "normal"

    # Enter IF mode
    player.mode_stack.append("if")
    assert player.mode == "if"
    assert player.mode_stack == ["normal", "if"]

    # Exit IF mode
    player.mode_stack.pop()
    assert player.mode == "normal"
    assert player.mode_stack == ["normal"]


@pytest.mark.asyncio
async def test_empty_input_allowed_in_if_mode(player):
    """Empty input is allowed in IF mode."""
    # Create a mock IF session
    mock_session = MagicMock()
    mock_session.handle_input = AsyncMock(
        return_value=IFResponse(output="Time passes.\r\n", done=False)
    )

    player.if_session = mock_session
    player.mode_stack.append("if")

    # Empty input should still be passed to IF session
    response = await player.if_session.handle_input("")
    assert response.output == "Time passes.\r\n"
    mock_session.handle_input.assert_called_once_with("")
