"""Tests for combat command targeting integration."""

import pytest

from mudlib.combat.commands import do_attack
from mudlib.combat.moves import CombatMove
from mudlib.mobs import Mob
from mudlib.player import Player


@pytest.fixture
def attack_move():
    """Create a test attack move."""
    return CombatMove(
        name="punch left",
        command="punch",
        variant="left",
        move_type="attack",
        stamina_cost=5,
        timing_window_ms=850,
        telegraph="telegraphs a left punch at {defender}",
        telegraph_color="yellow",
        aliases=[],
    )


@pytest.mark.asyncio
async def test_attack_with_prefix_match(player, nearby_player, attack_move):
    """Attack with prefix match finds target."""
    player.stamina = 10

    # Use "veg" to find Vegeta
    await do_attack(player, "veg", attack_move)

    # Should send combat start message
    sent = "".join(call.args[0] for call in player.writer.write.call_args_list)
    assert "You engage Vegeta in combat!" in sent
    assert "You use punch left!" in sent


@pytest.mark.asyncio
async def test_attack_with_ordinal(player, nearby_player, test_zone, attack_move):
    """Attack with ordinal selects correct target."""
    # Create two more players on the same tile, both starting with "p"
    from unittest.mock import AsyncMock, MagicMock

    mock_writer2 = MagicMock()
    mock_writer2.write = MagicMock()
    mock_writer2.drain = AsyncMock()
    mock_reader2 = MagicMock()
    player2 = Player(name="Piccolo", x=0, y=0, reader=mock_reader2, writer=mock_writer2)
    player2.location = test_zone
    test_zone._contents.append(player2)

    mock_writer3 = MagicMock()
    mock_writer3.write = MagicMock()
    mock_writer3.drain = AsyncMock()
    mock_reader3 = MagicMock()
    player3 = Player(name="Puar", x=0, y=0, reader=mock_reader3, writer=mock_writer3)
    player3.location = test_zone
    test_zone._contents.append(player3)

    from mudlib.player import players

    players[player2.name] = player2
    players[player3.name] = player3

    player.stamina = 10

    # Use "2.p" to find the second player starting with "p" (should be Puar)
    await do_attack(player, "2.p", attack_move)

    # Should engage the second "p" match
    sent = "".join(call.args[0] for call in player.writer.write.call_args_list)
    assert (
        "You engage Piccolo in combat!" in sent or "You engage Puar in combat!" in sent
    )


@pytest.mark.asyncio
async def test_attack_nonexistent_target(player, attack_move):
    """Attack on non-existent target shows error."""
    player.stamina = 10

    await do_attack(player, "nobody", attack_move)

    sent = "".join(call.args[0] for call in player.writer.write.call_args_list)
    assert "You need a target to start combat." in sent


@pytest.mark.asyncio
async def test_attack_z_axis_mismatch(player, nearby_player, attack_move):
    """Attack on different z-axis is blocked with specific error message."""
    player.stamina = 10
    player.flying = True
    nearby_player.flying = False

    # find_entity_on_tile returns None due to z-axis mismatch
    await do_attack(player, "Vegeta", attack_move)

    sent = "".join(call.args[0] for call in player.writer.write.call_args_list)
    assert "You can't reach them from here!" in sent


@pytest.mark.asyncio
async def test_attack_mob_with_prefix(player, test_zone, attack_move):
    """Attack mob with prefix match."""
    player.stamina = 10

    # Create a mob on the same tile
    mob = Mob(name="Goblin", x=0, y=0)
    mob.location = test_zone
    test_zone._contents.append(mob)

    # Use "gob" to find the goblin
    await do_attack(player, "gob", attack_move)

    sent = "".join(call.args[0] for call in player.writer.write.call_args_list)
    assert "You engage Goblin in combat!" in sent


@pytest.mark.asyncio
async def test_attack_skips_dead_mob(player, test_zone, attack_move):
    """Attack skips dead mobs."""
    player.stamina = 10

    # Create a dead mob on the same tile
    mob = Mob(name="Goblin", x=0, y=0)
    mob.location = test_zone
    mob.alive = False
    test_zone._contents.append(mob)

    # Should not find the dead mob
    await do_attack(player, "goblin", attack_move)

    sent = "".join(call.args[0] for call in player.writer.write.call_args_list)
    assert "You need a target to start combat." in sent
