"""
Static Item Loader Service - YAML-based static item loading.

This service loads predefined item definitions (consumables, materials, quest items)
from YAML files, providing a way to reference specific items by ID in loot tables.

Static items differ from procedurally generated items in that they have fixed
properties defined in YAML rather than randomly generated affixes.
"""

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

from app.models.items import Item
from app.models.effects import Effect
from app.models.enums import ItemType, ItemRarity, EffectType, DamageType
from app.utils.logging import get_logger

logger = get_logger(__file__)


class StaticItemLoader:
    """
    Loads and manages static item definitions from YAML configuration files.

    Static items are predefined items (consumables, materials, quest items)
    that can be referenced by item_id in enemy loot tables.

    Items are loaded from:
    - api/app/data/static_items/consumables.yaml
    - api/app/data/static_items/materials.yaml

    Each call to get_item() creates a new Item instance with a unique ID,
    so multiple drops of the same item_id become distinct inventory items.
    """

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

        Args:
            data_dir: Path to directory containing static item YAML files.
                     Defaults to /app/data/static_items/
        """
        if data_dir is None:
            # Default to app/data/static_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" / "static_items")

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

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

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

    def _load_all(self) -> None:
        """Load all static item YAML files."""
        if not self.data_dir.exists():
            logger.warning(
                "Static items directory not found",
                path=str(self.data_dir)
            )
            self._loaded = True
            return

        # Load all YAML files in the directory
        for yaml_file in self.data_dir.glob("*.yaml"):
            self._load_file(yaml_file)

        self._loaded = True
        logger.info("Static items loaded", count=len(self._cache))

    def _load_file(self, yaml_file: Path) -> None:
        """
        Load items from a single YAML file.

        Args:
            yaml_file: Path to the YAML file
        """
        try:
            with open(yaml_file, 'r') as f:
                data = yaml.safe_load(f)

            if data is None:
                logger.warning("Empty YAML file", file=str(yaml_file))
                return

            items = data.get("items", {})
            for item_id, item_data in items.items():
                # Store the template data with its ID
                item_data["_item_id"] = item_id
                self._cache[item_id] = item_data

            logger.debug(
                "Static items loaded from file",
                file=str(yaml_file),
                count=len(items)
            )

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

    def get_item(self, item_id: str, quantity: int = 1) -> Optional[Item]:
        """
        Get an item instance by ID.

        Creates a new Item instance with a unique ID for each call,
        so multiple drops become distinct inventory items.

        Args:
            item_id: The static item ID (e.g., "health_potion_small")
            quantity: Requested quantity (not used for individual item,
                     but available for future stackable item support)

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

        template = self._cache.get(item_id)
        if template is None:
            logger.warning("Static item not found", item_id=item_id)
            return None

        # Create new instance with unique ID
        instance_id = f"{item_id}_{uuid.uuid4().hex[:8]}"

        # Parse item type
        item_type_str = template.get("item_type", "quest_item")
        try:
            item_type = ItemType(item_type_str)
        except ValueError:
            logger.warning(
                "Unknown item type, defaulting to quest_item",
                item_type=item_type_str,
                item_id=item_id
            )
            item_type = ItemType.QUEST_ITEM

        # Parse rarity
        rarity_str = template.get("rarity", "common")
        try:
            rarity = ItemRarity(rarity_str)
        except ValueError:
            logger.warning(
                "Unknown rarity, defaulting to common",
                rarity=rarity_str,
                item_id=item_id
            )
            rarity = ItemRarity.COMMON

        # Parse effects if present
        effects = []
        for effect_data in template.get("effects_on_use", []):
            try:
                effect = self._parse_effect(effect_data)
                if effect:
                    effects.append(effect)
            except Exception as e:
                logger.warning(
                    "Failed to parse effect",
                    item_id=item_id,
                    error=str(e)
                )

        # Parse stat bonuses if present
        stat_bonuses = template.get("stat_bonuses", {})

        # Parse damage type if present (for weapons)
        damage_type = None
        damage_type_str = template.get("damage_type")
        if damage_type_str:
            try:
                damage_type = DamageType(damage_type_str)
            except ValueError:
                logger.warning(
                    "Unknown damage type, defaulting to physical",
                    damage_type=damage_type_str,
                    item_id=item_id
                )
                damage_type = DamageType.PHYSICAL

        return Item(
            item_id=instance_id,
            name=template.get("name", item_id),
            item_type=item_type,
            rarity=rarity,
            description=template.get("description", ""),
            value=template.get("value", 1),
            is_tradeable=template.get("is_tradeable", True),
            stat_bonuses=stat_bonuses,
            effects_on_use=effects,
            # Weapon-specific fields
            damage=template.get("damage", 0),
            spell_power=template.get("spell_power", 0),
            damage_type=damage_type,
            crit_chance=template.get("crit_chance", 0.05),
            crit_multiplier=template.get("crit_multiplier", 2.0),
            # Armor-specific fields
            defense=template.get("defense", 0),
            resistance=template.get("resistance", 0),
            # Level requirements
            required_level=template.get("required_level", 1),
        )

    def _parse_effect(self, effect_data: Dict) -> Optional[Effect]:
        """
        Parse an effect from YAML data.

        Supports simplified YAML format where effect_type is a string.

        Args:
            effect_data: Effect definition from YAML

        Returns:
            Effect instance or None if parsing fails
        """
        # Parse effect type
        effect_type_str = effect_data.get("effect_type", "buff")
        try:
            effect_type = EffectType(effect_type_str)
        except ValueError:
            logger.warning(
                "Unknown effect type",
                effect_type=effect_type_str
            )
            return None

        # Generate effect ID if not provided
        effect_id = effect_data.get(
            "effect_id",
            f"effect_{uuid.uuid4().hex[:8]}"
        )

        return Effect(
            effect_id=effect_id,
            name=effect_data.get("name", "Unknown Effect"),
            effect_type=effect_type,
            duration=effect_data.get("duration", 1),
            power=effect_data.get("power", 0),
            stacks=effect_data.get("stacks", 1),
            max_stacks=effect_data.get("max_stacks", 5),
        )

    def get_all_item_ids(self) -> List[str]:
        """
        Get list of all available static item IDs.

        Returns:
            List of item_id strings
        """
        self._ensure_loaded()
        return list(self._cache.keys())

    def has_item(self, item_id: str) -> bool:
        """
        Check if an item ID exists.

        Args:
            item_id: The item ID to check

        Returns:
            True if item exists in cache
        """
        self._ensure_loaded()
        return item_id in self._cache

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


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


def get_static_item_loader() -> StaticItemLoader:
    """
    Get the global StaticItemLoader instance.

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