package com.tiedup.remake.entities.ai.personality;

import com.tiedup.remake.entities.EntityDamsel;
import com.tiedup.remake.personality.NpcCommand;
import com.tiedup.remake.personality.PersonalityState;
import java.util.EnumSet;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.Container;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;

/**
 * AI Goal: NPC transfers items from chest A to chest B in a continuous loop.
 *
 * <p>Work flow:</p>
 * <ul>
 *   <li>Move to source chest (A)</li>
 *   <li>Collect items into NPC inventory</li>
 *   <li>Move to destination chest (B)</li>
 *   <li>Store items from inventory</li>
 *   <li>Repeat if source chest has more items</li>
 *   <li>If empty, wait and rescan periodically</li>
 * </ul>
 */
public class NpcTransferCommandGoal extends Goal {

    private final EntityDamsel npc;
    private final double speedModifier;

    private static final double INTERACT_DISTANCE = 2.0;
    private static final int XP_INTERVAL = 1200; // Award XP every 60 seconds
    private static final int IDLE_RESCAN_INTERVAL = 100; // Rescan every 5 seconds when idle

    /** Rest drain intensity for transferring (heavy work) */
    private static final float WORK_INTENSITY = 1.3f;

    private enum TransferPhase {
        MOVING_TO_SOURCE,
        COLLECTING,
        MOVING_TO_DEST,
        STORING,
        IDLE,
    }

    private TransferPhase phase;

    @Nullable
    private BlockPos sourceChest; // Chest A

    @Nullable
    private BlockPos destChest; // Chest B

    @Nullable
    private Player master;

    private int pathRecalcTimer;
    private int xpTimer;
    private int idleTimer;
    private int actionTimer;

    public NpcTransferCommandGoal(EntityDamsel npc, double speedModifier) {
        this.npc = npc;
        this.speedModifier = speedModifier;
        this.setFlags(EnumSet.of(Goal.Flag.MOVE));
    }

    @Override
    public boolean canUse() {
        if (npc.getActiveCommand() != NpcCommand.TRANSFER) {
            return false;
        }
        if (!npc.hasCollar()) {
            return false;
        }

        this.master = NpcGoalHelper.findCommandingPlayer(npc);
        return this.master != null;
    }

    @Override
    public boolean canContinueToUse() {
        if (npc.getActiveCommand() != NpcCommand.TRANSFER) {
            return false;
        }
        return true;
    }

    @Override
    public void start() {
        var state = npc.getPersonalityState();
        if (state == null) {
            npc.cancelCommand();
            return;
        }

        // Get source chest A (commandTarget)
        this.sourceChest = state.getCommandTarget();
        // Get destination chest B (commandTarget2)
        this.destChest = state.getCommandTarget2();

        if (this.sourceChest == null || this.destChest == null) {
            // Both chests are required
            npc.cancelCommand();
            return;
        }

        this.phase = TransferPhase.MOVING_TO_SOURCE;
        this.pathRecalcTimer = 0;
        this.xpTimer = 0;
        this.idleTimer = 0;
        this.actionTimer = 0;
    }

    @Override
    public void stop() {
        this.sourceChest = null;
        this.destChest = null;
        this.master = null;
        npc.getNavigation().stop();
    }

    @Override
    public void tick() {
        // Periodic XP award
        if (++this.xpTimer >= XP_INTERVAL) {
            this.xpTimer = 0;
            awardXP(2);
        }

        switch (this.phase) {
            case MOVING_TO_SOURCE -> tickMovingToSource();
            case COLLECTING -> tickCollecting();
            case MOVING_TO_DEST -> tickMovingToDest();
            case STORING -> tickStoring();
            case IDLE -> tickIdle();
        }
    }

    private void tickMovingToSource() {
        if (this.sourceChest == null) {
            this.phase = TransferPhase.IDLE;
            return;
        }

        // Move towards source chest
        if (--this.pathRecalcTimer <= 0) {
            this.pathRecalcTimer = 10;
            npc
                .getNavigation()
                .moveTo(
                    sourceChest.getX() + 0.5,
                    sourceChest.getY(),
                    sourceChest.getZ() + 0.5,
                    speedModifier
                );
        }

        // Check if close enough
        double dist = npc.blockPosition().distSqr(sourceChest);
        if (dist <= INTERACT_DISTANCE * INTERACT_DISTANCE) {
            npc.getNavigation().stop();
            this.phase = TransferPhase.COLLECTING;
            this.actionTimer = 20; // Brief delay before collecting
        }
    }

    private void tickCollecting() {
        if (sourceChest == null) {
            this.phase = TransferPhase.IDLE;
            return;
        }

        // Wait for action timer (simulates opening chest)
        if (--this.actionTimer > 0) {
            return;
        }

        Level level = npc.level();
        Container container = getChestContainer(level, sourceChest);

        if (container == null) {
            // Chest was removed?
            this.phase = TransferPhase.IDLE;
            return;
        }

        // Collect items from chest into NPC inventory
        var npcInventory = npc.getNpcInventory();
        int collectedCount = 0;
        boolean hasMoreItems = false;

        for (int i = 0; i < container.getContainerSize(); i++) {
            ItemStack chestStack = container.getItem(i);
            if (chestStack.isEmpty()) continue;

            hasMoreItems = true;

            // Try to add to NPC inventory
            ItemStack remaining = addToNpcInventory(
                npcInventory,
                chestStack.copy()
            );
            int taken = chestStack.getCount() - remaining.getCount();

            if (taken > 0) {
                chestStack.shrink(taken);
                container.setItem(
                    i,
                    chestStack.isEmpty() ? ItemStack.EMPTY : chestStack
                );
                collectedCount += taken;
            }

            // Check if NPC inventory is full
            if (
                !remaining.isEmpty() &&
                remaining.getCount() == chestStack.getCount() + taken
            ) {
                // Couldn't take any more - inventory is full
                break;
            }
        }

        // Play chest sound
        if (collectedCount > 0) {
            level.playSound(
                null,
                sourceChest,
                SoundEvents.CHEST_OPEN,
                SoundSource.BLOCKS,
                0.5f,
                1.0f
            );

            // Drain rest from work
            PersonalityState personalityState = npc.getPersonalityState();
            if (personalityState != null) {
                personalityState.getNeeds().drainFromWork(WORK_INTENSITY);
            }
        }

        // Check if we have items to deliver
        if (hasNpcInventoryItems(npcInventory)) {
            this.phase = TransferPhase.MOVING_TO_DEST;
            this.pathRecalcTimer = 0;
        } else if (!hasMoreItems) {
            // Source is empty, go idle
            this.phase = TransferPhase.IDLE;
            this.idleTimer = 0;
        } else {
            // Can't carry more but source has items - still go deliver
            this.phase = TransferPhase.MOVING_TO_DEST;
            this.pathRecalcTimer = 0;
        }
    }

    private void tickMovingToDest() {
        if (this.destChest == null) {
            this.phase = TransferPhase.IDLE;
            return;
        }

        // Move towards destination chest
        if (--this.pathRecalcTimer <= 0) {
            this.pathRecalcTimer = 10;
            npc
                .getNavigation()
                .moveTo(
                    destChest.getX() + 0.5,
                    destChest.getY(),
                    destChest.getZ() + 0.5,
                    speedModifier
                );
        }

        // Check if close enough
        double dist = npc.blockPosition().distSqr(destChest);
        if (dist <= INTERACT_DISTANCE * INTERACT_DISTANCE) {
            npc.getNavigation().stop();
            this.phase = TransferPhase.STORING;
            this.actionTimer = 20; // Brief delay before storing
        }
    }

    private void tickStoring() {
        if (destChest == null) {
            this.phase = TransferPhase.IDLE;
            return;
        }

        // Wait for action timer
        if (--this.actionTimer > 0) {
            return;
        }

        Level level = npc.level();
        Container container = getChestContainer(level, destChest);

        if (container == null) {
            // Chest was removed?
            this.phase = TransferPhase.IDLE;
            return;
        }

        // Store items from NPC inventory into destination chest
        var npcInventory = npc.getNpcInventory();
        int storedCount = 0;

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

            ItemStack toStore = stack.copy();
            insertIntoContainer(container, toStore);

            if (toStore.isEmpty()) {
                npcInventory.set(i, ItemStack.EMPTY);
                storedCount++;
            } else if (toStore.getCount() < stack.getCount()) {
                // Partially stored
                npcInventory.set(i, toStore);
                storedCount++;
            }
            // else: couldn't store, dest is full
        }

        // Play chest sound
        if (storedCount > 0) {
            level.playSound(
                null,
                destChest,
                SoundEvents.CHEST_CLOSE,
                SoundSource.BLOCKS,
                0.5f,
                1.0f
            );

            // Drain rest from work
            PersonalityState personalityState = npc.getPersonalityState();
            if (personalityState != null) {
                personalityState.getNeeds().drainFromWork(WORK_INTENSITY);
            }
        }

        // Go back to source for more items
        this.phase = TransferPhase.MOVING_TO_SOURCE;
        this.pathRecalcTimer = 0;
    }

    private void tickIdle() {
        // Periodically check if source chest has new items
        if (++this.idleTimer >= IDLE_RESCAN_INTERVAL) {
            this.idleTimer = 0;

            if (sourceChest != null) {
                Level level = npc.level();
                Container container = getChestContainer(level, sourceChest);

                if (container != null && !isContainerEmpty(container)) {
                    // Source has items, go collect
                    this.phase = TransferPhase.MOVING_TO_SOURCE;
                    this.pathRecalcTimer = 0;
                }
            }
        }
    }

    // ========== Utility Methods ==========

    @Nullable
    private Container getChestContainer(Level level, BlockPos pos) {
        return NpcGoalHelper.getChestContainer(level, pos);
    }

    private boolean isContainerEmpty(Container container) {
        for (int i = 0; i < container.getContainerSize(); i++) {
            if (!container.getItem(i).isEmpty()) {
                return false;
            }
        }
        return true;
    }

    private boolean hasNpcInventoryItems(
        net.minecraft.core.NonNullList<ItemStack> inventory
    ) {
        for (ItemStack stack : inventory) {
            if (!stack.isEmpty()) {
                return true;
            }
        }
        return false;
    }

    private ItemStack addToNpcInventory(
        net.minecraft.core.NonNullList<ItemStack> inventory,
        ItemStack stack
    ) {
        // Try to stack with existing items first
        for (int i = 0; i < inventory.size() && !stack.isEmpty(); i++) {
            ItemStack slotStack = inventory.get(i);
            if (
                !slotStack.isEmpty() &&
                ItemStack.isSameItemSameTags(slotStack, stack)
            ) {
                int space = slotStack.getMaxStackSize() - slotStack.getCount();
                int toTransfer = Math.min(space, stack.getCount());
                if (toTransfer > 0) {
                    slotStack.grow(toTransfer);
                    stack.shrink(toTransfer);
                }
            }
        }

        // Then try empty slots
        for (int i = 0; i < inventory.size() && !stack.isEmpty(); i++) {
            if (inventory.get(i).isEmpty()) {
                inventory.set(i, stack.copy());
                stack.setCount(0);
            }
        }

        return stack;
    }

    private void insertIntoContainer(Container container, ItemStack stack) {
        NpcGoalHelper.insertIntoContainer(container, stack);
    }

    private void awardXP(int amount) {
        if (this.master != null) {
        }
    }
}
