"""
Item validation service for AI-granted items.

This module validates and resolves items that the AI grants to players during
gameplay, ensuring they meet character requirements and game balance rules.
"""

import uuid
from pathlib import Path
from typing import Optional

import structlog
import yaml

from app.models.items import Item
from app.models.enums import ItemType
from app.models.character import Character
from app.ai.response_parser import ItemGrant

logger = structlog.get_logger(__name__)


class ItemValidationError(Exception):
    """
    Exception raised when an item fails validation.

    Attributes:
        message: Human-readable error message
        item_grant: The ItemGrant that failed validation
        reason: Machine-readable reason code
    """

    def __init__(self, message: str, item_grant: ItemGrant, reason: str):
        super().__init__(message)
        self.message = message
        self.item_grant = item_grant
        self.reason = reason


class ItemValidator:
    """
    Validates and resolves items granted by the AI.

    This service:
    1. Resolves item references (by ID or creates generic items)
    2. Validates items against character requirements
    3. Logs validation failures for review
    """

    # Map of generic item type strings to ItemType enums
    TYPE_MAP = {
        "weapon": ItemType.WEAPON,
        "armor": ItemType.ARMOR,
        "consumable": ItemType.CONSUMABLE,
        "quest_item": ItemType.QUEST_ITEM,
    }

    def __init__(self, data_path: Optional[Path] = None):
        """
        Initialize the item validator.

        Args:
            data_path: Path to game data directory. Defaults to app/data/
        """
        if data_path is None:
            # Default to api/app/data/
            data_path = Path(__file__).parent.parent / "data"

        self.data_path = data_path
        self._item_registry: dict[str, dict] = {}
        self._generic_templates: dict[str, dict] = {}
        self._load_data()

        logger.info(
            "ItemValidator initialized",
            items_loaded=len(self._item_registry),
            generic_templates_loaded=len(self._generic_templates)
        )

    def _load_data(self) -> None:
        """Load item data from YAML files."""
        # Load main item registry if it exists
        items_file = self.data_path / "items.yaml"
        if items_file.exists():
            with open(items_file) as f:
                data = yaml.safe_load(f) or {}
                self._item_registry = data.get("items", {})

        # Load generic item templates
        generic_file = self.data_path / "generic_items.yaml"
        if generic_file.exists():
            with open(generic_file) as f:
                data = yaml.safe_load(f) or {}
                self._generic_templates = data.get("templates", {})

    def resolve_item(self, item_grant: ItemGrant) -> Item:
        """
        Resolve an ItemGrant to an actual Item instance.

        For existing items (by item_id), looks up from item registry.
        For generic items (by name/type), creates a new Item.

        Args:
            item_grant: The ItemGrant from AI response

        Returns:
            Resolved Item instance

        Raises:
            ItemValidationError: If item cannot be resolved
        """
        if item_grant.is_existing_item():
            return self._resolve_existing_item(item_grant)
        elif item_grant.is_generic_item():
            return self._create_generic_item(item_grant)
        else:
            raise ItemValidationError(
                "ItemGrant has neither item_id nor name",
                item_grant,
                "INVALID_ITEM_GRANT"
            )

    def _resolve_existing_item(self, item_grant: ItemGrant) -> Item:
        """
        Look up an existing item by ID.

        Args:
            item_grant: ItemGrant with item_id set

        Returns:
            Item instance from registry

        Raises:
            ItemValidationError: If item not found
        """
        item_id = item_grant.item_id

        if item_id not in self._item_registry:
            logger.warning(
                "Item not found in registry",
                item_id=item_id
            )
            raise ItemValidationError(
                f"Unknown item_id: {item_id}",
                item_grant,
                "ITEM_NOT_FOUND"
            )

        item_data = self._item_registry[item_id]

        # Convert to Item instance
        return Item.from_dict({
            "item_id": item_id,
            **item_data
        })

    def _create_generic_item(self, item_grant: ItemGrant) -> Item:
        """
        Create a generic item from AI-provided details.

        Generic items are simple items with no special stats,
        suitable for mundane objects like torches, food, etc.

        Args:
            item_grant: ItemGrant with name, type, description

        Returns:
            New Item instance

        Raises:
            ItemValidationError: If item type is invalid
        """
        # Validate item type
        item_type_str = (item_grant.item_type or "consumable").lower()
        if item_type_str not in self.TYPE_MAP:
            logger.warning(
                "Invalid item type from AI",
                item_type=item_type_str,
                item_name=item_grant.name
            )
            # Default to consumable for unknown types
            item_type_str = "consumable"

        item_type = self.TYPE_MAP[item_type_str]

        # Generate unique ID for this item instance
        item_id = f"generic_{uuid.uuid4().hex[:8]}"

        # Check if we have a template for this item name
        template = self._find_template(item_grant.name or "")

        if template:
            # Use template values as defaults
            return Item(
                item_id=item_id,
                name=item_grant.name or template.get("name", "Unknown Item"),
                item_type=item_type,
                description=item_grant.description or template.get("description", ""),
                value=item_grant.value or template.get("value", 0),
                is_tradeable=template.get("is_tradeable", True),
                required_level=template.get("required_level", 1),
            )
        else:
            # Create with provided values only
            return Item(
                item_id=item_id,
                name=item_grant.name or "Unknown Item",
                item_type=item_type,
                description=item_grant.description or "A simple item.",
                value=item_grant.value,
                is_tradeable=True,
                required_level=1,
            )

    def _find_template(self, item_name: str) -> Optional[dict]:
        """
        Find a generic item template by name.

        Uses case-insensitive partial matching.

        Args:
            item_name: Name of the item to find

        Returns:
            Template dict or None if not found
        """
        name_lower = item_name.lower()

        # Exact match first
        if name_lower in self._generic_templates:
            return self._generic_templates[name_lower]

        # Partial match
        for template_name, template in self._generic_templates.items():
            if template_name in name_lower or name_lower in template_name:
                return template

        return None

    def validate_item_for_character(
        self,
        item: Item,
        character: Character
    ) -> tuple[bool, Optional[str]]:
        """
        Validate that a character can receive an item.

        Checks:
        - Level requirements
        - Class restrictions

        Args:
            item: The Item to validate
            character: The Character to receive the item

        Returns:
            Tuple of (is_valid, error_message)
        """
        # Check level requirement
        if item.required_level > character.level:
            error_msg = (
                f"Item '{item.name}' requires level {item.required_level}, "
                f"but character is level {character.level}"
            )
            logger.warning(
                "Item validation failed: level requirement",
                item_name=item.name,
                required_level=item.required_level,
                character_level=character.level,
                character_name=character.name
            )
            return False, error_msg

        # Check class restriction
        if item.required_class:
            character_class = character.player_class.class_id
            if item.required_class.lower() != character_class.lower():
                error_msg = (
                    f"Item '{item.name}' requires class {item.required_class}, "
                    f"but character is {character_class}"
                )
                logger.warning(
                    "Item validation failed: class restriction",
                    item_name=item.name,
                    required_class=item.required_class,
                    character_class=character_class,
                    character_name=character.name
                )
                return False, error_msg

        return True, None

    def validate_and_resolve_item(
        self,
        item_grant: ItemGrant,
        character: Character
    ) -> tuple[Optional[Item], Optional[str]]:
        """
        Resolve an item grant and validate it for a character.

        This is the main entry point for processing AI-granted items.

        Args:
            item_grant: The ItemGrant from AI response
            character: The Character to receive the item

        Returns:
            Tuple of (Item if valid else None, error_message if invalid else None)
        """
        try:
            # Resolve the item
            item = self.resolve_item(item_grant)

            # Validate for character
            is_valid, error_msg = self.validate_item_for_character(item, character)

            if not is_valid:
                return None, error_msg

            logger.info(
                "Item validated successfully",
                item_name=item.name,
                item_id=item.item_id,
                character_name=character.name
            )
            return item, None

        except ItemValidationError as e:
            logger.warning(
                "Item resolution failed",
                error=e.message,
                reason=e.reason
            )
            return None, e.message


# Global instance for convenience
_validator_instance: Optional[ItemValidator] = None


def get_item_validator() -> ItemValidator:
    """
    Get or create the global ItemValidator instance.

    Returns:
        ItemValidator singleton instance
    """
    global _validator_instance
    if _validator_instance is None:
        _validator_instance = ItemValidator()
    return _validator_instance
