# waves.py : machine a etats des vagues (combat / boss / preparation / victory)

from dataclasses import dataclass

from constants import PREPARATION_DURATION, TICK_DURATION, WAVE_BOSSES


@dataclass
class SpawnGroup:
    enemy_type: str   # type d'ennemi à spawner : "fracture", "rampant", "colosse", "eclat"
    count: int        # nombre total d'ennemis à spawner dans ce groupe
    interval: int     # ticks entre chaque spawn (ex: 40 ticks = 2s à 20Hz)
                      # 20 Hz = 20 ticks par seconde → 40 ticks = 2 secondes


@dataclass
class PhaseConfig:
    """Configuration d'une phase : plusieurs groupes d'ennemis spawnent en parallèle."""
    groups: list[SpawnGroup]   # chaque groupe spawn indépendamment selon son interval


# Données des vagues
# WAVE_CONFIGS[i] = liste de PhaseConfig pour la vague i+1
# Chaque vague a plusieurs phases. Dans chaque phase, plusieurs groupes spawnent en parallèle.

WAVE_CONFIGS: list[list[PhaseConfig]] = [

    # Vague 1 — L'Éveil (5 phases de difficulté croissante)
    [
        PhaseConfig([SpawnGroup("fracture", 4, 40)]),   # phase 1 : 4 fractures, 1 toutes les 2s
        PhaseConfig([SpawnGroup("rampant",  6, 30)]),   # phase 2 : 6 rampants, 1 toutes les 1.5s
        PhaseConfig([SpawnGroup("fracture", 4, 30), SpawnGroup("rampant",  3, 60)]),   # phase 3 : les deux en même temps
        PhaseConfig([SpawnGroup("colosse",  2, 80), SpawnGroup("fracture", 4, 40)]),   # phase 4 : colosses + fractures
        PhaseConfig([SpawnGroup("fracture", 6, 20), SpawnGroup("rampant",  6, 20), SpawnGroup("eclat", 4, 15)]),  # phase 5 : chaos total
    ],

    # Vague 2 — Le Héraut (3 phases, puis boss Vexaris)
    [
        PhaseConfig([SpawnGroup("fracture", 6, 30), SpawnGroup("rampant",  4, 40)]),
        PhaseConfig([SpawnGroup("eclat",    8, 15), SpawnGroup("colosse",  2, 80)]),
        PhaseConfig([SpawnGroup("fracture", 8, 20), SpawnGroup("rampant",  6, 25), SpawnGroup("eclat", 6, 15)]),
    ],

    # Vague 3 — Morveth (3 phases encore plus dures, puis boss Morveth)
    [
        PhaseConfig([SpawnGroup("fracture", 10, 15), SpawnGroup("rampant",  8, 20), SpawnGroup("colosse", 3, 60)]),
        PhaseConfig([SpawnGroup("eclat",    10, 10), SpawnGroup("fracture", 8, 15), SpawnGroup("colosse", 3, 60)]),
        PhaseConfig([SpawnGroup("fracture", 12, 10), SpawnGroup("rampant", 10, 15), SpawnGroup("colosse", 4, 50), SpawnGroup("eclat", 8, 12)]),
    ],
]


# WaveManager

class WaveManager:
    """
    machine a etats : combat -> boss -> preparation -> ... -> victory
    """

    def __init__(self):
        self._wave_idx = 0
        self._phase_idx = 0
        self._state = "preparation"
        self._prep_timer = PREPARATION_DURATION
        self._initial_prep = True
        self._phase_spawned = []
        self._phase_ticks = []
        self._total_to_spawn = 0
        self._total_spawned = 0
        self._boss_spawned = False
        self._init_phase()

    @property
    def wave_number(self):
        return self._wave_idx + 1  # affiche 1/2/3

    @property
    def phase_number(self):
        return self._phase_idx + 1

    @property
    def state(self):
        return self._state

    @property
    def prep_timer_remaining(self):
        return max(0.0, self._prep_timer)

    def _current_phase(self):
        return WAVE_CONFIGS[self._wave_idx][self._phase_idx]

    def _init_phase(self):
        phase = self._current_phase()
        n = len(phase.groups)
        self._phase_spawned = [0] * n
        # ticks initialises a l'intervalle pour spawn des le 1er tick
        self._phase_ticks = [g.interval for g in phase.groups]
        self._total_to_spawn = sum(g.count for g in phase.groups)
        self._total_spawned = 0

    def enemies_remaining(self):
        # ennemis restants a spawner dans la phase courante (pas ceux vivants sur la map)
        return self._total_to_spawn - self._total_spawned

    def tick(self, spawn_fn, enemy_count):
        events = []

        if self._state == "victory":
            return events

        # phase boss : on spawn le boss 1 fois, puis on attend qu'il meure
        if self._state == "boss":
            if not self._boss_spawned:
                spawn_fn(WAVE_BOSSES[self._wave_idx + 1])
                self._boss_spawned = True
            elif enemy_count == 0:
                next_wave = self._wave_idx + 1
                if next_wave < len(WAVE_CONFIGS):
                    self._state = "preparation"
                    self._prep_timer = PREPARATION_DURATION
                    events.append("wave_complete")
                    events.append("preparation_start")
                else:
                    self._state = "victory"
                    events.append("game_over_victory")
            return events

        # phase preparation : timer 20s avant la vague suivante
        if self._state == "preparation":
            self._prep_timer -= TICK_DURATION
            if self._prep_timer <= 0.0:
                if self._initial_prep:
                    self._initial_prep = False  # vague 1 : pas d'increment
                else:
                    self._wave_idx += 1
                self._phase_idx = 0
                self._init_phase()
                self._state = "combat"
                events.append("wave_start")
            return events

        # combat : spawn des groupes en parallele
        phase = self._current_phase()

        for i, group in enumerate(phase.groups):
            if self._phase_spawned[i] >= group.count:
                continue
            self._phase_ticks[i] += 1
            if self._phase_ticks[i] >= group.interval:
                spawn_fn(group.enemy_type)
                self._phase_spawned[i] += 1
                self._total_spawned += 1
                self._phase_ticks[i] = 0

        # fin de phase : tout spawne ET 0 ennemis vivants
        all_spawned = self._total_spawned >= self._total_to_spawn
        if all_spawned and enemy_count == 0:
            next_phase = self._phase_idx + 1
            if next_phase < len(WAVE_CONFIGS[self._wave_idx]):
                self._phase_idx = next_phase
                self._init_phase()
                events.append("wave_phase")
            else:
                wave_number = self._wave_idx + 1
                next_wave = self._wave_idx + 1
                if wave_number in WAVE_BOSSES:
                    self._state = "boss"
                    self._boss_spawned = False
                    events.append("boss_incoming")
                elif next_wave < len(WAVE_CONFIGS):
                    self._state = "preparation"
                    self._prep_timer = PREPARATION_DURATION
                    events.append("wave_complete")
                    events.append("preparation_start")
                else:
                    # Dernière vague sans boss → victoire
                    self._state = "victory"
                    events.append("game_over_victory")

        return events
