"""Combat move definitions and TOML loading."""

import logging
import tomllib
from collections.abc import Callable
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

log = logging.getLogger(__name__)

CommandHandler = Callable[..., Any]


@dataclass
class UnlockCondition:
    """Condition that must be met to unlock a combat move."""

    type: str  # "kill_count" or "mob_kills"
    threshold: int = 0
    mob_name: str = ""  # for mob_kills type


@dataclass
class CombatMove:
    """Defines a combat move with its properties and counters."""

    name: str
    move_type: str  # "attack" or "defense"
    stamina_cost: float
    timing_window_ms: int
    aliases: list[str] = field(default_factory=list)
    telegraph: str = ""
    damage_pct: float = 0.0
    countered_by: list[str] = field(default_factory=list)
    handler: CommandHandler | None = None
    # base command name ("punch" for "punch left", same as name for simple moves)
    command: str = ""
    # variant key ("left", "right", "" for simple moves)
    variant: str = ""
    description: str = ""
    # Three-beat output templates
    announce: str = ""  # POV template for announce beat
    resolve_hit: str = ""  # POV template for hit (wrong/no defense)
    resolve_miss: str = ""  # POV template for successful counter
    telegraph_color: str = "dim"  # color tag for telegraph
    announce_color: str = ""  # color tag for announce (default/none)
    resolve_color: str = "bold"  # color tag for resolve
    unlock_condition: UnlockCondition | None = None


def load_move(path: Path) -> list[CombatMove]:
    """Load combat move(s) from a TOML file.

    If the file has a [variants] section, each variant produces a separate
    CombatMove with a qualified name (e.g. "punch left"). Shared properties
    come from the top level, variant-specific properties override them.

    Args:
        path: Path to TOML file

    Returns:
        List of CombatMove instances (one per variant, or one for simple moves)

    Raises:
        ValueError: If required fields are missing
    """
    with open(path, "rb") as f:
        data = tomllib.load(f)

    # Required fields
    required_fields = ["name", "move_type", "stamina_cost", "timing_window_ms"]
    for field_name in required_fields:
        if field_name not in data:
            msg = f"missing required field: {field_name}"
            raise ValueError(msg)

    base_name = data["name"]
    variants = data.get("variants")

    # Parse optional unlock condition
    unlock_data = data.get("unlock")
    unlock_condition = None
    if unlock_data:
        unlock_condition = UnlockCondition(
            type=unlock_data["type"],
            threshold=unlock_data.get("threshold", 0),
            mob_name=unlock_data.get("mob_name", ""),
        )

    if variants:
        moves = []
        for variant_key, variant_data in variants.items():
            qualified_name = f"{base_name} {variant_key}"
            moves.append(
                CombatMove(
                    name=qualified_name,
                    move_type=data["move_type"],
                    stamina_cost=variant_data.get("stamina_cost", data["stamina_cost"]),
                    timing_window_ms=variant_data.get(
                        "timing_window_ms", data["timing_window_ms"]
                    ),
                    aliases=variant_data.get("aliases", []),
                    telegraph=variant_data.get("telegraph", data.get("telegraph", "")),
                    damage_pct=variant_data.get(
                        "damage_pct", data.get("damage_pct", 0.0)
                    ),
                    countered_by=variant_data.get(
                        "countered_by", data.get("countered_by", [])
                    ),
                    handler=None,
                    command=base_name,
                    variant=variant_key,
                    description=data.get("description", ""),
                    announce=variant_data.get("announce", data.get("announce", "")),
                    resolve_hit=variant_data.get(
                        "resolve_hit", data.get("resolve_hit", "")
                    ),
                    resolve_miss=variant_data.get(
                        "resolve_miss", data.get("resolve_miss", "")
                    ),
                    telegraph_color=variant_data.get(
                        "telegraph_color", data.get("telegraph_color", "dim")
                    ),
                    announce_color=variant_data.get(
                        "announce_color", data.get("announce_color", "")
                    ),
                    resolve_color=variant_data.get(
                        "resolve_color", data.get("resolve_color", "bold")
                    ),
                    unlock_condition=unlock_condition,
                )
            )
        return moves

    # Simple move (no variants)
    return [
        CombatMove(
            name=base_name,
            move_type=data["move_type"],
            stamina_cost=data["stamina_cost"],
            timing_window_ms=data["timing_window_ms"],
            aliases=data.get("aliases", []),
            telegraph=data.get("telegraph", ""),
            damage_pct=data.get("damage_pct", 0.0),
            countered_by=data.get("countered_by", []),
            handler=None,
            command=base_name,
            variant="",
            description=data.get("description", ""),
            announce=data.get("announce", ""),
            resolve_hit=data.get("resolve_hit", ""),
            resolve_miss=data.get("resolve_miss", ""),
            telegraph_color=data.get("telegraph_color", "dim"),
            announce_color=data.get("announce_color", ""),
            resolve_color=data.get("resolve_color", "bold"),
            unlock_condition=unlock_condition,
        )
    ]


def load_moves(directory: Path) -> dict[str, CombatMove]:
    """Load all combat moves from TOML files in a directory.

    Creates a lookup dict keyed by both move names and their aliases.
    All aliases point to the same CombatMove object.

    Args:
        directory: Path to directory containing .toml files

    Returns:
        Dict mapping move names and aliases to CombatMove instances

    Raises:
        ValueError: If duplicate names or aliases are found
    """
    moves: dict[str, CombatMove] = {}
    seen_names: set[str] = set()
    seen_aliases: set[str] = set()
    all_moves: list[CombatMove] = []

    for toml_file in sorted(directory.glob("*.toml")):
        file_moves = load_move(toml_file)

        for move in file_moves:
            # Check for name collisions
            if move.name in seen_names:
                msg = f"duplicate move name: {move.name}"
                raise ValueError(msg)
            seen_names.add(move.name)

            # Check for alias collisions
            for alias in move.aliases:
                if alias in seen_aliases or alias in seen_names:
                    msg = f"duplicate move alias: {alias}"
                    raise ValueError(msg)
                seen_aliases.add(alias)

            # Add to dict by name
            moves[move.name] = move

            # Add to dict by all aliases (pointing to same object)
            for alias in move.aliases:
                moves[alias] = move

            all_moves.append(move)

    # Validate countered_by references
    for move in all_moves:
        for counter_name in move.countered_by:
            if counter_name not in moves:
                log.warning(
                    "Move '%s' references non-existent counter '%s' in countered_by",
                    move.name,
                    counter_name,
                )

    return moves
