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.ArrayList;
import java.util.EnumSet;
import java.util.List;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;

/**
 * AI Goal: NPC collects all items in a zone and brings them to master.
 *
 * Personality System Phase D: AI Goals
 *
 * <p><b>Behavior:</b></p>
 * <ul>
 *   <li>Only active when NPC has COLLECT command</li>
 *   <li>Scans zone for items</li>
 *   <li>Picks up items and stores them (up to inventory capacity)</li>
 *   <li>Returns to master when full or zone is clear</li>
 *   <li>Repeats until zone stays clear for a while</li>
 *   <li>Awards XP periodically and on completion</li>
 * </ul>
 */
public class NpcCollectCommandGoal extends Goal {

    private final EntityDamsel npc;
    private final double speedModifier;

    /** Collection zone radius */
    private static final int COLLECT_RADIUS = 12;

    /** Max items to hold before returning */
    private static final int MAX_HELD_ITEMS = 8;

    /** Distance to pick up item */
    private static final double PICKUP_DISTANCE = 1.5;

    /** Distance to deliver to master */
    private static final double DELIVER_DISTANCE = 2.0;

    /** Ticks between zone scans */
    private static final int SCAN_INTERVAL = 60;

    /** Ticks to wait when zone is empty before considering done */
    private static final int EMPTY_ZONE_TIMEOUT = 200;

    /** Ticks between XP awards */
    private static final int XP_INTERVAL = 1200; // 1 minute

    /** Rest drain intensity for collecting (light work) */
    private static final float WORK_INTENSITY = 0.7f;

    private enum CollectPhase {
        SCANNING,
        COLLECTING,
        RETURNING,
    }

    private CollectPhase phase;

    @Nullable
    private BlockPos collectCenter;

    @Nullable
    private ItemEntity targetItem;

    @Nullable
    private Player master;

    private List<ItemStack> collectedItems = new ArrayList<>();
    private int scanTimer;
    private int emptyZoneTimer;
    private int xpTimer;
    private int pathRecalcTimer;

    public NpcCollectCommandGoal(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.COLLECT) {
            return false;
        }
        if (!npc.hasCollar()) {
            return false;
        }

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

    @Override
    public boolean canContinueToUse() {
        if (npc.getActiveCommand() != NpcCommand.COLLECT) {
            return false;
        }
        if (!npc.hasCollar()) {
            return false;
        }
        if (this.master == null || !this.master.isAlive()) {
            return false;
        }
        return true;
    }

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

        this.phase = CollectPhase.SCANNING;
        this.collectedItems.clear();
        this.scanTimer = 0;
        this.emptyZoneTimer = 0;
        this.xpTimer = 0;
        this.pathRecalcTimer = 0;
        this.targetItem = null;
    }

    @Override
    public void stop() {
        // Deliver any remaining items
        if (!collectedItems.isEmpty() && master != null) {
            deliverItems();
        }

        this.collectCenter = null;
        this.targetItem = null;
        this.master = null;
        this.collectedItems.clear();
        npc.getNavigation().stop();
    }

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

        switch (this.phase) {
            case SCANNING -> tickScanning();
            case COLLECTING -> tickCollecting();
            case RETURNING -> tickReturning();
        }
    }

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

            // Find items in zone
            this.targetItem = findNearestItemInZone();

            if (this.targetItem != null) {
                this.phase = CollectPhase.COLLECTING;
                this.emptyZoneTimer = 0;
            } else {
                // Zone is empty
                this.emptyZoneTimer += SCAN_INTERVAL;

                // If we have items, return them
                if (!collectedItems.isEmpty()) {
                    this.phase = CollectPhase.RETURNING;
                } else if (this.emptyZoneTimer >= EMPTY_ZONE_TIMEOUT) {
                    // Zone has been empty for a while, job complete
                    awardXP(NpcCommand.COLLECT.getCompletionXP());
                    npc.cancelCommand();
                }
            }
        }
    }

    private void tickCollecting() {
        if (this.targetItem == null || !this.targetItem.isAlive()) {
            // Item gone, scan again
            this.phase = CollectPhase.SCANNING;
            this.scanTimer = 0;
            return;
        }

        // Move towards item
        if (--this.pathRecalcTimer <= 0) {
            this.pathRecalcTimer = 10;
            npc.getNavigation().moveTo(targetItem, speedModifier);
        }

        // Check if close enough to pick up
        double dist = npc.distanceTo(targetItem);
        if (dist <= PICKUP_DISTANCE) {
            pickUpItem();
        }
    }

    private void tickReturning() {
        if (this.master == null || !this.master.isAlive()) {
            return;
        }

        // Move towards master
        if (--this.pathRecalcTimer <= 0) {
            this.pathRecalcTimer = 10;
            npc.getNavigation().moveTo(master, speedModifier);
        }

        // Check if close enough to deliver
        double dist = npc.distanceTo(master);
        if (dist <= DELIVER_DISTANCE) {
            deliverItems();
            // Go back to scanning
            this.phase = CollectPhase.SCANNING;
            this.scanTimer = 0;
        }
    }

    private void pickUpItem() {
        if (this.targetItem == null) return;

        // Add to collected items
        collectedItems.add(targetItem.getItem().copy());

        // Apply yield bonus from experience
        float yieldMult = NpcGoalHelper.getJobYieldMultiplier(
            npc,
            NpcCommand.COLLECT
        );
        if (
            yieldMult > 1.0f && npc.getRandom().nextFloat() < (yieldMult - 1.0f)
        ) {
            collectedItems.add(targetItem.getItem().copy()); // Bonus duplicate
        }

        targetItem.discard();
        this.targetItem = null;

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

        // Check if full
        if (collectedItems.size() >= MAX_HELD_ITEMS) {
            this.phase = CollectPhase.RETURNING;
        } else {
            this.phase = CollectPhase.SCANNING;
            this.scanTimer = 0;
        }
    }

    private void deliverItems() {
        if (this.master == null) return;

        for (ItemStack item : collectedItems) {
            if (!master.getInventory().add(item.copy())) {
                // Inventory full, drop it
                npc.spawnAtLocation(item.copy());
            }
        }

        int itemCount = collectedItems.size();
        collectedItems.clear();

        // Bonus XP for delivering items
        if (itemCount > 0) {
            awardXP(1);
        }
    }

    @Nullable
    private ItemEntity findNearestItemInZone() {
        if (this.collectCenter == null) return null;

        AABB searchArea = new AABB(collectCenter).inflate(COLLECT_RADIUS);
        List<ItemEntity> items = npc
            .level()
            .getEntitiesOfClass(ItemEntity.class, searchArea);

        ItemEntity nearest = null;
        double nearestDist = Double.MAX_VALUE;

        for (ItemEntity item : items) {
            double dist = npc.distanceToSqr(item);
            if (dist < nearestDist) {
                nearest = item;
                nearestDist = dist;
            }
        }

        return nearest;
    }

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