package com.tiedup.remake.entities.damsel.components;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.AbstractTiedUpNpc;
import com.tiedup.remake.personality.NpcNeeds;
import com.tiedup.remake.personality.PersonalityState;
import com.tiedup.remake.util.FoodEffects;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.entity.ChestBlockEntity;

/**
 * Manages inventory, equipment, and feeding for a Damsel NPC.
 * Responsibilities:
 * - NPC inventory (expandable 9/18/27 slots)
 * - Armor inventory (4 slots)
 * - Equipment slots (main hand)
 * - Feeding system (auto-eat, player feeding)
 * - MenuProvider (GUI access)
 *
 */
public class DamselInventoryManager {

    private final AbstractTiedUpNpc entity;

    /**
     * NPC inventory for carrying items.
     * Default 9 slots, expandable to 18 or 27 via upgrade.
     */
    private NonNullList<ItemStack> npcInventory;

    /**
     * Current inventory size (9 default, 18 or 27 with upgrades).
     */
    private int npcInventorySize;

    /**
     * Armor inventory (4 slots: HEAD, CHEST, LEGS, FEET).
     * Index 0 = HEAD, 1 = CHEST, 2 = LEGS, 3 = FEET.
     */
    private final NonNullList<ItemStack> armorInventory;

    public DamselInventoryManager(AbstractTiedUpNpc entity) {
        this.entity = entity;
        this.npcInventory = NonNullList.withSize(9, ItemStack.EMPTY);
        this.npcInventorySize = 9;
        this.armorInventory = NonNullList.withSize(4, ItemStack.EMPTY);
    }

    // EQUIPMENT SLOTS

    /**
     * Get item in equipment slot.
     * Implements Mob.getItemBySlot().
     */
    public ItemStack getItemBySlot(EquipmentSlot slot) {
        return switch (slot) {
            case HEAD -> armorInventory.get(0);
            case CHEST -> armorInventory.get(1);
            case LEGS -> armorInventory.get(2);
            case FEET -> armorInventory.get(3);
            case MAINHAND -> this.entity.getEntityData().get(
                AbstractTiedUpNpc.DATA_MAIN_HAND
            );
            case OFFHAND -> ItemStack.EMPTY;
        };
    }

    /**
     * Set item in equipment slot.
     * Implements Mob.setItemSlot().
     * Note: Entity should call verifyEquippedItem before delegating to this method.
     */
    public void setItemSlot(EquipmentSlot slot, ItemStack stack) {
        switch (slot) {
            case HEAD -> armorInventory.set(0, stack);
            case CHEST -> armorInventory.set(1, stack);
            case LEGS -> armorInventory.set(2, stack);
            case FEET -> armorInventory.set(3, stack);
            case MAINHAND -> this.entity.getEntityData().set(
                AbstractTiedUpNpc.DATA_MAIN_HAND,
                stack
            );
            case OFFHAND -> {
            } // Not supported
        }
    }

    /**
     * Get armor slots iterable.
     * Implements Mob.getArmorSlots().
     */
    public Iterable<ItemStack> getArmorSlots() {
        return this.armorInventory;
    }

    /**
     * Get main hand item.
     * Implements Mob.getMainHandItem().
     */
    public ItemStack getMainHandItem() {
        return this.entity.getEntityData().get(
            AbstractTiedUpNpc.DATA_MAIN_HAND
        );
    }

    /**
     * Set the main hand item.
     *
     * @param stack Item to hold
     */
    public void setMainHandItem(ItemStack stack) {
        this.entity.getEntityData().set(
            AbstractTiedUpNpc.DATA_MAIN_HAND,
            stack
        );
    }

    // NPC INVENTORY

    /**
     * Get the NPC's carry inventory.
     *
     * @return NonNullList of ItemStacks
     */
    public NonNullList<ItemStack> getNpcInventory() {
        return this.npcInventory;
    }

    /**
     * Get the current NPC inventory size.
     *
     * @return Inventory size (9, 18, or 27)
     */
    public int getNpcInventorySize() {
        return this.npcInventorySize;
    }

    /**
     * Set/upgrade the NPC inventory size.
     *
     * @param newSize New size (9, 18, or 27)
     */
    public void setNpcInventorySize(int newSize) {
        if (newSize != 9 && newSize != 18 && newSize != 27) {
            TiedUpMod.LOGGER.warn(
                "[DamselInventoryManager] Invalid inventory size: {}. Must be 9, 18, or 27.",
                newSize
            );
            return;
        }

        if (newSize <= this.npcInventorySize) return; // Can't downgrade

        // Create new inventory preserving existing items
        NonNullList<ItemStack> newInventory = NonNullList.withSize(
            newSize,
            ItemStack.EMPTY
        );
        for (int i = 0; i < this.npcInventory.size(); i++) {
            newInventory.set(i, this.npcInventory.get(i));
        }
        this.npcInventory = newInventory;
        this.npcInventorySize = newSize;
    }

    // FEEDING SYSTEM

    /**
     * Check if the NPC has any edible item in their inventory.
     *
     * @return true if food is available
     */
    public boolean hasEdibleInInventory() {
        for (int i = 0; i < this.npcInventorySize; i++) {
            ItemStack stack = this.npcInventory.get(i);
            if (
                !stack.isEmpty() && stack.getItem().getFoodProperties() != null
            ) {
                return true;
            }
        }
        return false;
    }

    /**
     * Try to eat food from inventory to satisfy hunger.
     * Used by job systems and AI goals.
     *
     * @param personalityState Personality state for needs access
     * @return true if food was consumed
     */
    public boolean tryEatFromInventory(PersonalityState personalityState) {
        if (personalityState == null) return false;

        NpcNeeds needs = personalityState.getNeeds();
        if (needs.getHunger() >= 80.0f) return false; // Not hungry enough

        // Find best food item
        int bestSlot = -1;
        int bestNutrition = 0;

        for (int i = 0; i < this.npcInventorySize; i++) {
            ItemStack stack = this.npcInventory.get(i);
            if (stack.isEmpty()) continue;

            FoodProperties food = stack.getItem().getFoodProperties();
            if (food != null && food.getNutrition() > bestNutrition) {
                bestSlot = i;
                bestNutrition = food.getNutrition();
            }
        }

        if (bestSlot >= 0) {
            ItemStack foodStack = this.npcInventory.get(bestSlot);
            FoodProperties food = foodStack.getItem().getFoodProperties();

            if (food != null) {
                // Consume food
                foodStack.shrink(1);
                needs.feed(food.getNutrition());
                personalityState.modifyMood(2);

                // Play eating sound
                this.entity.level().playSound(
                    null,
                    this.entity.blockPosition(),
                    SoundEvents.GENERIC_EAT,
                    SoundSource.NEUTRAL,
                    0.5f,
                    1.0f
                );

                return true;
            }
        }

        return false;
    }

    /**
     * Try to eat food from a container (chest) when inventory is empty.
     * Used by job systems for autonomous feeding.
     *
     * @param chestPos Position of the chest to eat from
     * @param personalityState Personality state for needs access
     * @return true if food was consumed
     */
    public boolean tryEatFromChest(
        BlockPos chestPos,
        PersonalityState personalityState
    ) {
        if (personalityState == null || chestPos == null) return false;

        NpcNeeds needs = personalityState.getNeeds();
        if (needs.getHunger() >= 80.0f) return false;

        // Get chest container
        if (
            !(this.entity.level().getBlockEntity(chestPos) instanceof
                    ChestBlockEntity chest)
        ) {
            return false;
        }

        Container container = ChestBlock.getContainer(
            (ChestBlock) chest.getBlockState().getBlock(),
            chest.getBlockState(),
            this.entity.level(),
            chestPos,
            false
        );

        if (container == null) return false;

        // Find food in chest
        for (int i = 0; i < container.getContainerSize(); i++) {
            ItemStack stack = container.getItem(i);
            if (stack.isEmpty()) continue;

            FoodProperties food = stack.getItem().getFoodProperties();
            if (food != null) {
                // Consume food from chest
                stack.shrink(1);
                needs.feed(food.getNutrition());
                personalityState.modifyMood(2);

                // Play eating sound
                this.entity.level().playSound(
                    null,
                    this.entity.blockPosition(),
                    SoundEvents.GENERIC_EAT,
                    SoundSource.NEUTRAL,
                    0.5f,
                    1.0f
                );

                return true;
            }
        }

        return false;
    }

    /**
     * Feed the NPC directly with a food item.
     * Effects: restore hunger, boost mood, heal HP, increase affinity.
     *
     * @param player The player feeding
     * @param foodStack The food item (will be consumed)
     * @param personalityState Personality state for needs and relationship access
     * @return true if feeding was successful
     */
    public boolean feedByPlayer(
        Player player,
        ItemStack foodStack,
        PersonalityState personalityState
    ) {
        if (foodStack.isEmpty()) return false;
        if (this.entity.level().isClientSide) return false;

        FoodEffects.FeedResult result = FoodEffects.calculateFeedEffects(
            foodStack
        );
        if (result == null) return false;
        if (personalityState == null) return false;

        // Restore hunger
        personalityState.getNeeds().feed(result.hunger());

        // Boost mood
        personalityState.modifyMood(result.mood());

        // Heal HP
        this.entity.heal(result.heal());

        // Consume the food item
        foodStack.shrink(1);

        // Play eating sound
        this.entity.playSound(SoundEvents.GENERIC_EAT, 0.5f, 1.0f);

        // Spawn heart particles
        if (this.entity.level() instanceof ServerLevel serverLevel) {
            serverLevel.sendParticles(
                ParticleTypes.HEART,
                this.entity.getX(),
                this.entity.getY() + this.entity.getBbHeight(),
                this.entity.getZ(),
                2,
                0.3,
                0.2,
                0.3,
                0.0
            );
        }

        // Trigger dialogue based on hunger state
        String dialogueKey = personalityState.getNeeds().isStarving()
            ? "action.feed.starving"
            : "action.feed";
        // Cast safe: feedByPlayer is only called from EntityDamsel which IS an EntityDamsel
        if (
            this.entity instanceof
                com.tiedup.remake.entities.EntityDamsel damsel
        ) {
            com.tiedup.remake.dialogue.EntityDialogueManager.talkByDialogueId(
                damsel,
                player,
                dialogueKey
            );
        }

        return true;
    }

    // MENU PROVIDER

    /**
     * Create inventory menu for player access.
     * Implements MenuProvider.createMenu().
     *
     * @param containerId Container ID
     * @param playerInventory Player's inventory
     * @param player The player opening the container
     * @return NpcInventoryMenu instance
     */
    public AbstractContainerMenu createMenu(
        int containerId,
        Inventory playerInventory,
        Player player
    ) {
        // Cast safe: createMenu is only called from EntityDamsel (MenuProvider)
        com.tiedup.remake.entities.EntityDamsel damsel =
            (com.tiedup.remake.entities.EntityDamsel) this.entity;
        return new com.tiedup.remake.entities.NpcInventoryMenu(
            containerId,
            playerInventory,
            damsel
        );
    }

    // NBT SERIALIZATION

    /**
     * Save inventory data to NBT tag.
     * Writes directly to top-level keys for backward compatibility.
     */
    public void saveToTag(CompoundTag tag) {
        // NPC Inventory
        tag.putInt("NpcInventorySize", this.npcInventorySize);
        ListTag inventoryTag = new ListTag();
        for (int i = 0; i < this.npcInventory.size(); i++) {
            ItemStack stack = this.npcInventory.get(i);
            if (!stack.isEmpty()) {
                CompoundTag slotTag = new CompoundTag();
                slotTag.putByte("Slot", (byte) i);
                stack.save(slotTag);
                inventoryTag.add(slotTag);
            }
        }
        tag.put("NpcInventory", inventoryTag);

        // Equipment (Armor + Main Hand)
        ListTag armorTag = new ListTag();
        for (int i = 0; i < 4; i++) {
            ItemStack stack = this.armorInventory.get(i);
            if (!stack.isEmpty()) {
                CompoundTag slotTag = new CompoundTag();
                slotTag.putByte("Slot", (byte) i);
                stack.save(slotTag);
                armorTag.add(slotTag);
            }
        }
        tag.put("ArmorInventory", armorTag);
        if (!this.getMainHandItem().isEmpty()) {
            tag.put(
                "MainHandItem",
                this.getMainHandItem().save(new CompoundTag())
            );
        }
    }

    /**
     * Load inventory data from NBT tag.
     * Reads from top-level keys for backward compatibility.
     */
    public void loadFromTag(CompoundTag tag) {
        // Restore NPC Inventory
        if (tag.contains("NpcInventorySize")) {
            this.npcInventorySize = tag.getInt("NpcInventorySize");
            this.npcInventory = NonNullList.withSize(
                this.npcInventorySize,
                ItemStack.EMPTY
            );
        }
        if (tag.contains("NpcInventory")) {
            ListTag inventoryTag = tag.getList("NpcInventory", 10);
            for (int i = 0; i < inventoryTag.size(); i++) {
                CompoundTag slotTag = inventoryTag.getCompound(i);
                int slot = slotTag.getByte("Slot") & 255;
                if (slot < this.npcInventory.size()) {
                    this.npcInventory.set(slot, ItemStack.of(slotTag));
                }
            }
        }

        // Restore Equipment (Armor + Main Hand)
        if (tag.contains("ArmorInventory")) {
            ListTag armorTag = tag.getList("ArmorInventory", 10);
            for (int i = 0; i < armorTag.size(); i++) {
                CompoundTag slotTag = armorTag.getCompound(i);
                int slot = slotTag.getByte("Slot") & 255;
                if (slot < 4) {
                    this.armorInventory.set(slot, ItemStack.of(slotTag));
                }
            }
        }
        if (tag.contains("MainHandItem")) {
            this.setMainHandItem(ItemStack.of(tag.getCompound("MainHandItem")));
        }
    }
}
