"""
Combat Loot Service - Orchestrates loot generation from combat encounters.

This service bridges the EnemyTemplate loot tables with both the StaticItemLoader
(for consumables and materials) and ItemGenerator (for procedural equipment).

The service calculates effective rarity based on:
- Party average level
- Enemy difficulty tier
- Character luck stat
- Optional loot bonus modifiers (from abilities, buffs, etc.)
"""

import random
from dataclasses import dataclass
from typing import List, Optional

from app.models.enemy import EnemyTemplate, LootEntry, LootType, EnemyDifficulty
from app.models.items import Item
from app.services.item_generator import get_item_generator, ItemGenerator
from app.services.static_item_loader import get_static_item_loader, StaticItemLoader
from app.utils.logging import get_logger

logger = get_logger(__file__)


# Difficulty tier rarity bonuses (converted to effective luck points)
# Higher difficulty enemies have better chances of dropping rare items
DIFFICULTY_RARITY_BONUS = {
    EnemyDifficulty.EASY: 0.0,
    EnemyDifficulty.MEDIUM: 0.05,
    EnemyDifficulty.HARD: 0.15,
    EnemyDifficulty.BOSS: 0.30,
}

# Multiplier for converting rarity bonus to effective luck points
# Each 0.05 bonus translates to +1 effective luck
LUCK_CONVERSION_FACTOR = 20


@dataclass
class LootContext:
    """
    Context for loot generation calculations.

    Provides all the factors that influence loot quality and rarity.

    Attributes:
        party_average_level: Average level of player characters in the encounter
        enemy_difficulty: Difficulty tier of the enemy being looted
        luck_stat: Party's luck stat (typically average or leader's luck)
        loot_bonus: Additional bonus from abilities, buffs, or modifiers (0.0 to 1.0)
    """
    party_average_level: int = 1
    enemy_difficulty: EnemyDifficulty = EnemyDifficulty.EASY
    luck_stat: int = 8
    loot_bonus: float = 0.0


class CombatLootService:
    """
    Service for generating combat loot drops.

    Supports two types of loot:
    - STATIC: Predefined items loaded from YAML (consumables, materials)
    - PROCEDURAL: Generated equipment with affixes (weapons, armor)

    The service handles:
    - Rolling for drops based on drop_chance
    - Loading static items via StaticItemLoader
    - Generating procedural items via ItemGenerator
    - Calculating effective rarity based on context
    """

    def __init__(
        self,
        item_generator: Optional[ItemGenerator] = None,
        static_loader: Optional[StaticItemLoader] = None
    ):
        """
        Initialize the combat loot service.

        Args:
            item_generator: ItemGenerator instance (uses global singleton if None)
            static_loader: StaticItemLoader instance (uses global singleton if None)
        """
        self.item_generator = item_generator or get_item_generator()
        self.static_loader = static_loader or get_static_item_loader()
        logger.info("CombatLootService initialized")

    def generate_loot_from_enemy(
        self,
        enemy: EnemyTemplate,
        context: LootContext
    ) -> List[Item]:
        """
        Generate all loot drops from a defeated enemy.

        Iterates through the enemy's loot table, rolling for each entry
        and generating appropriate items based on loot type.

        Args:
            enemy: The defeated enemy template
            context: Loot generation context (party level, luck, etc.)

        Returns:
            List of Item objects to add to player inventory
        """
        items = []

        for entry in enemy.loot_table:
            # Roll for drop chance
            if random.random() >= entry.drop_chance:
                continue

            # Determine quantity
            quantity = random.randint(entry.quantity_min, entry.quantity_max)

            if entry.loot_type == LootType.STATIC:
                # Static item: load from predefined templates
                static_items = self._generate_static_items(entry, quantity)
                items.extend(static_items)

            elif entry.loot_type == LootType.PROCEDURAL:
                # Procedural equipment: generate with ItemGenerator
                procedural_items = self._generate_procedural_items(
                    entry, quantity, context
                )
                items.extend(procedural_items)

        logger.info(
            "Loot generated from enemy",
            enemy_id=enemy.enemy_id,
            enemy_difficulty=enemy.difficulty.value,
            item_count=len(items),
            party_level=context.party_average_level,
            luck=context.luck_stat
        )

        return items

    def _generate_static_items(
        self,
        entry: LootEntry,
        quantity: int
    ) -> List[Item]:
        """
        Generate static items from a loot entry.

        Args:
            entry: The loot table entry
            quantity: Number of items to generate

        Returns:
            List of Item instances
        """
        items = []

        if not entry.item_id:
            logger.warning(
                "Static loot entry missing item_id",
                entry=entry.to_dict()
            )
            return items

        for _ in range(quantity):
            item = self.static_loader.get_item(entry.item_id)
            if item:
                items.append(item)
            else:
                logger.warning(
                    "Failed to load static item",
                    item_id=entry.item_id
                )

        return items

    def _generate_procedural_items(
        self,
        entry: LootEntry,
        quantity: int,
        context: LootContext
    ) -> List[Item]:
        """
        Generate procedural items from a loot entry.

        Calculates effective luck based on:
        - Base luck stat
        - Entry-specific rarity bonus
        - Difficulty bonus
        - Loot bonus from abilities/buffs

        Args:
            entry: The loot table entry
            quantity: Number of items to generate
            context: Loot generation context

        Returns:
            List of generated Item instances
        """
        items = []

        if not entry.item_type:
            logger.warning(
                "Procedural loot entry missing item_type",
                entry=entry.to_dict()
            )
            return items

        # Calculate effective luck for rarity roll
        effective_luck = self._calculate_effective_luck(entry, context)

        for _ in range(quantity):
            item = self.item_generator.generate_loot_drop(
                character_level=context.party_average_level,
                luck_stat=effective_luck,
                item_type=entry.item_type
            )
            if item:
                items.append(item)
            else:
                logger.warning(
                    "Failed to generate procedural item",
                    item_type=entry.item_type,
                    level=context.party_average_level
                )

        return items

    def _calculate_effective_luck(
        self,
        entry: LootEntry,
        context: LootContext
    ) -> int:
        """
        Calculate effective luck for rarity rolling.

        Combines multiple factors:
        - Base luck stat from party
        - Entry-specific rarity bonus (defined per loot entry)
        - Difficulty bonus (based on enemy tier)
        - Loot bonus (from abilities, buffs, etc.)

        The formula:
            effective_luck = base_luck + (entry_bonus + difficulty_bonus + loot_bonus) * FACTOR

        Args:
            entry: The loot table entry
            context: Loot generation context

        Returns:
            Effective luck stat for rarity calculations
        """
        # Get difficulty bonus
        difficulty_bonus = DIFFICULTY_RARITY_BONUS.get(
            context.enemy_difficulty, 0.0
        )

        # Sum all bonuses
        total_bonus = (
            entry.rarity_bonus +
            difficulty_bonus +
            context.loot_bonus
        )

        # Convert bonus to effective luck points
        bonus_luck = int(total_bonus * LUCK_CONVERSION_FACTOR)

        effective_luck = context.luck_stat + bonus_luck

        logger.debug(
            "Effective luck calculated",
            base_luck=context.luck_stat,
            entry_bonus=entry.rarity_bonus,
            difficulty_bonus=difficulty_bonus,
            loot_bonus=context.loot_bonus,
            total_bonus=total_bonus,
            effective_luck=effective_luck
        )

        return effective_luck

    def generate_boss_loot(
        self,
        enemy: EnemyTemplate,
        context: LootContext,
        guaranteed_drops: int = 1
    ) -> List[Item]:
        """
        Generate loot from a boss enemy with guaranteed drops.

        Boss enemies are guaranteed to drop at least one piece of equipment
        in addition to their normal loot table rolls.

        Args:
            enemy: The boss enemy template
            context: Loot generation context
            guaranteed_drops: Number of guaranteed equipment drops

        Returns:
            List of Item objects including guaranteed drops
        """
        # Generate normal loot first
        items = self.generate_loot_from_enemy(enemy, context)

        # Add guaranteed procedural drops for bosses
        if enemy.is_boss():
            context_for_boss = LootContext(
                party_average_level=context.party_average_level,
                enemy_difficulty=EnemyDifficulty.BOSS,
                luck_stat=context.luck_stat,
                loot_bonus=context.loot_bonus + 0.1  # Extra bonus for bosses
            )

            for _ in range(guaranteed_drops):
                # Alternate between weapon and armor
                item_type = random.choice(["weapon", "armor"])
                effective_luck = self._calculate_effective_luck(
                    LootEntry(
                        loot_type=LootType.PROCEDURAL,
                        item_type=item_type,
                        rarity_bonus=0.15  # Boss-tier bonus
                    ),
                    context_for_boss
                )

                item = self.item_generator.generate_loot_drop(
                    character_level=context.party_average_level,
                    luck_stat=effective_luck,
                    item_type=item_type
                )
                if item:
                    items.append(item)

            logger.info(
                "Boss loot generated",
                enemy_id=enemy.enemy_id,
                guaranteed_drops=guaranteed_drops,
                total_items=len(items)
            )

        return items


# Global singleton
_service_instance: Optional[CombatLootService] = None


def get_combat_loot_service() -> CombatLootService:
    """
    Get the global CombatLootService instance.

    Returns:
        Singleton CombatLootService instance
    """
    global _service_instance
    if _service_instance is None:
        _service_instance = CombatLootService()
    return _service_instance
