"""Tests for sleep command."""

import pytest

from mudlib.commands.sleep import cmd_sleep
from mudlib.resting import STAMINA_PER_TICK, process_resting


@pytest.mark.asyncio
async def test_sleep_when_full_sends_not_tired_message(player):
    """Test sleeping when at full stamina sends 'not tired' message."""
    player.stamina = 100.0
    player.max_stamina = 100.0

    await cmd_sleep(player, "")

    player.writer.write.assert_called_once_with("You're not tired.\r\n")
    assert player.stamina == 100.0
    assert not player.sleeping
    assert not player.resting


@pytest.mark.asyncio
async def test_sleep_when_not_sleeping_starts_sleeping(player):
    """Test sleep command when not sleeping starts the sleeping state."""
    player.stamina = 50.0
    player.max_stamina = 100.0
    player.sleeping = False
    player.resting = False

    await cmd_sleep(player, "")

    assert player.sleeping
    assert player.resting  # sleeping implies resting
    messages = [call[0][0] for call in player.writer.write.call_args_list]
    assert any("fall asleep" in msg or "go to sleep" in msg for msg in messages)


@pytest.mark.asyncio
async def test_sleep_when_already_sleeping_wakes_up(player):
    """Test sleep command when already sleeping wakes the player."""
    player.stamina = 50.0
    player.max_stamina = 100.0
    player.sleeping = True
    player.resting = True

    await cmd_sleep(player, "")

    assert not player.sleeping
    assert not player.resting
    messages = [call[0][0] for call in player.writer.write.call_args_list]
    assert any("wake up" in msg or "wake" in msg for msg in messages)


@pytest.mark.asyncio
async def test_sleep_blocked_during_combat(player):
    """Test sleep is blocked when player is in combat mode."""
    player.stamina = 50.0
    player.mode_stack.append("combat")  # Push combat mode onto the stack

    await cmd_sleep(player, "")

    assert not player.sleeping
    messages = [call[0][0] for call in player.writer.write.call_args_list]
    assert any(
        "can't sleep" in msg.lower() or "combat" in msg.lower() for msg in messages
    )


@pytest.mark.asyncio
async def test_sleep_broadcasts_to_nearby_players(player, nearby_player):
    """Test sleeping broadcasts message to nearby players."""
    player.stamina = 50.0
    player.sleeping = False

    await cmd_sleep(player, "")

    nearby_messages = [call[0][0] for call in nearby_player.writer.write.call_args_list]
    assert any(
        "Goku" in msg and ("asleep" in msg or "sleep" in msg) for msg in nearby_messages
    )


@pytest.mark.asyncio
async def test_wake_broadcasts_to_nearby_players(player, nearby_player):
    """Test waking up broadcasts message to nearby players."""
    player.stamina = 50.0
    player.sleeping = True
    player.resting = True

    await cmd_sleep(player, "")

    nearby_messages = [call[0][0] for call in nearby_player.writer.write.call_args_list]
    assert any("Goku" in msg and "wake" in msg for msg in nearby_messages)


@pytest.mark.asyncio
async def test_sleep_stamina_recovery_faster_than_rest(player):
    """Test sleeping provides faster stamina recovery than resting."""
    player.stamina = 50.0
    player.max_stamina = 100.0
    player.sleeping = True
    player.resting = True

    await process_resting()

    # Sleep should give 3x the base rate (0.2 * 3 = 0.6)
    expected = 50.0 + (STAMINA_PER_TICK * 3)
    assert player.stamina == expected


@pytest.mark.asyncio
async def test_sleep_auto_stops_when_full(player):
    """Test sleep auto-stops when stamina reaches max."""
    player.stamina = 99.5
    player.max_stamina = 100.0
    player.sleeping = True
    player.resting = True

    await process_resting()

    assert player.stamina == 100.0
    assert not player.sleeping
    assert not player.resting
    messages = [call[0][0] for call in player.writer.write.call_args_list]
    assert any("fully rested" in msg or "wake" in msg for msg in messages)


@pytest.mark.asyncio
async def test_sleeping_player_does_not_see_nearby_messages(player, nearby_player):
    """Test sleeping players don't receive nearby messages."""
    from mudlib.commands.movement import send_nearby_message

    player.sleeping = True

    # Nearby player does something that would normally broadcast
    await send_nearby_message(
        nearby_player, nearby_player.x, nearby_player.y, "Test message.\r\n"
    )

    # Sleeping player should not have received it
    messages = [call[0][0] for call in player.writer.write.call_args_list]
    assert len(messages) == 0


@pytest.mark.asyncio
async def test_awake_player_sees_nearby_messages(player, nearby_player):
    """Test awake players receive nearby messages normally."""
    from mudlib.commands.movement import send_nearby_message

    player.sleeping = False

    await send_nearby_message(
        nearby_player, nearby_player.x, nearby_player.y, "Test message.\r\n"
    )

    messages = [call[0][0] for call in player.writer.write.call_args_list]
    assert any("Test message" in msg for msg in messages)
