"""Combat encounter management and processing."""

import time

from mudlib.combat.encounter import IDLE_TIMEOUT, CombatEncounter, CombatState
from mudlib.combat.stamina import check_stamina_cues
from mudlib.entity import Entity, Mob
from mudlib.gmcp import send_char_status, send_char_vitals
from mudlib.render.colors import colorize
from mudlib.render.pov import render_pov

# Global list of active combat encounters
active_encounters: list[CombatEncounter] = []


def start_encounter(attacker: Entity, defender: Entity) -> CombatEncounter:
    """Start a new combat encounter.

    Args:
        attacker: The entity initiating combat
        defender: The target entity

    Returns:
        The created CombatEncounter

    Raises:
        ValueError: If either entity is already in combat
    """
    # Check if either entity is already in combat
    if get_encounter(attacker) is not None:
        msg = f"{attacker.name} is already in combat"
        raise ValueError(msg)

    if get_encounter(defender) is not None:
        msg = f"{defender.name} is already in combat"
        raise ValueError(msg)

    # Create and register the encounter
    encounter = CombatEncounter(
        attacker=attacker,
        defender=defender,
        last_action_at=time.monotonic(),
    )
    active_encounters.append(encounter)

    # Cancel any active power-up tasks
    for entity in (attacker, defender):
        task = getattr(entity, "_power_task", None)
        if task is not None:
            task.cancel()
            if hasattr(entity, "_power_task"):
                entity._power_task = None  # type: ignore[attr-defined]

    return encounter


def get_encounter(entity: Entity) -> CombatEncounter | None:
    """Find the active encounter for an entity.

    Args:
        entity: The entity to search for

    Returns:
        CombatEncounter if entity is in combat, None otherwise
    """
    for encounter in active_encounters:
        if encounter.attacker is entity or encounter.defender is entity:
            return encounter
    return None


def end_encounter(encounter: CombatEncounter) -> None:
    """End and remove an encounter from the active list.

    Args:
        encounter: The encounter to end
    """
    if encounter in active_encounters:
        active_encounters.remove(encounter)


async def process_combat() -> None:
    """Process all active combat encounters.

    This should be called each game loop tick to advance combat state machines.
    """
    from mudlib.player import Player

    now = time.monotonic()

    for encounter in active_encounters[:]:  # Copy list to allow modification
        # Check for idle timeout
        if now - encounter.last_action_at > IDLE_TIMEOUT:
            await encounter.attacker.send("Combat has fizzled out.\r\n")
            await encounter.defender.send("Combat has fizzled out.\r\n")

            for entity in (encounter.attacker, encounter.defender):
                if isinstance(entity, Player) and entity.mode == "combat":
                    entity.mode_stack.pop()
                    send_char_status(entity)

            end_encounter(encounter)
            continue

        # Save previous state to detect transitions
        previous_state = encounter.state

        # Tick the state machine
        encounter.tick(now)

        # Send announce message on TELEGRAPH → WINDOW transition
        if (
            previous_state == CombatState.TELEGRAPH
            and encounter.state == CombatState.WINDOW
            and encounter.current_move
            and encounter.current_move.announce
        ):
            template = encounter.current_move.announce
            announce_color = encounter.current_move.announce_color

            for viewer in (encounter.attacker, encounter.defender):
                msg = render_pov(
                    template, viewer, encounter.attacker, encounter.defender
                )
                if announce_color:
                    color_depth = getattr(viewer, "color_depth", None)
                    msg = colorize(f"{{{announce_color}}}{msg}{{/}}", color_depth)
                await viewer.send(msg + "\r\n")

        # Auto-resolve if in RESOLVE state
        if encounter.state == CombatState.RESOLVE:
            # Save current_move before resolve() clears it
            current_move = encounter.current_move
            result = encounter.resolve()

            # Send resolution messages to both participants using POV
            resolve_color = current_move.resolve_color if current_move else "bold"

            for viewer in (encounter.attacker, encounter.defender):
                msg = render_pov(
                    result.resolve_template,
                    viewer,
                    encounter.attacker,
                    encounter.defender,
                )
                if resolve_color:
                    color_depth = getattr(viewer, "color_depth", None)
                    msg = colorize(f"{{{resolve_color}}}{msg}{{/}}", color_depth)
                await viewer.send(msg + "\r\n")

            # Send vitals update after damage resolution
            for entity in (encounter.attacker, encounter.defender):
                if isinstance(entity, Player):
                    send_char_vitals(entity)

            # Check stamina cues after damage
            await check_stamina_cues(encounter.attacker)
            await check_stamina_cues(encounter.defender)

            if result.combat_ended:
                # Determine winner/loser
                if encounter.defender.pl <= 0:
                    loser = encounter.defender
                    winner = encounter.attacker
                else:
                    loser = encounter.attacker
                    winner = encounter.defender

                # Track kill/death stats
                if isinstance(winner, Player):
                    winner.kills += 1
                    if isinstance(loser, Mob):
                        winner.mob_kills[loser.name] = (
                            winner.mob_kills.get(loser.name, 0) + 1
                        )

                    # Check for new unlocks
                    from mudlib.combat.commands import combat_moves
                    from mudlib.combat.unlock import check_unlocks

                    newly_unlocked = check_unlocks(winner, combat_moves)
                    for move_name in newly_unlocked:
                        await winner.send(f"You have learned {move_name}!\r\n")

                if isinstance(loser, Player):
                    loser.deaths += 1

                # Despawn mob losers, send victory/defeat messages
                if isinstance(loser, Mob):
                    from mudlib.corpse import create_corpse
                    from mudlib.mobs import mob_templates
                    from mudlib.zone import Zone

                    zone = loser.location
                    if isinstance(zone, Zone):
                        # Look up loot table from mob template
                        template = mob_templates.get(loser.name)
                        loot_table = template.loot if template else None
                        create_corpse(loser, zone, loot_table=loot_table)
                    else:
                        from mudlib.mobs import despawn_mob

                        despawn_mob(loser)
                    await winner.send(f"You have defeated the {loser.name}!\r\n")
                elif isinstance(winner, Mob):
                    await loser.send(
                        f"You have been defeated by the {winner.name}!\r\n"
                    )

                # Pop combat mode from both entities if they're Players
                attacker = encounter.attacker
                if isinstance(attacker, Player) and attacker.mode == "combat":
                    attacker.mode_stack.pop()
                    send_char_status(attacker)

                defender = encounter.defender
                if isinstance(defender, Player) and defender.mode == "combat":
                    defender.mode_stack.pop()
                    send_char_status(defender)

                # Remove encounter from active list
                end_encounter(encounter)
