"""Tests for paint mode - terrain editing admin tool."""

import pytest

from mudlib.player import Player
from mudlib.zone import Zone


@pytest.fixture
def player():
    """Create a test player with a mock writer."""
    mock_writer = MockWriter()
    player = Player(
        name="TestPlayer",
        x=5,
        y=5,
        writer=mock_writer,
    )
    # Create a simple zone for testing
    zone = Zone(
        "test_zone",
        width=20,
        height=20,
        terrain=[["." for _ in range(20)] for _ in range(20)],
        impassable={"^", "~", "#"},  # Add # as impassable
    )
    # Add a wall for passability testing
    zone.terrain[5][6] = "#"
    player.location = zone
    return player


class MockWriter:
    """Mock writer that captures output."""

    def __init__(self):
        self.messages = []
        # Mock option negotiation state
        from unittest.mock import MagicMock

        self.local_option = MagicMock()
        self.local_option.enabled = MagicMock(return_value=False)
        self.remote_option = MagicMock()
        self.remote_option.enabled = MagicMock(return_value=False)

    def write(self, message: str):
        self.messages.append(message)

    async def drain(self):
        pass

    def send_gmcp(self, package: str, data: dict):
        """Mock GMCP send (no-op for paint mode tests)."""
        pass

    def send_msdp(self, data: dict):
        """Mock MSDP send (no-op for paint mode tests)."""
        pass

    def get_output(self) -> str:
        return "".join(self.messages)

    def clear(self):
        self.messages = []


@pytest.mark.asyncio
async def test_enter_paint_mode(player):
    """@paint command sets player.paint_mode = True and sends confirmation."""
    from mudlib.commands.paint import cmd_paint

    await cmd_paint(player, "")
    assert player.paint_mode is True
    output = player.writer.get_output()
    assert "paint mode" in output.lower()


@pytest.mark.asyncio
async def test_exit_paint_mode(player):
    """@paint when already in paint mode exits it."""
    from mudlib.commands.paint import cmd_paint

    # Enter paint mode first
    await cmd_paint(player, "")
    player.writer.clear()

    # Exit paint mode
    await cmd_paint(player, "")
    assert player.paint_mode is False
    output = player.writer.get_output()
    assert "exit" in output.lower() or "off" in output.lower()


@pytest.mark.asyncio
async def test_paint_mode_default_survey(player):
    """Entering paint mode starts in survey state (player.painting = False)."""
    from mudlib.commands.paint import cmd_paint

    await cmd_paint(player, "")
    assert player.paint_mode is True
    assert player.painting is False


@pytest.mark.asyncio
async def test_toggle_painting(player):
    """p command toggles player.painting between True and False."""
    from mudlib.commands.paint import cmd_toggle_painting

    # Must be in paint mode first
    player.paint_mode = True

    # Toggle to painting
    await cmd_toggle_painting(player, "")
    assert player.painting is True
    player.writer.clear()

    # Toggle back to survey
    await cmd_toggle_painting(player, "")
    assert player.painting is False


@pytest.mark.asyncio
async def test_toggle_painting_requires_paint_mode(player):
    """p command only works in paint mode."""
    from mudlib.commands.paint import cmd_toggle_painting

    player.paint_mode = False
    await cmd_toggle_painting(player, "")
    output = player.writer.get_output()
    assert "paint mode" in output.lower()


@pytest.mark.asyncio
async def test_set_brush(player):
    """Typing a single character while in paint mode sets player.paint_brush."""
    from mudlib.commands.paint import cmd_set_brush

    player.paint_mode = True

    await cmd_set_brush(player, "#")
    assert player.paint_brush == "#"

    await cmd_set_brush(player, "~")
    assert player.paint_brush == "~"


@pytest.mark.asyncio
async def test_set_brush_requires_paint_mode(player):
    """Brush command only works in paint mode."""
    from mudlib.commands.paint import cmd_set_brush

    player.paint_mode = False
    await cmd_set_brush(player, "#")
    output = player.writer.get_output()
    assert "paint mode" in output.lower()


@pytest.mark.asyncio
async def test_set_brush_requires_single_char(player):
    """Brush must be a single character."""
    from mudlib.commands.paint import cmd_set_brush

    player.paint_mode = True
    await cmd_set_brush(player, "##")
    output = player.writer.get_output()
    assert "single character" in output.lower()


@pytest.mark.asyncio
async def test_paint_mode_movement_ignores_passability(player):
    """Movement in paint mode doesn't check passability."""
    from mudlib.commands.movement import move_player

    # There's a wall at (6, 5) - normally can't move there
    player.paint_mode = True

    # Should be able to move into the wall
    await move_player(player, 1, 0, "east")
    assert player.x == 6
    assert player.y == 5


@pytest.mark.asyncio
async def test_painting_places_tile(player):
    """Moving while painting sets terrain tile at old position to brush char."""
    from mudlib.commands.movement import move_player

    player.paint_mode = True
    player.painting = True
    player.paint_brush = "~"

    # Move from (5,5) to (6,5)
    old_x, old_y = player.x, player.y
    await move_player(player, 1, 0, "east")

    # Check that the old position now has the brush character
    zone = player.location
    assert isinstance(zone, Zone)
    assert zone.terrain[old_y][old_x] == "~"


@pytest.mark.asyncio
async def test_painting_only_in_paint_mode(player):
    """Painting flag has no effect outside paint mode."""
    from mudlib.commands.movement import move_player

    player.paint_mode = False
    player.painting = True
    player.paint_brush = "~"

    # Try to move into wall at (6, 5)
    await move_player(player, 1, 0, "east")

    # Should have failed - player still at (5, 5)
    assert player.x == 5
    assert player.y == 5


@pytest.mark.asyncio
async def test_survey_mode_does_not_paint(player):
    """Survey mode (painting=False) allows movement but doesn't paint."""
    from mudlib.commands.movement import move_player

    player.paint_mode = True
    player.painting = False
    player.paint_brush = "~"

    old_x, old_y = player.x, player.y
    await move_player(player, 1, 0, "east")

    # Movement should have happened
    assert player.x == 6
    assert player.y == 5

    # But no painting should have occurred
    zone = player.location
    assert isinstance(zone, Zone)
    assert zone.terrain[old_y][old_x] == "."  # Still the original tile
