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 java.util.List;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
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.item.Items;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.AbstractFurnaceBlock;
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
import net.minecraft.world.level.block.state.BlockState;

/**
 * AI Goal: NPC cooks items using furnaces, with resources from chest hub.
 *
 * <p><b>Chest-Hub System:</b></p>
 * <ul>
 *   <li>Chest provides items to cook and fuel</li>
 *   <li>Cooked items are stored back in chest</li>
 *   <li>Searches for furnaces in work zone</li>
 * </ul>
 *
 * <p><b>Behavior:</b></p>
 * <ul>
 *   <li>Scan zone for furnaces</li>
 *   <li>Load items from chest into furnace</li>
 *   <li>Wait for cooking to complete</li>
 *   <li>Collect output and store in chest</li>
 *   <li>Repeat while cookable items remain</li>
 * </ul>
 */
public class NpcCookCommandGoal extends Goal {

    private final EntityDamsel npc;
    private final double speedModifier;

    private static final int WORK_RADIUS = 8;
    private static final double INTERACT_DISTANCE = 2.0;
    private static final int SCAN_INTERVAL = 60;
    private static final int WAIT_CHECK_INTERVAL = 40;
    private static final int IDLE_TIMEOUT = 600;
    private static final int XP_INTERVAL = 1200;

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

    private enum CookPhase {
        SCANNING,
        LOADING,
        WAITING,
        COLLECTING,
        STORING,
        IDLE,
    }

    private CookPhase phase;

    @Nullable
    private BlockPos chestPos;

    @Nullable
    private BlockPos targetFurnace;

    @Nullable
    private Player master;

    private ItemStack heldOutput = ItemStack.EMPTY;
    private int scanTimer;
    private int waitTimer;
    private int idleTimer;
    private int xpTimer;
    private int pathRecalcTimer;

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

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

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

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

    @Override
    public void start() {
        NpcGoalHelper.ensureOutsideCell(npc);
        var state = npc.getPersonalityState();
        if (state != null && state.getCommandTarget() != null) {
            this.chestPos = state.getCommandTarget();
        } else {
            npc.cancelCommand();
            return;
        }

        this.phase = CookPhase.SCANNING;
        this.heldOutput = ItemStack.EMPTY;
        this.scanTimer = 0;
        this.waitTimer = 0;
        this.idleTimer = 0;
        this.xpTimer = 0;
        this.pathRecalcTimer = 0;
        this.targetFurnace = null;
    }

    @Override
    public void stop() {
        // Store any held items
        if (!heldOutput.isEmpty() && chestPos != null) {
            storeInChest(heldOutput);
            heldOutput = ItemStack.EMPTY;
        }

        this.chestPos = null;
        this.targetFurnace = null;
        this.master = null;
        npc.getNavigation().stop();
    }

    @Override
    public void tick() {
        if (++this.xpTimer >= XP_INTERVAL) {
            this.xpTimer = 0;
            awardXP(2);
            NpcGoalHelper.incrementJobExperience(npc, NpcCommand.COOK);
        }

        switch (this.phase) {
            case SCANNING -> tickScanning();
            case LOADING -> tickLoading();
            case WAITING -> tickWaiting();
            case COLLECTING -> tickCollecting();
            case STORING -> tickStoring();
            case IDLE -> tickIdle();
        }
    }

    private void tickScanning() {
        if (--this.scanTimer <= 0) {
            // Apply efficiency modifier - tired/inexperienced NPCs scan slower
            float efficiency = NpcGoalHelper.getJobEfficiency(
                npc,
                NpcCommand.COOK
            );
            this.scanTimer = (int) (SCAN_INTERVAL / efficiency);

            // First check if we have output to store
            if (!heldOutput.isEmpty()) {
                this.phase = CookPhase.STORING;
                return;
            }

            // Find a furnace that needs attention
            this.targetFurnace = findFurnaceNeedingWork();

            if (this.targetFurnace != null) {
                // Determine what to do with the furnace
                AbstractFurnaceBlockEntity furnace = getFurnaceEntity(
                    targetFurnace
                );
                if (furnace != null) {
                    if (!furnace.getItem(2).isEmpty()) {
                        // Has output - collect it
                        this.phase = CookPhase.COLLECTING;
                    } else if (
                        furnace.getItem(0).isEmpty() && hasCookableInChest()
                    ) {
                        // Empty and we have cookables - load it
                        this.phase = CookPhase.LOADING;
                    } else if (!furnace.getItem(0).isEmpty()) {
                        // Cooking - wait
                        this.phase = CookPhase.WAITING;
                    }
                }
                this.idleTimer = 0;
            } else {
                // No work available
                this.idleTimer += SCAN_INTERVAL;

                if (this.idleTimer >= IDLE_TIMEOUT) {
                    this.phase = CookPhase.IDLE;
                }
            }
        }
    }

    private void tickLoading() {
        if (targetFurnace == null || chestPos == null) {
            this.phase = CookPhase.SCANNING;
            return;
        }

        // Move towards furnace
        if (moveTowards(targetFurnace)) {
            // Close enough - load furnace
            AbstractFurnaceBlockEntity furnace = getFurnaceEntity(
                targetFurnace
            );
            if (furnace != null) {
                // Get cookable item from chest
                ItemStack cookable = extractCookableFromChest();
                if (!cookable.isEmpty()) {
                    furnace.setItem(0, cookable);

                    // Add fuel if needed
                    if (
                        furnace.getItem(1).isEmpty() ||
                        furnace.getItem(1).getCount() < 8
                    ) {
                        ItemStack fuel = extractFuelFromChest();
                        if (!fuel.isEmpty()) {
                            ItemStack existingFuel = furnace.getItem(1);
                            if (existingFuel.isEmpty()) {
                                furnace.setItem(1, fuel);
                            } else if (
                                ItemStack.isSameItemSameTags(existingFuel, fuel)
                            ) {
                                existingFuel.grow(fuel.getCount());
                            }
                        }
                    }

                    awardXP(1);

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

            this.targetFurnace = null;
            this.phase = CookPhase.SCANNING;
            this.scanTimer = 20;
        }
    }

    private void tickWaiting() {
        if (--this.waitTimer <= 0) {
            this.waitTimer = WAIT_CHECK_INTERVAL;

            // Check if furnace is done
            if (targetFurnace != null) {
                AbstractFurnaceBlockEntity furnace = getFurnaceEntity(
                    targetFurnace
                );
                if (furnace != null && !furnace.getItem(2).isEmpty()) {
                    this.phase = CookPhase.COLLECTING;
                    return;
                }
            }
        }

        // While waiting, stay near furnace area but don't block
        if (targetFurnace != null) {
            double dist = npc.blockPosition().distSqr(targetFurnace);
            if (dist > 16) {
                moveTowards(targetFurnace);
            }
        }
    }

    private void tickCollecting() {
        if (targetFurnace == null) {
            this.phase = CookPhase.SCANNING;
            return;
        }

        if (moveTowards(targetFurnace)) {
            // Close enough - collect output
            AbstractFurnaceBlockEntity furnace = getFurnaceEntity(
                targetFurnace
            );
            if (furnace != null) {
                ItemStack output = furnace.getItem(2);
                if (!output.isEmpty()) {
                    this.heldOutput = output.copy();
                    furnace.setItem(2, ItemStack.EMPTY);

                    // Apply yield bonus from experience
                    float yieldMult = NpcGoalHelper.getJobYieldMultiplier(
                        npc,
                        NpcCommand.COOK
                    );
                    if (
                        yieldMult > 1.0f &&
                        npc.getRandom().nextFloat() < (yieldMult - 1.0f)
                    ) {
                        this.heldOutput.grow(1); // Bonus item
                    }

                    awardXP(2);

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

            this.targetFurnace = null;
            this.phase = CookPhase.STORING;
        }
    }

    private void tickStoring() {
        if (chestPos == null || heldOutput.isEmpty()) {
            this.heldOutput = ItemStack.EMPTY;
            this.phase = CookPhase.SCANNING;
            return;
        }

        if (moveTowards(chestPos)) {
            storeInChest(heldOutput);
            this.heldOutput = ItemStack.EMPTY;
            this.phase = CookPhase.SCANNING;
            this.scanTimer = 0;
        }
    }

    private void tickIdle() {
        if (--this.scanTimer <= 0) {
            this.scanTimer = SCAN_INTERVAL * 2;

            // Check for new work
            this.targetFurnace = findFurnaceNeedingWork();
            if (this.targetFurnace != null || hasCookableInChest()) {
                this.phase = CookPhase.SCANNING;
                this.idleTimer = 0;
            }
        }
    }

    private boolean moveTowards(BlockPos pos) {
        if (--this.pathRecalcTimer <= 0) {
            this.pathRecalcTimer = 10;
            npc
                .getNavigation()
                .moveTo(
                    pos.getX() + 0.5,
                    pos.getY(),
                    pos.getZ() + 0.5,
                    speedModifier
                );
        }

        double dist = npc.blockPosition().distSqr(pos);
        return dist <= INTERACT_DISTANCE * INTERACT_DISTANCE;
    }

    @Nullable
    private BlockPos findFurnaceNeedingWork() {
        if (chestPos == null) return null;

        Level level = npc.level();
        BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();

        for (int x = -WORK_RADIUS; x <= WORK_RADIUS; x++) {
            for (int z = -WORK_RADIUS; z <= WORK_RADIUS; z++) {
                for (int y = -2; y <= 2; y++) {
                    mutable.set(
                        chestPos.getX() + x,
                        chestPos.getY() + y,
                        chestPos.getZ() + z
                    );
                    BlockState state = level.getBlockState(mutable);

                    if (state.getBlock() instanceof AbstractFurnaceBlock) {
                        AbstractFurnaceBlockEntity furnace = getFurnaceEntity(
                            mutable
                        );
                        if (furnace != null) {
                            // Check if furnace needs work
                            if (!furnace.getItem(2).isEmpty()) {
                                // Has output to collect
                                return mutable.immutable();
                            }
                            if (
                                furnace.getItem(0).isEmpty() &&
                                hasCookableInChest()
                            ) {
                                // Empty and we have items to cook
                                return mutable.immutable();
                            }
                        }
                    }
                }
            }
        }

        return null;
    }

    @Nullable
    private AbstractFurnaceBlockEntity getFurnaceEntity(BlockPos pos) {
        if (
            npc.level().getBlockEntity(pos) instanceof
                AbstractFurnaceBlockEntity furnace
        ) {
            return furnace;
        }
        return null;
    }

    private boolean hasCookableInChest() {
        if (chestPos == null) return false;

        Container container = getChestContainer();
        if (container == null) return false;

        Level level = npc.level();
        for (int i = 0; i < container.getContainerSize(); i++) {
            ItemStack stack = container.getItem(i);
            if (!stack.isEmpty() && isCookable(stack, level)) {
                return true;
            }
        }
        return false;
    }

    private boolean isCookable(ItemStack stack, Level level) {
        return level
            .getRecipeManager()
            .getRecipeFor(
                RecipeType.SMELTING,
                new net.minecraft.world.SimpleContainer(stack),
                level
            )
            .isPresent();
    }

    private ItemStack extractCookableFromChest() {
        Container container = getChestContainer();
        if (container == null) return ItemStack.EMPTY;

        Level level = npc.level();
        for (int i = 0; i < container.getContainerSize(); i++) {
            ItemStack stack = container.getItem(i);
            if (!stack.isEmpty() && isCookable(stack, level)) {
                ItemStack extracted = stack.split(
                    Math.min(stack.getCount(), 8)
                );
                return extracted;
            }
        }
        return ItemStack.EMPTY;
    }

    private ItemStack extractFuelFromChest() {
        Container container = getChestContainer();
        if (container == null) return ItemStack.EMPTY;

        for (int i = 0; i < container.getContainerSize(); i++) {
            ItemStack stack = container.getItem(i);
            if (!stack.isEmpty() && isFuel(stack)) {
                return stack.split(Math.min(stack.getCount(), 8));
            }
        }
        return ItemStack.EMPTY;
    }

    private boolean isFuel(ItemStack stack) {
        return (
            stack.is(Items.COAL) ||
            stack.is(Items.CHARCOAL) ||
            stack.is(Items.COAL_BLOCK) ||
            stack.is(Items.LAVA_BUCKET) ||
            stack.is(Items.BLAZE_ROD)
        );
    }

    @Nullable
    private Container getChestContainer() {
        if (chestPos == null) return null;
        return NpcGoalHelper.getChestContainer(npc.level(), chestPos);
    }

    private void storeInChest(ItemStack stack) {
        if (chestPos == null) {
            npc.spawnAtLocation(stack);
            return;
        }
        NpcGoalHelper.storeItemsInChest(
            npc,
            npc.level(),
            chestPos,
            List.of(stack)
        );
    }

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