"""
Base Item Loader Service - YAML-based base item template loading.

This service loads base item templates (weapons, armor) from YAML files,
providing the foundation for procedural item generation.
"""

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

from app.models.affixes import BaseItemTemplate
from app.utils.logging import get_logger

logger = get_logger(__file__)

# Rarity order for comparison
RARITY_ORDER = {
    "common": 0,
    "uncommon": 1,
    "rare": 2,
    "epic": 3,
    "legendary": 4
}


class BaseItemLoader:
    """
    Loads and manages base item templates from YAML configuration files.

    This allows game designers to define base items without touching code.
    Templates are organized into weapons.yaml and armor.yaml files.
    """

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

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

        self.data_dir = Path(data_dir)
        self._weapon_cache: Dict[str, BaseItemTemplate] = {}
        self._armor_cache: Dict[str, BaseItemTemplate] = {}
        self._loaded = False

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

    def _ensure_loaded(self) -> None:
        """Ensure templates are loaded before any operation."""
        if not self._loaded:
            self.load_all()

    def load_all(self) -> None:
        """Load all base item templates from YAML files."""
        if not self.data_dir.exists():
            logger.warning("Base item data directory not found", path=str(self.data_dir))
            return

        # Load weapons
        weapons_file = self.data_dir / "weapons.yaml"
        if weapons_file.exists():
            self._load_templates_from_file(weapons_file, "weapons", self._weapon_cache)

        # Load armor
        armor_file = self.data_dir / "armor.yaml"
        if armor_file.exists():
            self._load_templates_from_file(armor_file, "armor", self._armor_cache)

        self._loaded = True
        logger.info(
            "Base item templates loaded",
            weapon_count=len(self._weapon_cache),
            armor_count=len(self._armor_cache)
        )

    def _load_templates_from_file(
        self,
        yaml_file: Path,
        key: str,
        cache: Dict[str, BaseItemTemplate]
    ) -> None:
        """
        Load templates from a YAML file into the cache.

        Args:
            yaml_file: Path to the YAML file
            key: Top-level key in YAML (e.g., "weapons", "armor")
            cache: Cache dictionary to populate
        """
        try:
            with open(yaml_file, 'r') as f:
                data = yaml.safe_load(f)

            templates_data = data.get(key, {})

            for template_id, template_data in templates_data.items():
                # Ensure template_id is set
                template_data["template_id"] = template_id

                # Set defaults for missing optional fields
                template_data.setdefault("description", "")
                template_data.setdefault("base_damage", 0)
                template_data.setdefault("base_spell_power", 0)
                template_data.setdefault("base_defense", 0)
                template_data.setdefault("base_resistance", 0)
                template_data.setdefault("base_value", 10)
                template_data.setdefault("damage_type", "physical")
                template_data.setdefault("crit_chance", 0.05)
                template_data.setdefault("crit_multiplier", 2.0)
                template_data.setdefault("required_level", 1)
                template_data.setdefault("drop_weight", 1.0)
                template_data.setdefault("min_rarity", "common")

                template = BaseItemTemplate.from_dict(template_data)
                cache[template.template_id] = template

            logger.debug(
                "Templates loaded from file",
                file=str(yaml_file),
                count=len(templates_data)
            )

        except Exception as e:
            logger.error(
                "Failed to load base item file",
                file=str(yaml_file),
                error=str(e)
            )

    def get_template(self, template_id: str) -> Optional[BaseItemTemplate]:
        """
        Get a specific template by ID.

        Args:
            template_id: Unique template identifier

        Returns:
            BaseItemTemplate instance or None if not found
        """
        self._ensure_loaded()

        if template_id in self._weapon_cache:
            return self._weapon_cache[template_id]
        if template_id in self._armor_cache:
            return self._armor_cache[template_id]

        return None

    def get_eligible_templates(
        self,
        item_type: str,
        rarity: str,
        character_level: int = 1
    ) -> List[BaseItemTemplate]:
        """
        Get all templates eligible for generation.

        Args:
            item_type: Type of item ("weapon", "armor")
            rarity: Target rarity
            character_level: Player level for eligibility

        Returns:
            List of eligible BaseItemTemplate instances
        """
        self._ensure_loaded()

        # Select the appropriate cache
        if item_type == "weapon":
            cache = self._weapon_cache
        elif item_type == "armor":
            cache = self._armor_cache
        else:
            logger.warning("Unknown item type", item_type=item_type)
            return []

        eligible = []
        for template in cache.values():
            # Check level requirement
            if not template.can_drop_for_level(character_level):
                continue

            # Check rarity requirement
            if not template.can_generate_at_rarity(rarity):
                continue

            eligible.append(template)

        return eligible

    def get_random_template(
        self,
        item_type: str,
        rarity: str,
        character_level: int = 1
    ) -> Optional[BaseItemTemplate]:
        """
        Get a random eligible template, weighted by drop_weight.

        Args:
            item_type: Type of item ("weapon", "armor")
            rarity: Target rarity
            character_level: Player level for eligibility

        Returns:
            Random eligible BaseItemTemplate or None if none available
        """
        eligible = self.get_eligible_templates(item_type, rarity, character_level)

        if not eligible:
            logger.warning(
                "No templates match criteria",
                item_type=item_type,
                rarity=rarity,
                level=character_level
            )
            return None

        # Weighted random selection based on drop_weight
        weights = [t.drop_weight for t in eligible]
        return random.choices(eligible, weights=weights, k=1)[0]

    def get_all_weapons(self) -> Dict[str, BaseItemTemplate]:
        """
        Get all cached weapon templates.

        Returns:
            Dictionary of weapon templates
        """
        self._ensure_loaded()
        return self._weapon_cache.copy()

    def get_all_armor(self) -> Dict[str, BaseItemTemplate]:
        """
        Get all cached armor templates.

        Returns:
            Dictionary of armor templates
        """
        self._ensure_loaded()
        return self._armor_cache.copy()

    def clear_cache(self) -> None:
        """Clear the template cache, forcing reload on next access."""
        self._weapon_cache.clear()
        self._armor_cache.clear()
        self._loaded = False
        logger.debug("Base item template cache cleared")


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


def get_base_item_loader() -> BaseItemLoader:
    """
    Get the global BaseItemLoader instance.

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