"""
Enemy Loader Service - YAML-based enemy template loading.

This service loads enemy definitions from YAML files, providing a data-driven
approach to defining monsters and enemies for combat encounters.
"""

from pathlib import Path
from typing import Dict, List, Optional
import yaml

from app.models.enemy import EnemyTemplate, EnemyDifficulty
from app.utils.logging import get_logger

logger = get_logger(__file__)


class EnemyLoader:
    """
    Loads enemy templates from YAML configuration files.

    This allows game designers to define enemies without touching code.
    Enemy files are organized by difficulty in subdirectories.
    """

    def __init__(self, data_dir: Optional[str] = None):
        """
        Initialize the enemy loader.

        Args:
            data_dir: Path to directory containing enemy YAML files
                     Defaults to /app/data/enemies/
        """
        if data_dir is None:
            # Default to app/data/enemies relative to this file
            current_file = Path(__file__)
            app_dir = current_file.parent.parent  # Go up to /app
            data_dir = str(app_dir / "data" / "enemies")

        self.data_dir = Path(data_dir)
        self._enemy_cache: Dict[str, EnemyTemplate] = {}
        self._loaded = False

        logger.info("EnemyLoader initialized", data_dir=str(self.data_dir))

    def load_enemy(self, enemy_id: str) -> Optional[EnemyTemplate]:
        """
        Load a single enemy template by ID.

        Args:
            enemy_id: Unique enemy identifier

        Returns:
            EnemyTemplate instance or None if not found
        """
        # Check cache first
        if enemy_id in self._enemy_cache:
            return self._enemy_cache[enemy_id]

        # If not cached, try loading all enemies first
        if not self._loaded:
            self.load_all_enemies()
            if enemy_id in self._enemy_cache:
                return self._enemy_cache[enemy_id]

        # Try loading from specific YAML file
        yaml_file = self.data_dir / f"{enemy_id}.yaml"
        if yaml_file.exists():
            return self._load_from_file(yaml_file)

        # Search in subdirectories
        for subdir in self.data_dir.iterdir():
            if subdir.is_dir():
                yaml_file = subdir / f"{enemy_id}.yaml"
                if yaml_file.exists():
                    return self._load_from_file(yaml_file)

        logger.warning("Enemy not found", enemy_id=enemy_id)
        return None

    def _load_from_file(self, yaml_file: Path) -> Optional[EnemyTemplate]:
        """
        Load an enemy template from a specific YAML file.

        Args:
            yaml_file: Path to the YAML file

        Returns:
            EnemyTemplate instance or None on error
        """
        try:
            with open(yaml_file, 'r') as f:
                data = yaml.safe_load(f)

            enemy = EnemyTemplate.from_dict(data)
            self._enemy_cache[enemy.enemy_id] = enemy

            logger.debug("Enemy loaded", enemy_id=enemy.enemy_id, file=str(yaml_file))
            return enemy

        except Exception as e:
            logger.error("Failed to load enemy file",
                        file=str(yaml_file),
                        error=str(e))
            return None

    def load_all_enemies(self) -> Dict[str, EnemyTemplate]:
        """
        Load all enemy templates from the data directory.

        Searches both the root directory and subdirectories for YAML files.

        Returns:
            Dictionary mapping enemy_id to EnemyTemplate instance
        """
        if not self.data_dir.exists():
            logger.warning("Enemy data directory not found", path=str(self.data_dir))
            return {}

        enemies = {}

        # Load from root directory
        for yaml_file in self.data_dir.glob("*.yaml"):
            enemy = self._load_from_file(yaml_file)
            if enemy:
                enemies[enemy.enemy_id] = enemy

        # Load from subdirectories (organized by difficulty)
        for subdir in self.data_dir.iterdir():
            if subdir.is_dir():
                for yaml_file in subdir.glob("*.yaml"):
                    enemy = self._load_from_file(yaml_file)
                    if enemy:
                        enemies[enemy.enemy_id] = enemy

        self._loaded = True
        logger.info("All enemies loaded", count=len(enemies))

        return enemies

    def get_enemies_by_difficulty(
        self,
        difficulty: EnemyDifficulty
    ) -> List[EnemyTemplate]:
        """
        Get all enemies matching a difficulty level.

        Args:
            difficulty: Difficulty level to filter by

        Returns:
            List of EnemyTemplate instances
        """
        if not self._loaded:
            self.load_all_enemies()

        return [
            enemy for enemy in self._enemy_cache.values()
            if enemy.difficulty == difficulty
        ]

    def get_enemies_by_tag(self, tag: str) -> List[EnemyTemplate]:
        """
        Get all enemies with a specific tag.

        Args:
            tag: Tag to filter by (e.g., "undead", "beast", "humanoid")

        Returns:
            List of EnemyTemplate instances with that tag
        """
        if not self._loaded:
            self.load_all_enemies()

        return [
            enemy for enemy in self._enemy_cache.values()
            if enemy.has_tag(tag)
        ]

    def get_enemies_by_location(
        self,
        location_type: str,
        difficulty: Optional[EnemyDifficulty] = None
    ) -> List[EnemyTemplate]:
        """
        Get all enemies that can appear at a specific location type.

        This is used by the encounter generator to find location-appropriate
        enemies for random encounters.

        Args:
            location_type: Location type to filter by (e.g., "forest", "dungeon",
                          "town", "wilderness", "crypt", "ruins", "road")
            difficulty: Optional difficulty filter to narrow results

        Returns:
            List of EnemyTemplate instances that can appear at the location
        """
        if not self._loaded:
            self.load_all_enemies()

        candidates = [
            enemy for enemy in self._enemy_cache.values()
            if enemy.has_location_tag(location_type)
        ]

        # Apply difficulty filter if specified
        if difficulty is not None:
            candidates = [e for e in candidates if e.difficulty == difficulty]

        logger.debug(
            "Enemies found for location",
            location_type=location_type,
            difficulty=difficulty.value if difficulty else None,
            count=len(candidates)
        )

        return candidates

    def get_random_enemies(
        self,
        count: int = 1,
        difficulty: Optional[EnemyDifficulty] = None,
        tag: Optional[str] = None,
        exclude_bosses: bool = True
    ) -> List[EnemyTemplate]:
        """
        Get random enemies for encounter generation.

        Args:
            count: Number of enemies to select
            difficulty: Optional difficulty filter
            tag: Optional tag filter
            exclude_bosses: Whether to exclude boss enemies

        Returns:
            List of randomly selected EnemyTemplate instances
        """
        import random

        if not self._loaded:
            self.load_all_enemies()

        # Build candidate list
        candidates = list(self._enemy_cache.values())

        # Apply filters
        if difficulty:
            candidates = [e for e in candidates if e.difficulty == difficulty]
        if tag:
            candidates = [e for e in candidates if e.has_tag(tag)]
        if exclude_bosses:
            candidates = [e for e in candidates if not e.is_boss()]

        if not candidates:
            logger.warning("No enemies match filters",
                         difficulty=difficulty.value if difficulty else None,
                         tag=tag)
            return []

        # Select random enemies (with replacement if needed)
        if len(candidates) >= count:
            return random.sample(candidates, count)
        else:
            # Not enough unique enemies, allow duplicates
            return random.choices(candidates, k=count)

    def clear_cache(self) -> None:
        """Clear the enemy cache, forcing reload on next access."""
        self._enemy_cache.clear()
        self._loaded = False
        logger.debug("Enemy cache cleared")

    def get_all_cached(self) -> Dict[str, EnemyTemplate]:
        """
        Get all cached enemies.

        Returns:
            Dictionary of cached enemy templates
        """
        if not self._loaded:
            self.load_all_enemies()
        return self._enemy_cache.copy()


# Global instance for convenience
_loader_instance: Optional[EnemyLoader] = None


def get_enemy_loader() -> EnemyLoader:
    """
    Get the global EnemyLoader instance.

    Returns:
        Singleton EnemyLoader instance
    """
    global _loader_instance
    if _loader_instance is None:
        _loader_instance = EnemyLoader()
    return _loader_instance
