"""
Enemy data models for combat encounters.

This module defines the EnemyTemplate dataclass representing enemies/monsters
that can be encountered in combat. Enemy definitions are loaded from YAML files
for data-driven game design.
"""

from dataclasses import dataclass, field, asdict
from typing import Dict, Any, List, Optional, Tuple
from enum import Enum

from app.models.stats import Stats


class EnemyDifficulty(Enum):
    """Enemy difficulty levels for scaling and encounter building."""
    EASY = "easy"
    MEDIUM = "medium"
    HARD = "hard"
    BOSS = "boss"


class LootType(Enum):
    """
    Types of loot drops in enemy loot tables.

    STATIC: Fixed item_id reference (consumables, quest items, materials)
    PROCEDURAL: Procedurally generated equipment (weapons, armor with affixes)
    """
    STATIC = "static"
    PROCEDURAL = "procedural"


@dataclass
class LootEntry:
    """
    Single entry in an enemy's loot table.

    Supports two types of loot:

    STATIC loot (default):
        - item_id references a predefined item (health_potion, gold_coin, etc.)
        - quantity_min/max define stack size

    PROCEDURAL loot:
        - item_type specifies "weapon" or "armor"
        - rarity_bonus adds to rarity roll (difficulty contribution)
        - Generated equipment uses the ItemGenerator system

    Attributes:
        loot_type: Type of loot (static or procedural)
        drop_chance: Probability of dropping (0.0 to 1.0)
        quantity_min: Minimum quantity if dropped
        quantity_max: Maximum quantity if dropped
        item_id: Reference to item definition (for STATIC loot)
        item_type: Type of equipment to generate (for PROCEDURAL loot)
        rarity_bonus: Added to rarity roll (0.0 to 0.5, for PROCEDURAL)
    """

    # Common fields
    loot_type: LootType = LootType.STATIC
    drop_chance: float = 0.1
    quantity_min: int = 1
    quantity_max: int = 1

    # Static loot fields
    item_id: Optional[str] = None

    # Procedural loot fields
    item_type: Optional[str] = None  # "weapon" or "armor"
    rarity_bonus: float = 0.0  # Added to rarity roll (0.0 to 0.5)

    def to_dict(self) -> Dict[str, Any]:
        """Serialize loot entry to dictionary."""
        data = {
            "loot_type": self.loot_type.value,
            "drop_chance": self.drop_chance,
            "quantity_min": self.quantity_min,
            "quantity_max": self.quantity_max,
        }
        # Only include relevant fields based on loot type
        if self.item_id is not None:
            data["item_id"] = self.item_id
        if self.item_type is not None:
            data["item_type"] = self.item_type
            data["rarity_bonus"] = self.rarity_bonus
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'LootEntry':
        """
        Deserialize loot entry from dictionary.

        Backward compatible: entries without loot_type default to STATIC,
        and item_id is required for STATIC entries (for backward compat).
        """
        # Parse loot type with backward compatibility
        loot_type_str = data.get("loot_type", "static")
        loot_type = LootType(loot_type_str)

        return cls(
            loot_type=loot_type,
            drop_chance=data.get("drop_chance", 0.1),
            quantity_min=data.get("quantity_min", 1),
            quantity_max=data.get("quantity_max", 1),
            item_id=data.get("item_id"),
            item_type=data.get("item_type"),
            rarity_bonus=data.get("rarity_bonus", 0.0),
        )


@dataclass
class EnemyTemplate:
    """
    Template definition for an enemy type.

    EnemyTemplates define the base characteristics of enemy types. When combat
    starts, instances are created from templates with randomized variations.

    Attributes:
        enemy_id: Unique identifier (e.g., "goblin", "dire_wolf")
        name: Display name (e.g., "Goblin Scout")
        description: Flavor text about the enemy
        base_stats: Base stat block for this enemy
        abilities: List of ability_ids this enemy can use
        loot_table: Potential drops on defeat
        experience_reward: Base XP granted on defeat
        gold_reward_min: Minimum gold dropped
        gold_reward_max: Maximum gold dropped
        difficulty: Difficulty classification for encounter building
        tags: Classification tags (e.g., ["humanoid", "goblinoid"])
        location_tags: Location types where this enemy appears (e.g., ["forest", "dungeon"])
        image_url: Optional image reference for UI

    Combat-specific attributes:
        base_damage: Base damage for basic attack (no weapon)
        crit_chance: Critical hit chance (0.0 to 1.0)
        flee_chance: Chance to successfully flee from this enemy
    """

    enemy_id: str
    name: str
    description: str
    base_stats: Stats
    abilities: List[str] = field(default_factory=list)
    loot_table: List[LootEntry] = field(default_factory=list)
    experience_reward: int = 10
    gold_reward_min: int = 1
    gold_reward_max: int = 5
    difficulty: EnemyDifficulty = EnemyDifficulty.EASY
    tags: List[str] = field(default_factory=list)
    location_tags: List[str] = field(default_factory=list)
    image_url: Optional[str] = None

    # Combat attributes
    base_damage: int = 5
    crit_chance: float = 0.05
    flee_chance: float = 0.5

    def get_gold_reward(self) -> int:
        """
        Roll random gold reward within range.

        Returns:
            Random gold amount between min and max
        """
        import random
        return random.randint(self.gold_reward_min, self.gold_reward_max)

    def roll_loot(self) -> List[Dict[str, Any]]:
        """
        Roll for loot drops based on loot table.

        Returns:
            List of dropped items with quantities
        """
        import random
        drops = []

        for entry in self.loot_table:
            if random.random() < entry.drop_chance:
                quantity = random.randint(entry.quantity_min, entry.quantity_max)
                drops.append({
                    "item_id": entry.item_id,
                    "quantity": quantity,
                })

        return drops

    def is_boss(self) -> bool:
        """Check if this enemy is a boss."""
        return self.difficulty == EnemyDifficulty.BOSS

    def has_tag(self, tag: str) -> bool:
        """Check if enemy has a specific tag."""
        return tag.lower() in [t.lower() for t in self.tags]

    def has_location_tag(self, location_type: str) -> bool:
        """Check if enemy can appear at a specific location type."""
        return location_type.lower() in [t.lower() for t in self.location_tags]

    def to_dict(self) -> Dict[str, Any]:
        """
        Serialize enemy template to dictionary.

        Returns:
            Dictionary containing all enemy data
        """
        return {
            "enemy_id": self.enemy_id,
            "name": self.name,
            "description": self.description,
            "base_stats": self.base_stats.to_dict(),
            "abilities": self.abilities,
            "loot_table": [entry.to_dict() for entry in self.loot_table],
            "experience_reward": self.experience_reward,
            "gold_reward_min": self.gold_reward_min,
            "gold_reward_max": self.gold_reward_max,
            "difficulty": self.difficulty.value,
            "tags": self.tags,
            "location_tags": self.location_tags,
            "image_url": self.image_url,
            "base_damage": self.base_damage,
            "crit_chance": self.crit_chance,
            "flee_chance": self.flee_chance,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'EnemyTemplate':
        """
        Deserialize enemy template from dictionary.

        Args:
            data: Dictionary containing enemy data (from YAML or JSON)

        Returns:
            EnemyTemplate instance
        """
        # Parse base stats
        stats_data = data.get("base_stats", {})
        base_stats = Stats.from_dict(stats_data)

        # Parse loot table
        loot_table = [
            LootEntry.from_dict(entry)
            for entry in data.get("loot_table", [])
        ]

        # Parse difficulty
        difficulty_value = data.get("difficulty", "easy")
        if isinstance(difficulty_value, str):
            difficulty = EnemyDifficulty(difficulty_value)
        else:
            difficulty = difficulty_value

        return cls(
            enemy_id=data["enemy_id"],
            name=data["name"],
            description=data.get("description", ""),
            base_stats=base_stats,
            abilities=data.get("abilities", []),
            loot_table=loot_table,
            experience_reward=data.get("experience_reward", 10),
            gold_reward_min=data.get("gold_reward_min", 1),
            gold_reward_max=data.get("gold_reward_max", 5),
            difficulty=difficulty,
            tags=data.get("tags", []),
            location_tags=data.get("location_tags", []),
            image_url=data.get("image_url"),
            base_damage=data.get("base_damage", 5),
            crit_chance=data.get("crit_chance", 0.05),
            flee_chance=data.get("flee_chance", 0.5),
        )

    def __repr__(self) -> str:
        """String representation of the enemy template."""
        return (
            f"EnemyTemplate({self.enemy_id}, {self.name}, "
            f"difficulty={self.difficulty.value}, "
            f"xp={self.experience_reward})"
        )
