package com.openosrs.client.api;

import com.openosrs.client.core.ClientCore;
import com.openosrs.client.core.InventoryState;
import com.openosrs.client.core.bridge.BridgeAdapter;
import com.openosrs.client.api.types.Item;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * InventoryAPI - Provides access to inventory and equipment state.
 *
 * This API module handles:
 * - Inventory management (28 slots)
 * - Equipment management (14 slots)
 * - Item utilities and queries
 * - Item interaction capabilities
 */
public class InventoryAPI {
    private static final Logger logger = LoggerFactory.getLogger(InventoryAPI.class);

    private final ClientCore clientCore;
    private final InventoryState inventoryState;
    private final BridgeAdapter bridgeAdapter;

    public InventoryAPI(ClientCore clientCore, BridgeAdapter bridgeAdapter) {
        this.clientCore = clientCore;
        this.inventoryState = clientCore.getInventoryState();
        this.bridgeAdapter = bridgeAdapter;
    }

    // === INVENTORY METHODS ===

    /**
     * Get the entire inventory as an array.
     */
    public Item[] getInventory() {
        return bridgeAdapter.getInventory();
    }

    /**
     * Get an item from a specific inventory slot (0-27).
     */
    public Item getInventoryItem(int slot) {
        if (slot < 0 || slot >= 28) {
            return Item.EMPTY;
        }

        Item[] inventory = getInventory();
        return inventory[slot];
    }

    /**
     * Check if the inventory contains an item with the specified name.
     */
    public boolean hasItem(String name) {
        return Arrays.stream(getInventory())
                .anyMatch(item -> !item.isEmpty() && item.getName().equalsIgnoreCase(name));
    }

    /**
     * Check if the inventory contains an item with the specified ID.
     */
    public boolean hasItem(int id) {
        return Arrays.stream(getInventory())
                .anyMatch(item -> !item.isEmpty() && item.getId() == id);
    }

    /**
     * Get the total count of items with the specified name.
     */
    public int getItemCount(String name) {
        return Arrays.stream(getInventory())
                .filter(item -> !item.isEmpty() && item.getName().equalsIgnoreCase(name))
                .mapToInt(Item::getQuantity)
                .sum();
    }

    /**
     * Get the total count of items with the specified ID.
     */
    public int getItemCount(int id) {
        return Arrays.stream(getInventory())
                .filter(item -> !item.isEmpty() && item.getId() == id)
                .mapToInt(Item::getQuantity)
                .sum();
    }

    /**
     * Get all items in the inventory with the specified name.
     */
    public List<Item> getItems(String name) {
        return Arrays.stream(getInventory())
                .filter(item -> !item.isEmpty() && item.getName().equalsIgnoreCase(name))
                .collect(Collectors.toList());
    }

    /**
     * Get all items in the inventory with the specified ID.
     */
    public List<Item> getItems(int id) {
        return Arrays.stream(getInventory())
                .filter(item -> !item.isEmpty() && item.getId() == id)
                .collect(Collectors.toList());
    }

    /**
     * Get the first item in the inventory with the specified name.
     */
    public Item getFirstItem(String name) {
        return Arrays.stream(getInventory())
                .filter(item -> !item.isEmpty() && item.getName().equalsIgnoreCase(name))
                .findFirst()
                .orElse(Item.EMPTY);
    }

    /**
     * Get the first item in the inventory with the specified ID.
     */
    public Item getFirstItem(int id) {
        return Arrays.stream(getInventory())
                .filter(item -> !item.isEmpty() && item.getId() == id)
                .findFirst()
                .orElse(Item.EMPTY);
    }

    /**
     * Get the slot index of the first item with the specified name (-1 if not found).
     */
    public int getItemSlot(String name) {
        Item[] inventory = getInventory();
        for (int i = 0; i < inventory.length; i++) {
            Item item = inventory[i];
            if (!item.isEmpty() && item.getName().equalsIgnoreCase(name)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Get the slot index of the first item with the specified ID (-1 if not found).
     */
    public int getItemSlot(int id) {
        Item[] inventory = getInventory();
        for (int i = 0; i < inventory.length; i++) {
            Item item = inventory[i];
            if (!item.isEmpty() && item.getId() == id) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Check if the inventory is full.
     */
    public boolean isFull() {
        return Arrays.stream(getInventory())
                .noneMatch(Item::isEmpty);
    }

    /**
     * Get the number of free inventory slots.
     */
    public int getFreeSlots() {
        return (int) Arrays.stream(getInventory())
                .filter(Item::isEmpty)
                .count();
    }

    /**
     * Check if the inventory can fit an item (considering stackability).
     */
    public boolean canFitItem(int itemId, int quantity) {
        // If we have the item and it's stackable, we can always fit more
        if (hasItem(itemId)) {
            Item existingItem = getFirstItem(itemId);
            if (existingItem.isStackable()) {
                return true;
            }
        }

        // Otherwise, we need free slots
        return getFreeSlots() > 0;
    }

    /**
     * Check if the inventory can fit an item by name.
     */
    public boolean canFitItem(String name) {
        // If we have the item and it's stackable, we can always fit more
        if (hasItem(name)) {
            Item existingItem = getFirstItem(name);
            if (existingItem.isStackable()) {
                return true;
            }
        }

        // Otherwise, we need free slots
        return getFreeSlots() > 0;
    }

    // === EQUIPMENT METHODS ===

    /**
     * Get the entire equipment as an array.
     */
    public Item[] getEquipment() {
        return bridgeAdapter.getEquipment();
    }

    /**
     * Get an item from a specific equipment slot.
     */
    public Item getEquipmentItem(EquipmentSlot slot) {
        Item[] equipment = getEquipment();
        int slotIndex = slot.getSlotIndex();

        if (slotIndex < 0 || slotIndex >= equipment.length) {
            return Item.EMPTY;
        }

        return equipment[slotIndex];
    }

    /**
     * Check if a specific equipment slot is occupied.
     */
    public boolean isEquipped(EquipmentSlot slot) {
        return !getEquipmentItem(slot).isEmpty();
    }

    /**
     * Check if an item with the specified name is equipped.
     */
    public boolean isEquipped(String name) {
        return Arrays.stream(getEquipment())
                .anyMatch(item -> !item.isEmpty() && item.getName().equalsIgnoreCase(name));
    }

    /**
     * Check if an item with the specified ID is equipped.
     */
    public boolean isEquipped(int id) {
        return Arrays.stream(getEquipment())
                .anyMatch(item -> !item.isEmpty() && item.getId() == id);
    }

    /**
     * Get the equipped weapon.
     */
    public Item getWeapon() {
        return getEquipmentItem(EquipmentSlot.WEAPON);
    }

    /**
     * Get the equipped shield.
     */
    public Item getShield() {
        return getEquipmentItem(EquipmentSlot.SHIELD);
    }

    /**
     * Get the equipped helmet.
     */
    public Item getHelmet() {
        return getEquipmentItem(EquipmentSlot.HEAD);
    }

    /**
     * Get the equipped chestplate/body armor.
     */
    public Item getChestplate() {
        return getEquipmentItem(EquipmentSlot.BODY);
    }

    /**
     * Get the equipped legs.
     */
    public Item getLegs() {
        return getEquipmentItem(EquipmentSlot.LEGS);
    }

    /**
     * Get the equipped boots.
     */
    public Item getBoots() {
        return getEquipmentItem(EquipmentSlot.FEET);
    }

    /**
     * Get the equipped gloves.
     */
    public Item getGloves() {
        return getEquipmentItem(EquipmentSlot.HANDS);
    }

    /**
     * Get the equipped cape.
     */
    public Item getCape() {
        return getEquipmentItem(EquipmentSlot.CAPE);
    }

    /**
     * Get the equipped amulet.
     */
    public Item getAmulet() {
        return getEquipmentItem(EquipmentSlot.AMULET);
    }

    /**
     * Get the equipped ring.
     */
    public Item getRing() {
        return getEquipmentItem(EquipmentSlot.RING);
    }

    /**
     * Get the equipped arrows.
     */
    public Item getArrows() {
        return getEquipmentItem(EquipmentSlot.AMMO);
    }

    // === UTILITY METHODS ===

    /**
     * Get all non-empty items in the inventory.
     */
    public List<Item> getAllItems() {
        return Arrays.stream(getInventory())
                .filter(item -> !item.isEmpty())
                .collect(Collectors.toList());
    }

    /**
     * Get all equipped items.
     */
    public List<Item> getAllEquippedItems() {
        return Arrays.stream(getEquipment())
                .filter(item -> !item.isEmpty())
                .collect(Collectors.toList());
    }

    /**
     * Check if the inventory contains food (items with "eat" action).
     */
    public boolean hasFood() {
        return Arrays.stream(getInventory())
                .filter(item -> !item.isEmpty())
                .anyMatch(item -> Arrays.asList(item.getActions()).contains("Eat"));
    }

    /**
     * Get the first food item in the inventory.
     */
    public Item getFirstFood() {
        return Arrays.stream(getInventory())
                .filter(item -> !item.isEmpty())
                .filter(item -> Arrays.asList(item.getActions()).contains("Eat"))
                .findFirst()
                .orElse(Item.EMPTY);
    }
}

/**
 * Equipment slot enumeration.
 */
enum EquipmentSlot {
    HEAD(0),
    CAPE(1),
    AMULET(2),
    WEAPON(3),
    BODY(4),
    SHIELD(5),
    LEGS(7),
    HANDS(9),
    FEET(10),
    RING(12),
    AMMO(13);

    private final int slotIndex;

    EquipmentSlot(int slotIndex) {
        this.slotIndex = slotIndex;
    }

    public int getSlotIndex() {
        return slotIndex;
    }
}
