package com.openosrs.client.core;

import com.openosrs.client.api.types.Item;
import com.openosrs.client.api.types.InventoryChange;
import com.openosrs.client.api.AgentAPI.EquipmentSlot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * InventoryState - Manages the player's inventory and equipment state.
 *
 * Tracks:
 * - Inventory contents (28 slots)
 * - Equipment items
 * - Changes and events
 */
public class InventoryState {
    private static final Logger logger = LoggerFactory.getLogger(InventoryState.class);

    private static final int INVENTORY_SIZE = 28;
    private static final int EQUIPMENT_SIZE = 14;

    private final EventSystem eventSystem;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    // Inventory and equipment arrays
    private final Item[] inventory = new Item[INVENTORY_SIZE];
    private final Item[] equipment = new Item[EQUIPMENT_SIZE];

    // Previous state for change detection
    private final Item[] previousInventory = new Item[INVENTORY_SIZE];
    private final Item[] previousEquipment = new Item[EQUIPMENT_SIZE];

    public InventoryState(EventSystem eventSystem) {
        this.eventSystem = eventSystem;

        // Initialize with empty items
        Arrays.fill(inventory, Item.EMPTY);
        Arrays.fill(equipment, Item.EMPTY);
        Arrays.fill(previousInventory, Item.EMPTY);
        Arrays.fill(previousEquipment, Item.EMPTY);
    }

    public void initialize() {
        logger.debug("InventoryState initialized");
    }

    public void shutdown() {
        logger.debug("InventoryState shutdown");
    }

    public void tick() {
        lock.readLock().lock();
        try {
            // Check for inventory changes
            for (int i = 0; i < INVENTORY_SIZE; i++) {
                if (!inventory[i].equals(previousInventory[i])) {
                    InventoryChange change = new InventoryChange(i, previousInventory[i], inventory[i]);
                    eventSystem.fireInventoryChanged(change);
                    previousInventory[i] = inventory[i];
                }
            }

            // Check for equipment changes (could add equipment change events if needed)
            System.arraycopy(equipment, 0, previousEquipment, 0, EQUIPMENT_SIZE);

        } finally {
            lock.readLock().unlock();
        }
    }

    /**
     * Update the entire inventory.
     */
    public void updateInventory(Item[] newInventory) {
        if (newInventory == null || newInventory.length != INVENTORY_SIZE) {
            logger.warn("Invalid inventory update - wrong size");
            return;
        }

        lock.writeLock().lock();
        try {
            System.arraycopy(newInventory, 0, inventory, 0, INVENTORY_SIZE);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * Update a specific inventory slot.
     */
    public void updateInventorySlot(int slot, Item item) {
        if (slot < 0 || slot >= INVENTORY_SIZE) {
            logger.warn("Invalid inventory slot: {}", slot);
            return;
        }

        lock.writeLock().lock();
        try {
            inventory[slot] = item != null ? item : Item.EMPTY;
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * Update the entire equipment.
     */
    public void updateEquipment(Item[] newEquipment) {
        if (newEquipment == null || newEquipment.length != EQUIPMENT_SIZE) {
            logger.warn("Invalid equipment update - wrong size");
            return;
        }

        lock.writeLock().lock();
        try {
            System.arraycopy(newEquipment, 0, equipment, 0, EQUIPMENT_SIZE);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * Update a specific equipment slot.
     */
    public void updateEquipmentSlot(EquipmentSlot slot, Item item) {
        if (slot == null) {
            logger.warn("Null equipment slot");
            return;
        }

        int slotId = slot.getId();
        if (slotId < 0 || slotId >= EQUIPMENT_SIZE) {
            logger.warn("Invalid equipment slot: {}", slotId);
            return;
        }

        lock.writeLock().lock();
        try {
            equipment[slotId] = item != null ? item : Item.EMPTY;
        } finally {
            lock.writeLock().unlock();
        }
    }

    // Read operations

    public Item[] getInventory() {
        lock.readLock().lock();
        try {
            return inventory.clone();
        } finally {
            lock.readLock().unlock();
        }
    }

    public Item getInventorySlot(int slot) {
        if (slot < 0 || slot >= INVENTORY_SIZE) {
            return Item.EMPTY;
        }

        lock.readLock().lock();
        try {
            return inventory[slot];
        } finally {
            lock.readLock().unlock();
        }
    }

    public Item[] getEquipment() {
        lock.readLock().lock();
        try {
            return equipment.clone();
        } finally {
            lock.readLock().unlock();
        }
    }

    public Item getEquipmentSlot(EquipmentSlot slot) {
        if (slot == null) {
            return Item.EMPTY;
        }

        int slotId = slot.getId();
        if (slotId < 0 || slotId >= EQUIPMENT_SIZE) {
            return Item.EMPTY;
        }

        lock.readLock().lock();
        try {
            return equipment[slotId];
        } finally {
            lock.readLock().unlock();
        }
    }

    public boolean hasItem(int itemId) {
        lock.readLock().lock();
        try {
            for (Item item : inventory) {
                if (item.getItemId() == itemId) {
                    return true;
                }
            }
            return false;
        } finally {
            lock.readLock().unlock();
        }
    }

    public int getItemCount(int itemId) {
        lock.readLock().lock();
        try {
            int count = 0;
            for (Item item : inventory) {
                if (item.getItemId() == itemId) {
                    count += item.getQuantity();
                }
            }
            return count;
        } finally {
            lock.readLock().unlock();
        }
    }

    public int findItemSlot(int itemId) {
        lock.readLock().lock();
        try {
            for (int i = 0; i < INVENTORY_SIZE; i++) {
                if (inventory[i].getItemId() == itemId) {
                    return i;
                }
            }
            return -1;
        } finally {
            lock.readLock().unlock();
        }
    }

    public boolean isInventoryFull() {
        lock.readLock().lock();
        try {
            for (Item item : inventory) {
                if (item.isEmpty()) {
                    return false;
                }
            }
            return true;
        } finally {
            lock.readLock().unlock();
        }
    }

    public int getEmptySlots() {
        lock.readLock().lock();
        try {
            int empty = 0;
            for (Item item : inventory) {
                if (item.isEmpty()) {
                    empty++;
                }
            }
            return empty;
        } finally {
            lock.readLock().unlock();
        }
    }
}
