"""Tests for z-axis altitude checks in combat."""

from pathlib import Path

import pytest

from mudlib.combat import commands as combat_commands
from mudlib.combat.encounter import CombatState
from mudlib.combat.engine import get_encounter, start_encounter
from mudlib.combat.moves import load_moves


@pytest.fixture
def target(mock_reader, mock_writer, test_zone):
    """Alias for nearby_player fixture with name 'target' for this module."""
    from mudlib.player import Player, players

    t = Player(name="Vegeta", x=0, y=0, reader=mock_reader, writer=mock_writer)
    t.location = test_zone
    test_zone._contents.append(t)
    players[t.name] = t
    return t


@pytest.fixture
def moves():
    """Load combat moves from content directory."""
    content_dir = Path(__file__).parent.parent / "content" / "combat"
    return load_moves(content_dir)


@pytest.fixture(autouse=True)
def inject_moves(moves):
    """Inject loaded moves into combat commands module."""
    combat_commands.combat_moves = moves
    yield
    combat_commands.combat_moves = {}


@pytest.fixture
def punch_right(moves):
    """Get the punch right move."""
    return moves["punch right"]


@pytest.fixture
def dodge_left(moves):
    """Get the dodge left move."""
    return moves["dodge left"]


# --- Z-axis altitude check tests for starting combat ---


@pytest.mark.asyncio
async def test_attack_fails_when_attacker_flying_defender_grounded(
    player, target, punch_right
):
    """Test attack fails when attacker is flying and defender is grounded."""
    player.flying = True
    target.flying = False

    await combat_commands.do_attack(player, "Vegeta", punch_right)

    # Combat should not start
    encounter = get_encounter(player)
    assert encounter is None

    # Player should get error message
    player.writer.write.assert_called()
    messages = [call[0][0] for call in player.writer.write.call_args_list]
    assert any("can't reach" in msg.lower() for msg in messages)


@pytest.mark.asyncio
async def test_attack_fails_when_attacker_grounded_defender_flying(
    player, target, punch_right
):
    """Test attack fails when attacker is grounded and defender is flying."""
    player.flying = False
    target.flying = True

    await combat_commands.do_attack(player, "Vegeta", punch_right)

    # Combat should not start
    encounter = get_encounter(player)
    assert encounter is None

    # Player should get error message
    player.writer.write.assert_called()
    messages = [call[0][0] for call in player.writer.write.call_args_list]
    assert any("can't reach" in msg.lower() for msg in messages)


@pytest.mark.asyncio
async def test_attack_succeeds_when_both_flying(player, target, punch_right):
    """Test attack succeeds when both are flying."""
    player.flying = True
    target.flying = True

    await combat_commands.do_attack(player, "Vegeta", punch_right)

    # Combat should start
    encounter = get_encounter(player)
    assert encounter is not None
    assert encounter.attacker is player
    assert encounter.defender is target


@pytest.mark.asyncio
async def test_attack_succeeds_when_both_grounded(player, target, punch_right):
    """Test attack succeeds when both are grounded."""
    player.flying = False
    target.flying = False

    await combat_commands.do_attack(player, "Vegeta", punch_right)

    # Combat should start
    encounter = get_encounter(player)
    assert encounter is not None
    assert encounter.attacker is player
    assert encounter.defender is target


# --- Flying dodge tests (altitude mismatch at resolve time) ---


@pytest.mark.asyncio
async def test_flying_during_window_causes_miss(player, target, punch_right):
    """Test flying during window phase causes attack to miss at resolve time."""
    # Both grounded, start combat
    player.flying = False
    target.flying = False

    encounter = start_encounter(player, target)
    encounter.attack(punch_right)

    # Advance to WINDOW phase
    encounter.state = CombatState.WINDOW

    # Defender flies during window
    target.flying = True

    # Resolve
    result = encounter.resolve()

    # Attack should miss (countered or zero damage)
    assert result.countered is True or result.damage == 0.0


@pytest.mark.asyncio
async def test_both_flying_at_resolve_attack_lands(player, target, punch_right):
    """Test both flying at resolve time — attack lands normally."""
    # Both flying, start combat
    player.flying = True
    target.flying = True

    encounter = start_encounter(player, target)
    encounter.attack(punch_right)

    # Advance to WINDOW phase (no altitude change)
    encounter.state = CombatState.WINDOW

    # Resolve
    result = encounter.resolve()

    # Attack should land (damage > 0 unless defended)
    # Since there's no defense, damage should be > 0
    assert result.damage > 0.0


@pytest.mark.asyncio
async def test_attacker_flies_during_window_causes_miss(player, target, punch_right):
    """Test attacker flying during window phase causes attack to miss."""
    # Both grounded, start combat
    player.flying = False
    target.flying = False

    encounter = start_encounter(player, target)
    encounter.attack(punch_right)

    # Advance to WINDOW phase
    encounter.state = CombatState.WINDOW

    # Attacker flies during window
    player.flying = True

    # Resolve
    result = encounter.resolve()

    # Attack should miss
    assert result.countered is True or result.damage == 0.0


@pytest.mark.asyncio
async def test_flying_dodge_messages_correct_grammar(player, target, punch_right):
    """Test flying dodge produces grammatically correct messages from both POVs."""
    from mudlib.render.pov import render_pov

    # Both grounded, start combat
    player.flying = False
    target.flying = False

    encounter = start_encounter(player, target)
    encounter.attack(punch_right)

    # Advance to WINDOW phase
    encounter.state = CombatState.WINDOW

    # Defender flies during window
    target.flying = True

    # Resolve
    result = encounter.resolve()

    # Render from both POVs
    attacker_msg = render_pov(result.resolve_template, player, player, target)
    defender_msg = render_pov(result.resolve_template, target, player, target)

    # Attacker POV should say "You miss — Vegeta is out of reach!"
    assert attacker_msg == "You miss — Vegeta is out of reach!"

    # Defender POV should say "Goku misses — You are out of reach!"
    assert defender_msg == "Goku misses — You are out of reach!"
