"""Tests for the describe command."""

from unittest.mock import AsyncMock, MagicMock

import pytest

from mudlib.commands.describe import cmd_describe
from mudlib.housing import init_housing
from mudlib.player import Player
from mudlib.zone import Zone
from mudlib.zones import register_zone, zone_registry


@pytest.fixture(autouse=True)
def _clean_registries():
    saved = dict(zone_registry)
    zone_registry.clear()
    yield
    zone_registry.clear()
    zone_registry.update(saved)


@pytest.fixture
def _init_housing(tmp_path):
    from mudlib.store import create_account, init_db

    init_housing(tmp_path / "player_zones")
    init_db(tmp_path / "test.db")
    # Create accounts for test players
    for name in ["alice", "bob", "charlie"]:
        create_account(name, "testpass")


def _make_home_zone(player_name="alice"):
    name = f"home:{player_name}"
    terrain = []
    for y in range(9):
        row = []
        for x in range(9):
            if x == 0 or x == 8 or y == 0 or y == 8:
                row.append("#")
            else:
                row.append(".")
        terrain.append(row)
    zone = Zone(
        name=name,
        description=f"{player_name}'s home",
        width=9,
        height=9,
        terrain=terrain,
        toroidal=False,
        impassable={"#", "^", "~"},
        spawn_x=4,
        spawn_y=4,
        safe=True,
    )
    register_zone(name, zone)
    return zone


def _make_player(name="alice", zone=None, x=4, y=4):
    mock_writer = MagicMock()
    mock_writer.write = MagicMock()
    mock_writer.drain = AsyncMock()
    p = Player(name=name, location=zone, x=x, y=y, writer=mock_writer)
    p.home_zone = f"home:{name}"
    return p


@pytest.mark.asyncio
async def test_describe_sets_description(_init_housing):
    """describe <text> sets zone.description."""
    home = _make_home_zone("alice")
    player = _make_player("alice", zone=home)

    await cmd_describe(player, "a cozy cottage by the sea")

    assert home.description == "a cozy cottage by the sea"


@pytest.mark.asyncio
async def test_describe_not_in_home_zone(_init_housing):
    """describe fails if not in player's own home zone."""
    _make_home_zone("alice")
    other_zone = Zone(
        name="overworld",
        description="The world",
        width=20,
        height=20,
        terrain=[["." for _ in range(20)] for _ in range(20)],
        toroidal=True,
    )
    register_zone("overworld", other_zone)
    player = _make_player("alice", zone=other_zone)

    await cmd_describe(player, "trying to describe the overworld")

    # Should show error message
    assert player.writer.write.called
    output = "".join(c[0][0] for c in player.writer.write.call_args_list)
    assert "only" in output.lower() and "home" in output.lower()


@pytest.mark.asyncio
async def test_describe_no_args_shows_current(_init_housing):
    """describe with no args shows current description."""
    home = _make_home_zone("alice")
    home.description = "a warm and welcoming place"
    player = _make_player("alice", zone=home)

    await cmd_describe(player, "")

    assert player.writer.write.called
    output = "".join(c[0][0] for c in player.writer.write.call_args_list)
    assert "a warm and welcoming place" in output


@pytest.mark.asyncio
async def test_describe_saves_zone(_init_housing):
    """describe saves the zone to disk."""
    import tomllib

    from mudlib.housing import _zone_path

    home = _make_home_zone("alice")
    player = _make_player("alice", zone=home)

    await cmd_describe(player, "a newly described home")

    # Check TOML file was saved
    zone_path = _zone_path("alice")
    assert zone_path.exists()

    with open(zone_path, "rb") as f:
        data = tomllib.load(f)

    assert data["description"] == "a newly described home"


@pytest.mark.asyncio
async def test_describe_multiword(_init_housing):
    """describe handles multiword descriptions."""
    home = _make_home_zone("bob")
    player = _make_player("bob", zone=home)

    await cmd_describe(player, "a cozy cottage with warm lighting")

    assert home.description == "a cozy cottage with warm lighting"
    assert player.writer.write.called
    output = "".join(c[0][0] for c in player.writer.write.call_args_list)
    assert "description" in output.lower() or "set" in output.lower()
