from unittest.mock import AsyncMock, MagicMock

import pytest

from mudlib.container import Container
from mudlib.player import Player
from mudlib.thing import Thing
from mudlib.zone import Zone


@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,
        terrain=terrain,
    )


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


@pytest.mark.asyncio
async def test_unlock_handler_on_non_container(player, test_zone, mock_writer):
    """unlock_handler on non-container gives appropriate error"""
    from mudlib.verb_handlers import unlock_handler

    thing = Thing(
        name="rock",
        description="a rock",
        location=test_zone,
        x=5,
        y=5,
    )

    await unlock_handler(thing, player, "")

    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "can't be unlocked" in output


@pytest.mark.asyncio
async def test_unlock_handler_on_unlocked_container(player, test_zone, mock_writer):
    """unlock_handler on unlocked container says it's not locked"""
    from mudlib.verb_handlers import unlock_handler

    chest = Container(
        name="chest",
        description="a wooden chest",
        location=test_zone,
        x=5,
        y=5,
        closed=True,
        locked=False,
    )

    await unlock_handler(chest, player, "")

    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "not locked" in output


@pytest.mark.asyncio
async def test_unlock_handler_without_key(player, test_zone, mock_writer):
    """unlock_handler on locked container without key in inventory"""
    from mudlib.verb_handlers import unlock_handler

    chest = Container(
        name="chest",
        description="a wooden chest",
        location=test_zone,
        x=5,
        y=5,
        closed=True,
        locked=True,
    )

    await unlock_handler(chest, player, "")

    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "don't have a key" in output


@pytest.mark.asyncio
async def test_unlock_handler_with_key(player, test_zone, mock_writer):
    """unlock_handler unlocks and opens chest with key in inventory"""
    from mudlib.verb_handlers import unlock_handler

    chest = Container(
        name="chest",
        description="a wooden chest",
        location=test_zone,
        x=5,
        y=5,
        closed=True,
        locked=True,
    )

    key = Thing(
        name="key",
        description="a brass key",
        location=player,
    )
    assert key in player.contents

    await unlock_handler(chest, player, "")

    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "You unlock the chest with the key" in output
    assert not chest.locked
    assert not chest.closed


@pytest.mark.asyncio
async def test_unlock_handler_key_found_by_alias(player, test_zone, mock_writer):
    """unlock_handler finds key even if it has a different name"""
    from mudlib.verb_handlers import unlock_handler

    chest = Container(
        name="chest",
        description="a wooden chest",
        location=test_zone,
        x=5,
        y=5,
        closed=True,
        locked=True,
    )

    key = Thing(
        name="brass key",
        description="a brass key",
        location=player,
    )
    key.aliases = ["key"]

    await unlock_handler(chest, player, "")

    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "You unlock the chest with the brass key" in output
    assert not chest.locked
    assert not chest.closed


@pytest.mark.asyncio
async def test_unlock_handler_preserves_chest_state(player, test_zone, mock_writer):
    """unlock_handler changes locked and closed but preserves other state"""
    from mudlib.verb_handlers import unlock_handler

    chest = Container(
        name="chest",
        description="a wooden chest",
        location=test_zone,
        x=5,
        y=5,
        closed=True,
        locked=True,
    )

    inner_thing = Thing(
        name="coin",
        description="a gold coin",
        location=chest,
    )

    key = Thing(
        name="key",
        description="a brass key",
        location=player,
    )
    assert key in player.contents

    await unlock_handler(chest, player, "")

    assert not chest.locked
    assert not chest.closed
    assert inner_thing in chest.contents
    assert chest.location == test_zone
