"""Tests for the home command."""

from unittest.mock import AsyncMock, MagicMock

import pytest

from mudlib.commands.home import cmd_home
from mudlib.housing import init_housing
from mudlib.player import Player
from mudlib.zone import Zone
from mudlib.zones import get_zone, register_zone, zone_registry


@pytest.fixture(autouse=True)
def _clean_zone_registry():
    saved = dict(zone_registry)
    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", "diane", "eve", "frank", "grace"]:
        create_account(name, "testpass")


def _make_zone(name="overworld", width=20, height=20):
    terrain = [["." for _ in range(width)] for _ in range(height)]
    zone = Zone(
        name=name,
        description=name,
        width=width,
        height=height,
        terrain=terrain,
        toroidal=True,
    )
    register_zone(name, zone)
    return zone


def _make_player(name="tester", zone=None, x=5, y=5):
    mock_writer = MagicMock()
    mock_writer.write = MagicMock()
    mock_writer.drain = AsyncMock()
    return Player(name=name, location=zone, x=x, y=y, writer=mock_writer)


@pytest.mark.asyncio
async def test_home_creates_zone(_init_housing):
    """Player with no home calls home and gets teleported to new zone."""
    overworld = _make_zone("overworld")
    player = _make_player("alice", zone=overworld)

    # Initially no home zone exists
    assert get_zone("home:alice") is None

    await cmd_home(player, "")

    # Now home zone exists and player is in it
    home = get_zone("home:alice")
    assert home is not None
    assert player.location is home
    assert player.home_zone == "home:alice"
    assert player.return_location == ("overworld", 5, 5)


@pytest.mark.asyncio
async def test_home_return(_init_housing):
    """Player goes home then home return, ends up back where they were."""
    overworld = _make_zone("overworld")
    player = _make_player("bob", zone=overworld, x=10, y=15)

    # Go home
    await cmd_home(player, "")
    home = get_zone("home:bob")
    assert player.location is home

    # Return
    await cmd_home(player, "return")
    assert player.location is overworld
    assert player.x == 10
    assert player.y == 15
    assert player.return_location is None


@pytest.mark.asyncio
async def test_home_return_without_location(_init_housing):
    """home return with no saved location shows error."""
    overworld = _make_zone("overworld")
    player = _make_player("charlie", zone=overworld)

    # No return location set
    assert player.return_location is None

    await cmd_home(player, "return")

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


@pytest.mark.asyncio
async def test_home_already_at_home(_init_housing):
    """Calling home while already at home doesn't overwrite return_location."""
    overworld = _make_zone("overworld")
    player = _make_player("diane", zone=overworld, x=7, y=8)

    # Go home first time
    await cmd_home(player, "")
    assert player.return_location == ("overworld", 7, 8)

    # Call home again while at home
    await cmd_home(player, "")

    # return_location should still point to overworld, not home
    assert player.return_location == ("overworld", 7, 8)


@pytest.mark.asyncio
async def test_home_invalid_args(_init_housing):
    """home foo shows usage."""
    overworld = _make_zone("overworld")
    player = _make_player("eve", zone=overworld)

    await cmd_home(player, "foo")

    assert player.writer.write.called
    output = "".join(c[0][0] for c in player.writer.write.call_args_list)
    assert "usage" in output.lower()


@pytest.mark.asyncio
async def test_home_departure_arrival_messages(_init_housing):
    """Check nearby messages are sent."""
    from mudlib.player import players

    players.clear()

    overworld = _make_zone("overworld")
    player = _make_player("frank", zone=overworld, x=10, y=10)
    other = _make_player("grace", zone=overworld, x=10, y=10)

    players["frank"] = player
    players["grace"] = other

    # Go home
    await cmd_home(player, "")

    # Other player should have seen departure message
    assert other.writer.write.called
    output = "".join(c[0][0] for c in other.writer.write.call_args_list)
    assert "frank" in output.lower()
    assert "vanishes" in output.lower()

    players.clear()
