"""Tests for the Container class."""

import pytest

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

# --- fixtures ---


@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


# --- construction ---


# --- can_accept ---


def test_container_accepts_thing_when_open():
    """Container accepts Things when open and has capacity."""
    c = Container(name="chest")
    sword = Thing(name="sword")
    assert c.can_accept(sword) is True


def test_container_rejects_when_closed():
    """Container rejects Things when closed."""
    c = Container(name="chest", closed=True)
    sword = Thing(name="sword")
    assert c.can_accept(sword) is False


def test_container_rejects_when_at_capacity():
    """Container rejects Things when at capacity."""
    c = Container(name="pouch", capacity=2)
    # Add two items
    Thing(name="rock1", location=c)
    Thing(name="rock2", location=c)
    # Third should be rejected
    rock3 = Thing(name="rock3")
    assert c.can_accept(rock3) is False


def test_container_accepts_when_below_capacity():
    """Container accepts Things when below capacity."""
    c = Container(name="pouch", capacity=2)
    Thing(name="rock1", location=c)
    # Second item should be accepted
    rock2 = Thing(name="rock2")
    assert c.can_accept(rock2) is True


def test_container_rejects_non_thing():
    """Container rejects objects that aren't Things."""
    c = Container(name="chest")
    other = Object(name="abstract")
    assert c.can_accept(other) is False


def test_container_locked_is_just_flag():
    """Locked flag is stored but doesn't affect can_accept (used by commands)."""
    c = Container(name="chest", locked=True, closed=False)
    sword = Thing(name="sword")
    # can_accept doesn't check locked (commands will check it separately)
    # This test documents current behavior — locked is for command layer
    assert c.locked is True
    # can_accept only checks closed and capacity
    assert c.can_accept(sword) is True


# --- integration with zones ---


def test_container_in_zone():
    """Container can be placed in a zone with coordinates."""
    terrain = [["." for _ in range(10)] for _ in range(10)]
    zone = Zone(name="test", width=10, height=10, terrain=terrain)
    chest = Container(name="chest", location=zone, x=5, y=5)
    assert chest.location is zone
    assert chest.x == 5
    assert chest.y == 5
    assert chest in zone.contents


def test_container_with_contents():
    """Container can hold Things."""
    chest = Container(name="chest")
    sword = Thing(name="sword", location=chest)
    gem = Thing(name="gem", location=chest)
    assert sword in chest.contents
    assert gem in chest.contents
    assert len(chest.contents) == 2


# --- look command container display ---


@pytest.mark.asyncio
async def test_look_shows_closed_container(player, test_zone, mock_writer):
    """look shows closed containers with (closed) suffix."""
    from mudlib.commands.look import cmd_look

    Container(name="chest", location=test_zone, x=5, y=5, closed=True)
    await cmd_look(player, "")
    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "chest (closed)" in output


@pytest.mark.asyncio
async def test_look_shows_open_empty_container(player, test_zone, mock_writer):
    """look shows open empty containers with (open, empty) suffix."""
    from mudlib.commands.look import cmd_look

    Container(name="chest", location=test_zone, x=5, y=5, closed=False)
    await cmd_look(player, "")
    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "chest (open, empty)" in output


@pytest.mark.asyncio
async def test_look_shows_open_container_with_contents(player, test_zone, mock_writer):
    """look shows open containers with their contents."""
    from mudlib.commands.look import cmd_look

    chest = Container(name="chest", location=test_zone, x=5, y=5, closed=False)
    Thing(name="rock", location=chest)
    Thing(name="coin", location=chest)
    await cmd_look(player, "")
    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "chest (open, containing: rock, coin)" in output


@pytest.mark.asyncio
async def test_look_shows_regular_things_unchanged(player, test_zone, mock_writer):
    """look shows regular Things without container suffixes."""
    from mudlib.commands.look import cmd_look

    Thing(name="rock", location=test_zone, x=5, y=5)
    await cmd_look(player, "")
    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "On the ground: rock" in output
    assert "(closed)" not in output
    assert "(open" not in output


# --- inventory command container display ---


@pytest.mark.asyncio
async def test_inventory_shows_closed_container(player, mock_writer):
    """inventory shows closed containers with (closed) suffix."""
    from mudlib.commands.things import cmd_inventory

    Container(name="sack", location=player, closed=True)
    await cmd_inventory(player, "")
    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "sack (closed)" in output


@pytest.mark.asyncio
async def test_inventory_shows_open_empty_container(player, mock_writer):
    """inventory shows open empty containers with (open, empty) suffix."""
    from mudlib.commands.things import cmd_inventory

    Container(name="sack", location=player, closed=False)
    await cmd_inventory(player, "")
    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "sack (open, empty)" in output


@pytest.mark.asyncio
async def test_inventory_shows_container_with_contents(player, mock_writer):
    """inventory shows open containers with their contents."""
    from mudlib.commands.things import cmd_inventory

    sack = Container(name="sack", location=player, closed=False)
    Thing(name="rock", location=sack)
    Thing(name="gem", location=sack)
    await cmd_inventory(player, "")
    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "sack (open, containing: rock, gem)" in output


@pytest.mark.asyncio
async def test_inventory_shows_regular_things_unchanged(player, mock_writer):
    """inventory shows regular Things without container suffixes."""
    from mudlib.commands.things import cmd_inventory

    Thing(name="rock", location=player)
    await cmd_inventory(player, "")
    output = "".join(call[0][0] for call in mock_writer.write.call_args_list)
    assert "  rock\r\n" in output
    assert "(closed)" not in output
    assert "(open" not in output
