"""
Item affix system for procedural item generation.

This module defines affixes (prefixes and suffixes) that can be attached to items
to provide stat bonuses and generate Diablo-style names like "Flaming Dagger of Strength".
"""

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

from app.models.enums import AffixType, AffixTier, DamageType, ItemRarity


@dataclass
class Affix:
    """
    Represents a single item affix (prefix or suffix).

    Affixes provide stat bonuses and contribute to item naming.
    Prefixes appear before the item name: "Flaming Dagger"
    Suffixes appear after the item name: "Dagger of Strength"

    Attributes:
        affix_id: Unique identifier (e.g., "flaming", "of_strength")
        name: Display name for the affix (e.g., "Flaming", "of Strength")
        affix_type: PREFIX or SUFFIX
        tier: MINOR, MAJOR, or LEGENDARY (determines bonus magnitude)
        description: Human-readable description of the affix effect

        Stat Bonuses:
        stat_bonuses: Dict mapping stat name to bonus value
                     Example: {"strength": 2, "constitution": 1}
        defense_bonus: Direct defense bonus
        resistance_bonus: Direct resistance bonus

        Weapon Properties (PREFIX only, elemental):
        damage_bonus: Flat damage bonus added to weapon
        damage_type: Elemental damage type (fire, ice, etc.)
        elemental_ratio: Portion of damage converted to elemental (0.0-1.0)
        crit_chance_bonus: Added to weapon crit chance
        crit_multiplier_bonus: Added to crit damage multiplier

        Restrictions:
        allowed_item_types: Empty list = all types allowed
        required_rarity: Minimum rarity to roll this affix (for legendary-only)
    """

    affix_id: str
    name: str
    affix_type: AffixType
    tier: AffixTier
    description: str = ""

    # Stat bonuses (applies to any item)
    stat_bonuses: Dict[str, int] = field(default_factory=dict)
    defense_bonus: int = 0
    resistance_bonus: int = 0

    # Weapon-specific bonuses
    damage_bonus: int = 0
    damage_type: Optional[DamageType] = None
    elemental_ratio: float = 0.0
    crit_chance_bonus: float = 0.0
    crit_multiplier_bonus: float = 0.0

    # Restrictions
    allowed_item_types: List[str] = field(default_factory=list)
    required_rarity: Optional[str] = None

    def applies_elemental_damage(self) -> bool:
        """
        Check if this affix converts damage to elemental.

        Returns:
            True if affix adds elemental damage component
        """
        return self.damage_type is not None and self.elemental_ratio > 0.0

    def is_legendary_only(self) -> bool:
        """
        Check if this affix only rolls on legendary items.

        Returns:
            True if affix requires legendary rarity
        """
        return self.required_rarity == "legendary"

    def can_apply_to(self, item_type: str, rarity: str) -> bool:
        """
        Check if this affix can be applied to an item.

        Args:
            item_type: Type of item ("weapon", "armor", etc.)
            rarity: Item rarity ("common", "rare", "epic", "legendary")

        Returns:
            True if affix can be applied, False otherwise
        """
        # Check rarity requirement
        if self.required_rarity and rarity != self.required_rarity:
            return False

        # Check item type restriction
        if self.allowed_item_types and item_type not in self.allowed_item_types:
            return False

        return True

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

        Returns:
            Dictionary containing all affix data
        """
        data = asdict(self)
        data["affix_type"] = self.affix_type.value
        data["tier"] = self.tier.value
        if self.damage_type:
            data["damage_type"] = self.damage_type.value
        return data

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

        Args:
            data: Dictionary containing affix data

        Returns:
            Affix instance
        """
        affix_type = AffixType(data["affix_type"])
        tier = AffixTier(data["tier"])
        damage_type = DamageType(data["damage_type"]) if data.get("damage_type") else None

        return cls(
            affix_id=data["affix_id"],
            name=data["name"],
            affix_type=affix_type,
            tier=tier,
            description=data.get("description", ""),
            stat_bonuses=data.get("stat_bonuses", {}),
            defense_bonus=data.get("defense_bonus", 0),
            resistance_bonus=data.get("resistance_bonus", 0),
            damage_bonus=data.get("damage_bonus", 0),
            damage_type=damage_type,
            elemental_ratio=data.get("elemental_ratio", 0.0),
            crit_chance_bonus=data.get("crit_chance_bonus", 0.0),
            crit_multiplier_bonus=data.get("crit_multiplier_bonus", 0.0),
            allowed_item_types=data.get("allowed_item_types", []),
            required_rarity=data.get("required_rarity"),
        )

    def __repr__(self) -> str:
        """String representation of the affix."""
        bonuses = []
        if self.stat_bonuses:
            bonuses.append(f"stats={self.stat_bonuses}")
        if self.damage_bonus:
            bonuses.append(f"dmg+{self.damage_bonus}")
        if self.defense_bonus:
            bonuses.append(f"def+{self.defense_bonus}")
        if self.applies_elemental_damage():
            bonuses.append(f"{self.damage_type.value}={self.elemental_ratio:.0%}")

        bonus_str = ", ".join(bonuses) if bonuses else "no bonuses"
        return f"Affix({self.name}, {self.affix_type.value}, {self.tier.value}, {bonus_str})"


@dataclass
class BaseItemTemplate:
    """
    Template for base items used in procedural generation.

    Base items define the foundation (e.g., "Dagger", "Longsword", "Chainmail")
    that affixes attach to during item generation.

    Attributes:
        template_id: Unique identifier (e.g., "dagger", "longsword")
        name: Display name (e.g., "Dagger", "Longsword")
        item_type: Category ("weapon", "armor")
        description: Flavor text for the base item

        Base Stats:
        base_damage: Base weapon damage (weapons only)
        base_defense: Base armor defense (armor only)
        base_resistance: Base magic resistance (armor only)
        base_value: Base gold value before rarity/affix modifiers

        Weapon Properties:
        damage_type: Primary damage type (usually "physical")
        crit_chance: Base critical hit chance
        crit_multiplier: Base critical damage multiplier

        Generation:
        required_level: Minimum character level for this template
        drop_weight: Weighting for random selection (higher = more common)
        min_rarity: Minimum rarity this template can generate at
    """

    template_id: str
    name: str
    item_type: str  # "weapon" or "armor"
    description: str = ""

    # Base stats
    base_damage: int = 0
    base_spell_power: int = 0  # For magical weapons (staves, wands)
    base_defense: int = 0
    base_resistance: int = 0
    base_value: int = 10

    # Weapon properties
    damage_type: str = "physical"
    crit_chance: float = 0.05
    crit_multiplier: float = 2.0

    # Generation settings
    required_level: int = 1
    drop_weight: float = 1.0
    min_rarity: str = "common"

    def can_generate_at_rarity(self, rarity: str) -> bool:
        """
        Check if this template can generate at a given rarity.

        Some templates (like greatswords) may only drop at rare+.

        Args:
            rarity: Target rarity to check

        Returns:
            True if template can generate at this rarity
        """
        rarity_order = ["common", "uncommon", "rare", "epic", "legendary"]
        min_index = rarity_order.index(self.min_rarity)
        target_index = rarity_order.index(rarity)
        return target_index >= min_index

    def can_drop_for_level(self, character_level: int) -> bool:
        """
        Check if this template can drop for a character level.

        Args:
            character_level: Character's current level

        Returns:
            True if template can drop for this level
        """
        return character_level >= self.required_level

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

        Returns:
            Dictionary containing all template data
        """
        return asdict(self)

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

        Args:
            data: Dictionary containing template data

        Returns:
            BaseItemTemplate instance
        """
        return cls(
            template_id=data["template_id"],
            name=data["name"],
            item_type=data["item_type"],
            description=data.get("description", ""),
            base_damage=data.get("base_damage", 0),
            base_spell_power=data.get("base_spell_power", 0),
            base_defense=data.get("base_defense", 0),
            base_resistance=data.get("base_resistance", 0),
            base_value=data.get("base_value", 10),
            damage_type=data.get("damage_type", "physical"),
            crit_chance=data.get("crit_chance", 0.05),
            crit_multiplier=data.get("crit_multiplier", 2.0),
            required_level=data.get("required_level", 1),
            drop_weight=data.get("drop_weight", 1.0),
            min_rarity=data.get("min_rarity", "common"),
        )

    def __repr__(self) -> str:
        """String representation of the template."""
        if self.item_type == "weapon":
            return (
                f"BaseItemTemplate({self.name}, weapon, dmg={self.base_damage}, "
                f"crit={self.crit_chance*100:.0f}%, lvl={self.required_level})"
            )
        elif self.item_type == "armor":
            return (
                f"BaseItemTemplate({self.name}, armor, def={self.base_defense}, "
                f"res={self.base_resistance}, lvl={self.required_level})"
            )
        else:
            return f"BaseItemTemplate({self.name}, {self.item_type})"
