"""
FreeExile Monster Loot Drop Generation and Ground Pickup Service.
Manages authoritative item drop generation, spatial ground tracking, and pickup resolution.
Follows Engineering Standards 2026: Soft cap <= 350 lines, Zero-trust server authority.
"""

from __future__ import annotations
import random
import time
import uuid
from typing import Dict, List, Optional, Tuple, Any

from .inventory_types import ItemType, ItemRarity, InventoryItem
from .inventory_service import InventoryService
from .loot_filter_types import GroundLootDrop, LootFilterConfig
from .spatial_grid_types import get_default_item_dimensions


class LootDropService:
    """Authoritative service handling ground loot spawns, filter checks, and inventory pickups."""

    def __init__(self, inventory_service: Optional[InventoryService] = None) -> None:
        self.inventory_service = inventory_service or InventoryService()
        self._ground_drops: Dict[str, GroundLootDrop] = {}

    def register_ground_drop(self, drop: GroundLootDrop) -> None:
        """Registers a spawned ground drop entity."""
        self._ground_drops[drop.drop_id] = drop

    def get_ground_drop(self, drop_id: str) -> Optional[GroundLootDrop]:
        """Retrieves a ground drop by drop_id."""
        return self._ground_drops.get(drop_id)

    def get_active_ground_drops(self) -> List[GroundLootDrop]:
        """Returns all non-collected ground drops."""
        return [d for d in self._ground_drops.values() if not d.is_collected]

    def remove_ground_drop(self, drop_id: str) -> bool:
        """Deletes a ground drop from the world."""
        if drop_id in self._ground_drops:
            del self._ground_drops[drop_id]
            return True
        return False

    def generate_monster_drops(
        self,
        monster_id: str,
        monster_level: int,
        is_boss: bool = False,
        drop_pos: Tuple[float, float] = (0.0, 0.0),
        allocation_mode: Optional[Any] = None,
        party_members: Optional[List[str]] = None,
        item_quantity_mult: float = 1.0,
        currency_quantity_mult: float = 1.0,
        rarity_mult: float = 1.0,
    ) -> List[GroundLootDrop]:
        """Generates authentic drops according to monster rank, level, drop tables, and party scaling."""
        drops: List[GroundLootDrop] = []
        base_x, base_y = drop_pos

        mode_val = "FREE_FOR_ALL"
        if allocation_mode is not None:
            mode_val = allocation_mode.value if hasattr(allocation_mode, "value") else str(allocation_mode)
        mode_val = mode_val.upper()

        base_drops = random.randint(3, 5) if is_boss else random.randint(1, 2)
        num_drops = max(1, int(round(base_drops * max(0.1, item_quantity_mult))))

        # 1. Guaranteed Currency Drop
        curr_drop = self._create_currency_drop(monster_level, is_boss, base_x, base_y, currency_quantity_mult)
        drops.append(curr_drop)

        # 2. Equipment or Additional Relic Drops
        for _ in range(num_drops - 1):
            offset_x = base_x + (random.random() - 0.5) * 1.6
            offset_y = base_y + (random.random() - 0.5) * 1.6
            gear_chance = 0.45 * rarity_mult
            if is_boss or random.random() < min(0.85, gear_chance):
                gear_drop = self._create_gear_drop(monster_level, is_boss, offset_x, offset_y, rarity_mult)
                drops.append(gear_drop)
            else:
                extra_curr = self._create_currency_drop(monster_level, False, offset_x, offset_y, currency_quantity_mult)
                drops.append(extra_curr)

        # 3. Round-Robin Loot Allocation Assignment
        members_list = [m for m in (party_members or []) if m]
        now = time.time()
        for idx, drop in enumerate(drops):
            if members_list:
                assigned_player = members_list[idx % len(members_list)]
                drop.allocated_player_id = assigned_player
                drop.allocation_mode = mode_val
                if mode_val == "SHORT":
                    drop.allocation_expires_at = drop.spawned_at + 5.0
                else:
                    drop.allocation_expires_at = 0.0
            else:
                drop.allocated_player_id = ""
                drop.allocation_mode = "FREE_FOR_ALL"
                drop.allocation_expires_at = 0.0

            self.register_ground_drop(drop)

        return drops

    def pickup_ground_drop(
        self,
        account_id: str,
        character_id: str,
        drop_id: str,
        filter_config: Optional[LootFilterConfig] = None
    ) -> Tuple[bool, str, Optional[int]]:
        """Authoritatively collects a ground drop into character survival backpack with allocation check."""
        drop = self.get_ground_drop(drop_id)
        if not drop:
            return False, f"Ground drop {drop_id} does not exist", None

        if drop.is_collected:
            return False, f"Ground drop {drop_id} already collected", None

        # Party Loot Allocation verification
        now = time.time()
        can_pickup = drop.can_player_pickup(character_id, now) or drop.can_player_pickup(account_id, now)
        if not can_pickup:
            return False, f"Ground drop {drop_id} is allocated to another player", None

        # Filter check
        if filter_config and not filter_config.evaluate_item(drop.item):
            drop.filtered_out = True
            return False, f"Item {drop.item.name} filtered out by loot filter", None

        # Insert into inventory bag
        success, msg, slot = self.inventory_service.add_item_to_inventory(
            account_id=account_id,
            character_id=character_id,
            item=drop.item
        )

        if success:
            drop.is_collected = True
            return True, f"Picked up {drop.item.name} into slot {slot}", slot

        return False, msg, None

    # --- PRIVATE HELPERS ---

    def _create_currency_drop(
        self, monster_level: int, is_boss: bool, wx: float, wy: float, currency_mult: float = 1.0
    ) -> GroundLootDrop:
        currencies = [
            ("curr_blood_soul_gem", "Huyết Hồn Thạch", ItemRarity.LINH_PHAM, "#29b6f6", 90),
            ("curr_chaos_stone", "Hỗn Nguyên Thạch", ItemRarity.CUC_PHAM, "#fbbf24", 130),
            ("curr_nether_bone_nail", "U Minh Cốt Đinh", ItemRarity.CUC_PHAM, "#f59e0b", 120),
            ("curr_primal_bone_seal", "Cổ Cốt Ấn", ItemRarity.CUC_PHAM, "#f59e0b", 140),
        ]
        if is_boss and random.random() < 0.15:
            c_id, c_name, rarity, color, beam_h = (
                "curr_void_mirror_shard", "Mảnh Kính Thái Hư", ItemRarity.THAN_THOAI, "#818cf8", 220
            )
        else:
            c_id, c_name, rarity, color, beam_h = random.choice(currencies)

        base_count = random.randint(3, 8) if is_boss else random.randint(1, 3)
        count = max(1, int(round(base_count * max(0.1, currency_mult))))
        item = InventoryItem(
            item_uuid=f"drop_{uuid.uuid4().hex[:10]}",
            item_id=c_id,
            name=c_name,
            item_type=ItemType.CURRENCY,
            rarity=rarity,
            item_level=1,
            width=1,
            height=1,
            stack_count=count,
            max_stack=5000
        )
        return GroundLootDrop(
            drop_id=f"gd_{uuid.uuid4().hex[:8]}",
            item=item,
            world_x=wx,
            world_y=wy,
            beam_color=color,
            beam_height=beam_h,
            spawned_at=time.time()
        )

    def _create_gear_drop(
        self, monster_level: int, is_boss: bool, wx: float, wy: float, rarity_mult: float = 1.0
    ) -> GroundLootDrop:
        gear_templates = [
            ("wpn_infernal_blade", "Xích Diễm Trảm Mã Đao", ItemType.WEAPON, 2, 4),
            ("wpn_bone_bow", "Tích Cốt Ma Hồn Cung", ItemType.WEAPON, 2, 4),
            ("wpn_azure_sword", "Thanh Phong Cổ Kiếm", ItemType.WEAPON, 1, 3),
            ("arm_yaksha_armor", "Dạ Xoa Huyết Giáp", ItemType.ARMOR, 2, 3),
            ("arm_crane_helm", "Linh Hạc Quan", ItemType.ARMOR, 2, 2),
            ("arm_cloud_boots", "Huyễn Ảnh Báo Thân Hài", ItemType.ARMOR, 2, 2),
        ]
        g_id, g_name, g_type, w, h = random.choice(gear_templates)
        cuc_pham_chance = min(0.75, 0.3 * rarity_mult)
        rarity = ItemRarity.CUC_PHAM if is_boss or random.random() < cuc_pham_chance else ItemRarity.LINH_PHAM
        color = "#fbbf24" if rarity == ItemRarity.CUC_PHAM else "#29b6f6"
        beam_h = 150 if rarity == ItemRarity.CUC_PHAM else 90

        item = InventoryItem(
            item_uuid=f"gear_{uuid.uuid4().hex[:10]}",
            item_id=g_id,
            name=g_name,
            item_type=g_type,
            rarity=rarity,
            item_level=monster_level,
            width=w,
            height=h,
            stack_count=1,
            max_stack=1
        )
        return GroundLootDrop(
            drop_id=f"gd_{uuid.uuid4().hex[:8]}",
            item=item,
            world_x=wx,
            world_y=wy,
            beam_color=color,
            beam_height=beam_h,
            spawned_at=time.time()
        )
