"""Tests for auto-triggering portals on movement."""

from unittest.mock import AsyncMock, MagicMock

import pytest

from mudlib.player import Player
from mudlib.portal import Portal
from mudlib.zone import Zone
from mudlib.zones import register_zone, zone_registry


@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 test_zone():
    terrain = [["." for _ in range(10)] for _ in range(10)]
    return Zone(
        name="testzone",
        width=10,
        height=10,
        toroidal=True,
        terrain=terrain,
    )


@pytest.fixture
def target_zone():
    terrain = [["." for _ in range(10)] for _ in range(10)]
    return Zone(
        name="targetzone",
        width=10,
        height=10,
        toroidal=True,
        terrain=terrain,
    )


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


@pytest.fixture(autouse=True)
def clear_zones():
    """Clear zone registry before and after each test."""
    zone_registry.clear()
    yield
    zone_registry.clear()


@pytest.mark.asyncio
async def test_move_onto_portal_triggers_zone_transition(
    player, test_zone, target_zone
):
    """Walking onto a portal tile auto-triggers zone transition."""
    from mudlib.commands.movement import move_player

    register_zone("targetzone", target_zone)
    # Place portal at (6, 5), one tile east of player
    Portal(
        name="shimmering doorway",
        location=test_zone,
        x=6,
        y=5,
        target_zone="targetzone",
        target_x=3,
        target_y=7,
    )

    # Move east onto the portal
    await move_player(player, 1, 0, "east")

    # Player should end up in target zone at portal's target coords
    assert player.location is target_zone
    assert player.x == 3
    assert player.y == 7
    assert player in target_zone.contents
    assert player not in test_zone.contents


@pytest.mark.asyncio
async def test_move_onto_portal_sends_transition_message(
    player, test_zone, target_zone, mock_writer
):
    """Auto-triggered portal shows transition message to player."""
    from mudlib.commands.movement import move_player

    register_zone("targetzone", target_zone)
    Portal(
        name="mystic portal",
        location=test_zone,
        x=6,
        y=5,
        target_zone="targetzone",
        target_x=2,
        target_y=3,
    )

    await move_player(player, 1, 0, "east")

    # Check that player got a transition message
    output = "".join([call[0][0] for call in mock_writer.write.call_args_list])
    assert "enter" in output.lower() or "portal" in output.lower()


@pytest.mark.asyncio
async def test_move_onto_tile_without_portal_normal_movement(
    player, test_zone, mock_writer
):
    """Normal movement still works when no portal present."""
    from mudlib.commands.movement import move_player

    initial_x = player.x
    initial_y = player.y

    # Move east to empty tile
    await move_player(player, 1, 0, "east")

    # Player should still be in test zone with updated position
    assert player.location is test_zone
    assert player.x == initial_x + 1
    assert player.y == initial_y
    assert player in test_zone.contents

    # Should see look output (viewport with @ symbol)
    output = "".join([call[0][0] for call in mock_writer.write.call_args_list])
    assert "@" in output  # Player's position marker in viewport


@pytest.mark.asyncio
async def test_portal_autotrigger_target_zone_not_found(player, test_zone, mock_writer):
    """If target zone not registered, player stays and gets error."""
    from mudlib.commands.movement import move_player

    # Create portal with invalid target zone (not registered)
    Portal(
        name="broken portal",
        location=test_zone,
        x=6,
        y=5,
        target_zone="nonexistent",
        target_x=3,
        target_y=7,
    )

    await move_player(player, 1, 0, "east")

    # Player should stay in original zone at portal tile
    assert player.location is test_zone
    assert player.x == 6
    assert player.y == 5

    # Should see error message
    output = "".join([call[0][0] for call in mock_writer.write.call_args_list])
    assert "doesn't lead anywhere" in output.lower() or "nowhere" in output.lower()
