# tests touche E (Kael / Seris / Aldric)

from unittest.mock import AsyncMock

from lobby import Lobby, LobbyPlayer
from game_loop import GameLoop
from game_state import EnemyState
from constants import (
    DISPLACEMENT_COOLDOWNS,
    KAEL_CHARGE_DISTANCE,
    SERIS_INTANGIBLE_DURATION,
    ALDRIC_FLYING_DURATION,
    ENEMY_STATS,
)


def make_game():
    classes = [("p1", "Alice", "kael"), ("p2", "Bob", "seris"), ("p3", "Clara", "aldric")]
    lobby = Lobby("TEST1", classes[0][0])
    lobby.players = [LobbyPlayer(cid, uname, cls) for cid, uname, cls in classes]
    return GameLoop(lobby, AsyncMock())


def add_enemy(game, etype="fracture", x=0.0, y=5.0):
    stats = ENEMY_STATS[etype]
    game._enemy_counter += 1
    e = EnemyState(id=f"en_{game._enemy_counter}", type=etype, x=x, y=y, hp=stats["hp"], max_hp=stats["hp"])
    game.state.enemies.append(e)
    return e


def test_kael_charge():
    game = make_game()
    p = game.state.players[0]
    p.x, p.y = 0.0, 0.0
    game.handle_displacement("p1", 10.0, 0.0)
    assert abs(p.x - KAEL_CHARGE_DISTANCE) < 0.01
    assert p.cooldowns["displacement"] == DISPLACEMENT_COOLDOWNS["kael"]


def test_kael_charge_bloque_par_cd():
    game = make_game()
    p = game.state.players[0]
    p.x, p.y = 0.0, 0.0
    p.cooldowns["displacement"] = 3.0
    game.handle_displacement("p1", 10.0, 0.0)
    assert p.x == 0.0


def test_seris_teleport():
    game = make_game()
    p = game.state.players[1]
    p.x, p.y = 0.0, 0.0
    game.handle_displacement("p2", 5.0, 3.0)
    assert abs(p.x - 5.0) < 0.01
    assert abs(p.y - 3.0) < 0.01
    assert any(b["type"] == "intangible" for b in p.buffs)


def test_seris_intangible_immunise():
    game = make_game()
    p = game.state.players[1]
    p.x, p.y = 0.0, 0.0
    p.hp = p.max_hp
    p.buffs.append({"type": "intangible", "timer": SERIS_INTANGIBLE_DURATION})
    e = add_enemy(game, "fracture", x=0.0, y=0.0)
    e.attack_cooldown = 0.0
    game._update_enemies()
    assert p.hp == p.max_hp


def test_aldric_vol():
    game = make_game()
    p = game.state.players[2]
    game.handle_displacement("p3", 0.0, 0.0)
    flying = next((b for b in p.buffs if b["type"] == "flying"), None)
    assert flying is not None
    assert abs(flying["timer"] - ALDRIC_FLYING_DURATION) < 0.001
