"""Tests for open and close commands."""

from unittest.mock import AsyncMock, MagicMock

import pytest

from mudlib.commands import _registry
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,
        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


# --- cmd_open ---


@pytest.mark.asyncio
async def test_open_container_on_ground(player, test_zone, mock_writer):
    """open finds container on ground and sets closed=False."""
    from mudlib.commands.containers import cmd_open

    chest = Container(name="chest", location=test_zone, x=5, y=5, closed=True)
    await cmd_open(player, "chest")
    assert chest.closed is False
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "open" in output.lower() and "chest" in output.lower()


@pytest.mark.asyncio
async def test_open_container_in_inventory(player, test_zone, mock_writer):
    """open finds container in player inventory and sets closed=False."""
    from mudlib.commands.containers import cmd_open

    box = Container(name="box", location=player, closed=True)
    await cmd_open(player, "box")
    assert box.closed is False
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "open" in output.lower() and "box" in output.lower()


@pytest.mark.asyncio
async def test_open_no_args(player, mock_writer):
    """open with no arguments gives usage hint."""
    from mudlib.commands.containers import cmd_open

    await cmd_open(player, "")
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "open what" in output.lower() or "what" in output.lower()


@pytest.mark.asyncio
async def test_open_already_open(player, test_zone, mock_writer):
    """open on already-open container gives feedback."""
    from mudlib.commands.containers import cmd_open

    _chest = Container(name="chest", location=test_zone, x=5, y=5, closed=False)
    await cmd_open(player, "chest")
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "already open" in output.lower()


@pytest.mark.asyncio
async def test_open_locked_container(player, test_zone, mock_writer):
    """open on locked container gives feedback."""
    from mudlib.commands.containers import cmd_open

    chest = Container(
        name="chest", location=test_zone, x=5, y=5, closed=True, locked=True
    )
    await cmd_open(player, "chest")
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "locked" in output.lower()
    # Container should still be closed
    assert chest.closed is True


@pytest.mark.asyncio
async def test_open_not_found(player, test_zone, mock_writer):
    """open on non-existent thing gives feedback."""
    from mudlib.commands.containers import cmd_open

    await cmd_open(player, "chest")
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "don't see" in output.lower()


@pytest.mark.asyncio
async def test_open_matches_aliases(player, test_zone):
    """open matches container aliases."""
    from mudlib.commands.containers import cmd_open

    chest = Container(
        name="wooden chest",
        aliases=["chest", "box"],
        location=test_zone,
        x=5,
        y=5,
        closed=True,
    )
    await cmd_open(player, "box")
    assert chest.closed is False


@pytest.mark.asyncio
async def test_open_non_container(player, test_zone, mock_writer):
    """open on non-container thing gives feedback."""
    from mudlib.commands.containers import cmd_open

    _rock = Thing(name="rock", location=test_zone, x=5, y=5)
    await cmd_open(player, "rock")
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "can't open" in output.lower()


# --- cmd_close ---


@pytest.mark.asyncio
async def test_close_container_on_ground(player, test_zone, mock_writer):
    """close finds container on ground and sets closed=True."""
    from mudlib.commands.containers import cmd_close

    chest = Container(name="chest", location=test_zone, x=5, y=5, closed=False)
    await cmd_close(player, "chest")
    assert chest.closed is True
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "close" in output.lower() and "chest" in output.lower()


@pytest.mark.asyncio
async def test_close_container_in_inventory(player, test_zone, mock_writer):
    """close finds container in inventory and sets closed=True."""
    from mudlib.commands.containers import cmd_close

    box = Container(name="box", location=player, closed=False)
    await cmd_close(player, "box")
    assert box.closed is True
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "close" in output.lower() and "box" in output.lower()


@pytest.mark.asyncio
async def test_close_already_closed(player, test_zone, mock_writer):
    """close on already-closed container gives feedback."""
    from mudlib.commands.containers import cmd_close

    _chest = Container(name="chest", location=test_zone, x=5, y=5, closed=True)
    await cmd_close(player, "chest")
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "already closed" in output.lower()


@pytest.mark.asyncio
async def test_close_no_args(player, mock_writer):
    """close with no arguments gives usage hint."""
    from mudlib.commands.containers import cmd_close

    await cmd_close(player, "")
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "close what" in output.lower() or "what" in output.lower()


@pytest.mark.asyncio
async def test_close_non_container(player, test_zone, mock_writer):
    """close on non-container thing gives feedback."""
    from mudlib.commands.containers import cmd_close

    _rock = Thing(name="rock", location=test_zone, x=5, y=5)
    await cmd_close(player, "rock")
    output = mock_writer.write.call_args_list[-1][0][0]
    assert "can't close" in output.lower()


# --- command registration ---


def test_open_command_registered():
    """open command is registered."""
    import mudlib.commands.containers  # noqa: F401

    assert "open" in _registry


def test_close_command_registered():
    """close command is registered."""
    import mudlib.commands.containers  # noqa: F401

    assert "close" in _registry
