#!/usr/bin/env python3
"""Item Manager - Coordinates item pickup/drop across multiple widgets

This module provides:
    - ItemManager: Central coordinator for item transfers
    - ItemSlot: Frame-based equipment slot widget
    - Item: Data class for item properties

The manager allows entities to move between:
    - Grid widgets (inventory, shop displays)
    - ItemSlot widgets (equipment slots)
    - The cursor (when held)

Usage:
    manager = ItemManager()
    manager.register_grid("inventory", inventory_grid)
    manager.register_slot("weapon", weapon_slot)

    # Widgets call manager methods on click:
    manager.pickup_from_grid("inventory", cell_pos)
    manager.drop_to_slot("weapon")
"""
import mcrfpy


class ItemEntity(mcrfpy.Entity):
    """Entity subclass that can hold item data."""

    def __init__(self):
        super().__init__()
        self.item = None  # Will be set after adding to grid


class Item:
    """Data class representing an item's properties."""

    def __init__(self, sprite_index, name, **stats):
        """
        Args:
            sprite_index: Texture sprite index
            name: Display name
            **stats: Arbitrary stats (atk, def, int, price, etc.)
        """
        self.sprite_index = sprite_index
        self.name = name
        self.stats = stats

    def __repr__(self):
        return f"Item({self.name}, sprite={self.sprite_index})"

    @property
    def price(self):
        return self.stats.get('price', 0)

    @property
    def atk(self):
        return self.stats.get('atk', 0)

    @property
    def def_(self):
        return self.stats.get('def', 0)

    @property
    def slot_type(self):
        """Equipment slot this item fits in (weapon, shield, etc.)"""
        return self.stats.get('slot_type', None)


class ItemSlot(mcrfpy.Frame):
    """A frame that can hold a single item (for equipment slots)."""

    def __init__(self, pos, size=(64, 64), slot_type=None,
                 empty_color=(60, 60, 70), valid_color=(60, 100, 60),
                 invalid_color=(100, 60, 60), filled_color=(80, 80, 90)):
        """
        Args:
            pos: Position tuple (x, y)
            size: Size tuple (w, h)
            slot_type: Type of items this slot accepts (e.g., 'weapon', 'shield')
            empty_color: Color when empty and not hovered
            valid_color: Color when valid item is hovering
            invalid_color: Color when invalid item is hovering
            filled_color: Color when containing an item
        """
        super().__init__(pos, size, fill_color=empty_color, outline=2, outline_color=(100, 100, 120))

        self.slot_type = slot_type
        self.empty_color = empty_color
        self.valid_color = valid_color
        self.invalid_color = invalid_color
        self.filled_color = filled_color

        # Item stored in this slot
        self.item = None
        self.item_sprite = None

        # Manager reference (set by manager.register_slot)
        self.manager = None
        self.slot_name = None

        # Setup sprite for displaying item
        sprite_scale = min(size[0], size[1]) / 16.0 * 0.8  # 80% of slot size
        self.item_sprite = mcrfpy.Sprite(
            pos=(size[0] * 0.1, size[1] * 0.1),
            texture=mcrfpy.default_texture,
            sprite_index=0
        )
        self.item_sprite.scale = sprite_scale
        self.item_sprite.visible = False
        self.children.append(self.item_sprite)

        # Setup events
        self.on_click = self._on_click
        self.on_enter = self._on_enter
        self.on_exit = self._on_exit

    def set_item(self, item):
        """Set the item in this slot."""
        self.item = item
        if item:
            self.item_sprite.sprite_index = item.sprite_index
            self.item_sprite.visible = True
            self.fill_color = self.filled_color
        else:
            self.item_sprite.visible = False
            self.fill_color = self.empty_color

    def clear(self):
        """Remove item from slot."""
        self.set_item(None)

    def can_accept(self, item):
        """Check if this slot can accept the given item."""
        if self.slot_type is None:
            return True  # Accepts anything
        if item is None:
            return True
        return item.slot_type == self.slot_type

    def _on_click(self, pos, button, action):
        if action != mcrfpy.InputState.PRESSED or button != mcrfpy.MouseButton.LEFT:
            return
        if self.manager:
            self.manager.handle_slot_click(self.slot_name)

    def _on_enter(self, pos, *args):
        if self.manager and self.manager.held_item:
            if self.can_accept(self.manager.held_item):
                self.fill_color = self.valid_color
            else:
                self.fill_color = self.invalid_color

    def _on_exit(self, pos, *args):
        if self.item:
            self.fill_color = self.filled_color
        else:
            self.fill_color = self.empty_color


class ItemManager:
    """Coordinates item pickup and placement across multiple widgets."""

    def __init__(self, scene):
        """
        Args:
            scene: The mcrfpy.Scene to add cursor elements to
        """
        self.scene = scene
        self.grids = {}       # name -> (grid, {(x,y): Item})
        self.slots = {}       # name -> ItemSlot

        # Currently held item state
        self.held_item = None
        self.held_source = None      # ('grid', name, pos) or ('slot', name)
        self.held_entity = None      # For grid sources, the original entity

        # Cursor sprite setup
        self.cursor_frame = mcrfpy.Frame(
            pos=(0, 0),
            size=(64, 64),
            fill_color=(0, 0, 0, 0),
            outline=0
        )
        self.cursor_sprite = mcrfpy.Sprite(
            pos=(0, 0),
            texture=mcrfpy.default_texture,
            sprite_index=0
        )
        self.cursor_sprite.scale = 4.0
        self.cursor_sprite.visible = False
        self.cursor_frame.children.append(self.cursor_sprite)
        scene.children.append(self.cursor_frame)

        # Callbacks for UI updates
        self.on_pickup = None       # Called when item picked up
        self.on_drop = None         # Called when item dropped
        self.on_cancel = None       # Called when pickup cancelled

    def register_grid(self, name, grid, items=None):
        """Register a grid for item management.

        Args:
            name: Unique name for this grid
            grid: mcrfpy.Grid instance
            items: Optional dict of {(x, y): Item} for initial items
        """
        item_map = items or {}

        # Add a color layer for highlighting if not present
        color_layer = grid.add_layer('color', z_index=-1)

        self.grids[name] = (grid, item_map, color_layer)

        # Setup grid event handlers
        grid.on_click = lambda pos, btn, act: self._on_grid_click(name, pos, btn, act)
        grid.on_cell_enter = lambda cell_pos: self._on_grid_cell_enter(name, cell_pos)
        grid.on_move = self._on_move

    def register_slot(self, name, slot):
        """Register an ItemSlot for item management.

        Args:
            name: Unique name for this slot
            slot: ItemSlot instance
        """
        self.slots[name] = slot
        slot.manager = self
        slot.slot_name = name

    def add_item_to_grid(self, grid_name, pos, item):
        """Add an item to a grid at the specified position.

        Args:
            grid_name: Name of registered grid
            pos: (x, y) cell position
            item: Item instance

        Returns:
            True if successful, False if cell occupied
        """
        if grid_name not in self.grids:
            return False

        grid, item_map, color_layer = self.grids[grid_name]

        if pos in item_map:
            return False  # Cell occupied

        # Create entity
        entity = ItemEntity()
        grid.entities.append(entity)
        entity.grid_pos = pos  # Use grid_pos for tile coordinates
        entity.sprite_index = item.sprite_index
        entity.item = item  # Store item reference on our subclass
        item_map[pos] = item

        return True

    def get_item_at(self, grid_name, pos):
        """Get item at grid position, or None."""
        if grid_name not in self.grids:
            return None
        grid, item_map, color_layer = self.grids[grid_name]
        return item_map.get(pos)

    def _get_entity_at(self, grid_name, pos):
        """Get entity at grid position."""
        grid, _, _ = self.grids[grid_name]
        for entity in grid.entities:
            gp = entity.grid_pos
            if (int(gp[0]), int(gp[1])) == pos:
                return entity
        return None

    def _on_grid_click(self, grid_name, pos, button, action):
        """Handle click on a registered grid."""
        if action != mcrfpy.InputState.PRESSED:
            return

        if button == mcrfpy.MouseButton.RIGHT:
            self.cancel_pickup()
            return

        if button != mcrfpy.MouseButton.LEFT:
            return

        grid, item_map, color_layer = self.grids[grid_name]

        # Convert screen pos to cell
        cell_size = 16 * grid.zoom
        x = int((pos[0] - grid.x) / cell_size)
        y = int((pos[1] - grid.y) / cell_size)
        grid_w, grid_h = grid.grid_size

        if not (0 <= x < grid_w and 0 <= y < grid_h):
            return

        cell_pos = (x, y)

        if self.held_item is None:
            # Try to pick up
            if cell_pos in item_map:
                self._pickup_from_grid(grid_name, cell_pos)
        else:
            # Try to drop
            if cell_pos not in item_map:
                self._drop_to_grid(grid_name, cell_pos)

    def _pickup_from_grid(self, grid_name, pos):
        """Pick up item from grid cell."""
        grid, item_map, color_layer = self.grids[grid_name]
        item = item_map[pos]
        entity = self._get_entity_at(grid_name, pos)

        if entity:
            entity.visible = False

        self.held_item = item
        self.held_source = ('grid', grid_name, pos)
        self.held_entity = entity

        # Setup cursor
        self.cursor_sprite.sprite_index = item.sprite_index
        self.cursor_sprite.visible = True

        # Highlight source cell
        color_layer.set(pos, (255, 255, 100, 200))

        if self.on_pickup:
            self.on_pickup(item, grid_name, pos)

    def _drop_to_grid(self, grid_name, pos):
        """Drop held item to grid cell."""
        grid, item_map, color_layer = self.grids[grid_name]

        # Check if same grid and moving item
        if self.held_source[0] == 'grid':
            source_grid_name = self.held_source[1]
            source_pos = self.held_source[2]

            # Remove from source
            source_grid, source_map, source_color_layer = self.grids[source_grid_name]
            if source_pos in source_map:
                del source_map[source_pos]

            # Clear source highlight
            source_color_layer.set(source_pos, (0, 0, 0, 0))

            # Move or recreate entity
            if grid_name == source_grid_name and self.held_entity:
                # Same grid - just move entity
                self.held_entity.grid_pos = pos
                self.held_entity.visible = True
            else:
                # Different grid - remove old, create new
                if self.held_entity:
                    # Remove from source grid
                    for i, e in enumerate(source_grid.entities):
                        if e is self.held_entity:
                            source_grid.entities.pop(i)
                            break

                # Create in target grid
                entity = ItemEntity()
                grid.entities.append(entity)
                entity.grid_pos = pos  # Use grid_pos for tile coordinates
                entity.sprite_index = self.held_item.sprite_index
                entity.item = self.held_item
                self.held_entity = None

        elif self.held_source[0] == 'slot':
            # Moving from slot to grid
            slot_name = self.held_source[1]
            slot = self.slots[slot_name]
            slot.clear()

            # Create entity in grid
            entity = ItemEntity()
            grid.entities.append(entity)
            entity.grid_pos = pos  # Use grid_pos for tile coordinates
            entity.sprite_index = self.held_item.sprite_index
            entity.item = self.held_item

        # Add to target grid's item map
        item_map[pos] = self.held_item

        if self.on_drop:
            self.on_drop(self.held_item, grid_name, pos)

        # Clear held state
        self.cursor_sprite.visible = False
        self.held_item = None
        self.held_source = None
        self.held_entity = None

    def handle_slot_click(self, slot_name):
        """Handle click on a registered slot."""
        slot = self.slots[slot_name]

        if self.held_item is None:
            # Try to pick up from slot
            if slot.item:
                self._pickup_from_slot(slot_name)
        else:
            # Try to drop to slot
            if slot.can_accept(self.held_item):
                self._drop_to_slot(slot_name)

    def _pickup_from_slot(self, slot_name):
        """Pick up item from slot."""
        slot = self.slots[slot_name]
        item = slot.item

        self.held_item = item
        self.held_source = ('slot', slot_name)
        self.held_entity = None

        slot.clear()

        # Setup cursor
        self.cursor_sprite.sprite_index = item.sprite_index
        self.cursor_sprite.visible = True

        if self.on_pickup:
            self.on_pickup(item, slot_name, None)

    def _drop_to_slot(self, slot_name):
        """Drop held item to slot."""
        slot = self.slots[slot_name]

        # If slot has item, swap
        old_item = slot.item
        slot.set_item(self.held_item)

        # Clean up source
        if self.held_source[0] == 'grid':
            source_grid_name = self.held_source[1]
            source_pos = self.held_source[2]
            source_grid, source_map, source_color_layer = self.grids[source_grid_name]

            # Remove from source grid
            if source_pos in source_map:
                del source_map[source_pos]

            # Clear highlight
            source_color_layer.set(source_pos, (0, 0, 0, 0))

            # Remove entity
            if self.held_entity:
                for i, e in enumerate(source_grid.entities):
                    if e is self.held_entity:
                        source_grid.entities.pop(i)
                        break

            # If swapping, put old item in source position
            if old_item:
                self.add_item_to_grid(source_grid_name, source_pos, old_item)

        elif self.held_source[0] == 'slot':
            # Slot to slot swap
            source_slot_name = self.held_source[1]
            source_slot = self.slots[source_slot_name]
            if old_item:
                source_slot.set_item(old_item)

        if self.on_drop:
            self.on_drop(self.held_item, slot_name, None)

        # Clear held state
        self.cursor_sprite.visible = False
        self.held_item = None
        self.held_source = None
        self.held_entity = None

    def cancel_pickup(self):
        """Cancel current pickup and return item to source."""
        if not self.held_item:
            return

        if self.held_source[0] == 'grid':
            grid_name = self.held_source[1]
            pos = self.held_source[2]
            grid, item_map, color_layer = self.grids[grid_name]

            # Restore entity visibility
            if self.held_entity:
                self.held_entity.visible = True

            # Restore item map
            item_map[pos] = self.held_item

            # Clear highlight
            color_layer.set(pos, (0, 0, 0, 0))

        elif self.held_source[0] == 'slot':
            slot_name = self.held_source[1]
            slot = self.slots[slot_name]
            slot.set_item(self.held_item)

        if self.on_cancel:
            self.on_cancel(self.held_item)

        # Clear held state
        self.cursor_sprite.visible = False
        self.held_item = None
        self.held_source = None
        self.held_entity = None

    def _on_grid_cell_enter(self, grid_name, cell_pos):
        """Handle cell hover on registered grid."""
        if not self.held_item:
            return

        grid, item_map, color_layer = self.grids[grid_name]
        x, y = int(cell_pos[0]), int(cell_pos[1])

        # Don't highlight source cell
        if (self.held_source[0] == 'grid' and
            self.held_source[1] == grid_name and
            self.held_source[2] == (x, y)):
            return

        if (x, y) in item_map:
            color_layer.set((x, y), (255, 100, 100, 200))  # Red - occupied
        else:
            color_layer.set((x, y), (100, 255, 100, 200))  # Green - available

    def _on_move(self, pos, *args):
        """Update cursor position."""
        if self.cursor_sprite.visible:
            self.cursor_frame.x = pos[0] - 32
            self.cursor_frame.y = pos[1] - 32


# Predefined items using the texture sprites
ITEM_DATABASE = {
    'buckler': Item(101, "Buckler", def_=1, slot_type='shield', price=15),
    'shield': Item(102, "Shield", def_=2, slot_type='shield', price=30),
    'shortsword': Item(103, "Shortsword", atk=1, slot_type='weapon', price=20),
    'longsword': Item(104, "Longsword", atk=2, slot_type='weapon', price=40),
    'cleaver': Item(105, "Cleaver", atk=3, slot_type='weapon', two_handed=True, price=60),
    'buster': Item(106, "Buster Sword", atk=4, slot_type='weapon', two_handed=True, price=100),
    'training_sword': Item(107, "Training Sword", atk=2, slot_type='weapon', two_handed=True, price=25),
    'hammer': Item(117, "Hammer", atk=2, slot_type='weapon', price=35),
    'double_axe': Item(118, "Double Axe", atk=5, slot_type='weapon', two_handed=True, price=120),
    'axe': Item(119, "Axe", atk=3, slot_type='weapon', price=50),
    'wand': Item(129, "Wand", atk=1, int_=4, slot_type='weapon', price=45),
    'staff': Item(130, "Staff", atk=1, int_=7, slot_type='weapon', two_handed=True, price=80),
    'spear': Item(131, "Spear", atk=4, range_=1, slot_type='weapon', two_handed=True, price=55),
    'cloudy_potion': Item(113, "Cloudy Potion", slot_type='consumable', price=5),
    'str_potion': Item(114, "Strength Potion", slot_type='consumable', price=25),
    'health_potion': Item(115, "Health Potion", slot_type='consumable', price=15),
    'mana_potion': Item(116, "Mana Potion", slot_type='consumable', price=15),
    'lesser_cloudy': Item(125, "Lesser Cloudy Potion", slot_type='consumable', price=3),
    'lesser_str': Item(126, "Lesser Strength Potion", slot_type='consumable', price=12),
    'lesser_health': Item(127, "Lesser Health Potion", slot_type='consumable', price=8),
    'lesser_mana': Item(128, "Lesser Mana Potion", slot_type='consumable', price=8),
}


def get_item(name):
    """Get an item from the database by name."""
    return ITEM_DATABASE.get(name)
