"""Tests for unconscious state mechanics."""

import pytest

from mudlib.combat.engine import active_encounters, start_encounter
from mudlib.combat.moves import CombatMove
from mudlib.player import Player, players
from mudlib.unconscious import process_unconscious


class MockWriter:
    """Mock writer that captures output."""

    def __init__(self):
        self.written = []
        self.closed = False

        # Mock option negotiation state
        from unittest.mock import MagicMock

        self.local_option = MagicMock()
        self.local_option.enabled = MagicMock(return_value=False)
        self.remote_option = MagicMock()
        self.remote_option.enabled = MagicMock(return_value=False)

    def write(self, data):
        if isinstance(data, str):
            self.written.append(data)

    async def drain(self):
        pass

    def close(self):
        self.closed = True

    def is_closing(self):
        return self.closed


@pytest.fixture
def clear_state():
    """Clear global state before each test."""
    players.clear()
    active_encounters.clear()
    yield
    players.clear()
    active_encounters.clear()


@pytest.fixture
def mock_writer():
    """Create a mock writer for testing."""
    return MockWriter()


def test_unconscious_when_pl_zero(clear_state, mock_writer):
    """Test that entity becomes unconscious when PL reaches 0."""
    player = Player(name="TestPlayer", x=0, y=0, pl=100.0, writer=mock_writer)

    # Player should start conscious
    assert player.posture != "unconscious"

    # Drop PL to 0
    player.pl = 0.0

    # Player should now be unconscious
    assert player.posture == "unconscious"


def test_unconscious_when_stamina_zero(clear_state, mock_writer):
    """Test that entity becomes unconscious when stamina reaches 0."""
    player = Player(name="TestPlayer", x=0, y=0, stamina=100.0, writer=mock_writer)

    # Player should start conscious
    assert player.posture != "unconscious"

    # Drop stamina to 0
    player.stamina = 0.0

    # Player should now be unconscious
    assert player.posture == "unconscious"


@pytest.mark.asyncio
async def test_unconscious_recovery(clear_state, mock_writer):
    """Test that unconscious player recovers after ticks."""
    player = Player(
        name="TestPlayer", x=0, y=0, pl=0.0, stamina=0.0, writer=mock_writer
    )
    players["TestPlayer"] = player

    # Player should start unconscious
    assert player.posture == "unconscious"

    # Process recovery ticks until player is conscious
    # Recovery rate is 0.1 per tick for both PL and stamina
    # Need 1 tick to get above 0
    await process_unconscious()

    # After one tick, both should be above 0
    assert player.pl > 0.0
    assert player.stamina > 0.0
    assert player.posture != "unconscious"


@pytest.mark.asyncio
async def test_come_to_message(clear_state, mock_writer):
    """Test that 'come to' message is sent on recovery."""
    player = Player(
        name="TestPlayer", x=0, y=0, pl=0.0, stamina=0.0, writer=mock_writer
    )
    players["TestPlayer"] = player

    # Clear any initial messages
    mock_writer.written.clear()

    # Process recovery
    await process_unconscious()

    # Check for come to message
    assert any("come to" in msg.lower() for msg in mock_writer.written)


@pytest.mark.asyncio
async def test_snap_neck_requires_unconscious(clear_state):
    """Test that snap neck only works on unconscious target."""
    from mudlib.commands.snapneck import cmd_snap_neck

    attacker_writer = MockWriter()
    defender_writer = MockWriter()

    attacker = Player(name="Attacker", x=0, y=0, writer=attacker_writer)
    defender = Player(name="Defender", x=0, y=0, writer=defender_writer, pl=100.0)

    players["Attacker"] = attacker
    players["Defender"] = defender

    # Start encounter first
    start_encounter(attacker, defender)

    # Try to snap neck on conscious target
    attacker_writer.written.clear()
    await cmd_snap_neck(attacker, "Defender")

    # Should fail with "not unconscious" message
    assert any("unconscious" in msg.lower() for msg in attacker_writer.written)


@pytest.mark.asyncio
async def test_snap_neck_success(clear_state):
    """Test that snap neck works on unconscious target."""
    from mudlib.commands.snapneck import cmd_snap_neck

    attacker_writer = MockWriter()
    defender_writer = MockWriter()

    attacker = Player(name="Attacker", x=0, y=0, writer=attacker_writer)
    defender = Player(name="Defender", x=0, y=0, writer=defender_writer, pl=0.0)

    players["Attacker"] = attacker
    players["Defender"] = defender

    # Start encounter
    encounter = start_encounter(attacker, defender)

    # Clear messages
    attacker_writer.written.clear()
    defender_writer.written.clear()

    # Snap neck on unconscious target
    await cmd_snap_neck(attacker, "Defender")

    # Should succeed with dramatic message
    assert any("snap" in msg.lower() for msg in attacker_writer.written)

    # Encounter should end
    assert encounter not in active_encounters


@pytest.mark.asyncio
async def test_snap_neck_message_sent_to_both(clear_state):
    """Test that snap neck message is sent to both parties."""
    from mudlib.commands.snapneck import cmd_snap_neck

    attacker_writer = MockWriter()
    defender_writer = MockWriter()

    attacker = Player(name="Attacker", x=0, y=0, writer=attacker_writer)
    defender = Player(name="Defender", x=0, y=0, writer=defender_writer, pl=0.0)

    players["Attacker"] = attacker
    players["Defender"] = defender

    # Start encounter
    start_encounter(attacker, defender)

    # Clear messages
    attacker_writer.written.clear()
    defender_writer.written.clear()

    # Snap neck
    await cmd_snap_neck(attacker, "Defender")

    # Both should receive messages
    assert len(attacker_writer.written) > 0
    assert len(defender_writer.written) > 0


@pytest.mark.asyncio
async def test_knockout_ends_combat(clear_state, mock_writer):
    """Test that combat ends when a player is knocked out."""
    from mudlib.combat.encounter import CombatEncounter, CombatState

    attacker = Player(name="Attacker", x=0, y=0, writer=mock_writer)
    defender = Player(name="Defender", x=0, y=0, writer=mock_writer, pl=1.0)

    # Create a simple punch move for testing
    move = CombatMove(
        name="test punch",
        command="testpunch",
        move_type="attack",
        damage_pct=0.5,
        stamina_cost=10.0,
        timing_window_ms=850,
        countered_by=[],
    )

    encounter = CombatEncounter(attacker=attacker, defender=defender)
    encounter.state = CombatState.RESOLVE
    encounter.current_move = move

    # Resolve should detect knockout
    result = encounter.resolve()

    # Combat should end
    assert result.combat_ended


@pytest.mark.asyncio
async def test_partial_recovery(clear_state, mock_writer):
    """Test that only PL or stamina being above 0 doesn't wake player."""
    player = Player(
        name="TestPlayer", x=0, y=0, pl=5.0, stamina=0.0, writer=mock_writer
    )
    players["TestPlayer"] = player

    # Player should be unconscious (stamina is 0)
    assert player.posture == "unconscious"

    # Process recovery - only stamina should increase
    await process_unconscious()

    # Check that PL wasn't affected (started at 5.0, should stay at 5.0)
    assert player.pl == 5.0
    # Stamina should have increased
    assert player.stamina > 0.0

    # Player should now be conscious (both > 0)
    assert player.posture != "unconscious"
