# tests boss : vexaris + morveth + transitions

from unittest.mock import AsyncMock

from lobby import Lobby, LobbyPlayer
from game_loop import GameLoop
from constants import (
    VEXARIS_HP, VEXARIS_HITBOX_RADIUS,
    MORVETH_HP,
    WAVE_BOSSES, SOUL_REWARDS,
)
from waves import WaveManager
from constants import PREPARATION_DURATION, TICK_DURATION


def make_lobby():
    lobby = Lobby("TEST1", "p1")
    lobby.players = [
        LobbyPlayer("p1", "Alice", "kael"),
        LobbyPlayer("p2", "Bob",   "seris"),
        LobbyPlayer("p3", "Clara", "aldric"),
    ]
    return lobby


def _ticks(wm, n, enemy_count=0):
    events = []
    for _ in range(n):
        events.extend(wm.tick(lambda t: None, enemy_count))
    return events


def _complete_wave_phases(wm):
    for _ in range(200_000):
        wm.tick(lambda t: None, 0)
        if wm.state != "combat":
            return
    raise AssertionError("vague combat infinie")


def _prep_ticks():
    return int(PREPARATION_DURATION / TICK_DURATION) + 1


def _skip_initial_prep(wm):
    while wm.state == "preparation":
        wm.tick(lambda t: None, 0)


def test_wave_bosses():
    assert WAVE_BOSSES[2] == "vexaris"
    assert WAVE_BOSSES[3] == "morveth"
    assert 1 not in WAVE_BOSSES


def test_spawn_vexaris():
    loop = GameLoop(make_lobby(), AsyncMock())
    loop.spawn_enemy("vexaris")
    boss = loop.state.enemies[0]
    assert boss.type == "vexaris"
    assert boss.hp == VEXARIS_HP
    assert boss.is_boss is True


def test_boss_tape_joueur_au_contact():
    loop = GameLoop(make_lobby(), AsyncMock())
    loop.spawn_enemy("vexaris")
    boss = loop.state.enemies[0]
    player = loop.state.players[0]
    boss.x, boss.y = player.x, player.y
    hp_initial = player.hp
    loop._update_enemies()
    assert player.hp < hp_initial


def test_boss_kill_donne_des_ames():
    loop = GameLoop(make_lobby(), AsyncMock())
    loop.spawn_enemy("vexaris")
    boss = loop.state.enemies[0]
    player = loop.state.players[0]
    initial_souls = player.souls
    from game_state import ProjectileState
    loop.state.projectiles.append(ProjectileState(
        id="pr_test", owner_id=player.id,
        x=boss.x, y=boss.y, vx=0, vy=0,
        damage=VEXARIS_HP, radius=VEXARIS_HITBOX_RADIUS + 1, ttl=1.0,
    ))
    loop._update_projectiles()
    assert player.souls == initial_souls + SOUL_REWARDS["vexaris"]


def test_spawn_morveth():
    loop = GameLoop(make_lobby(), AsyncMock())
    loop.spawn_enemy("morveth")
    boss = loop.state.enemies[0]
    assert boss.type == "morveth"
    assert boss.hp == MORVETH_HP


def _morveth_at_pct(pct):
    loop = GameLoop(make_lobby(), AsyncMock())
    loop.spawn_enemy("morveth")
    boss = loop.state.enemies[0]
    boss.hp = max(1, int(boss.max_hp * pct))
    boss.data.update({
        "charge_cd": 99.0, "charge_timer": 0.0,
        "charge_tx": 0.0,  "charge_ty": 0.0,
        "is_charging": False,
        "burst_cd": 99.0,
        "general_1_spawned": False,
        "general_2_spawned": False,
        "general_3_spawned": False,
    })
    return loop


def test_morveth_invoque_general_a_75():
    loop = _morveth_at_pct(0.74)
    loop._update_enemies()
    generals = [e for e in loop.state.enemies if e.type == "general"]
    assert len(generals) == 1


def test_morveth_pas_de_double_invocation():
    loop = _morveth_at_pct(0.74)
    loop._update_enemies()
    loop._update_enemies()
    generals = [e for e in loop.state.enemies if e.type == "general"]
    assert len(generals) == 1


def test_victory_apres_morveth():
    wm = WaveManager()
    _skip_initial_prep(wm)
    _complete_wave_phases(wm)
    _ticks(wm, _prep_ticks())
    _complete_wave_phases(wm)
    _ticks(wm, 3)  # Vexaris spawn + kill
    _ticks(wm, _prep_ticks())
    _complete_wave_phases(wm)
    _ticks(wm, 3)  # Morveth spawn + kill
    assert wm.state == "victory"
