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.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 fetches a nearby item and brings it to the master.
 *
 * Personality System Phase D: AI Goals
 *
 * <p><b>Behavior:</b></p>
 * <ul>
 *   <li>Only active when NPC has FETCH command</li>
 *   <li>Finds the nearest item entity</li>
 *   <li>Walks to item and picks it up</li>
 *   <li>Returns to master and drops the item</li>
 *   <li>Awards XP on completion</li>
 * </ul>
 */
public class NpcFetchCommandGoal extends Goal {

    private final EntityDamsel npc;
    private final double speedModifier;

    /** Search radius for items */
    private static final double SEARCH_RADIUS = 16.0;

    /** 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;

    /** Maximum time to complete fetch */
    private static final int MAX_TICKS = 600; // 30 seconds

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

    private enum FetchPhase {
        SEARCHING,
        FETCHING,
        RETURNING,
    }

    private FetchPhase phase;

    @Nullable
    private ItemEntity targetItem;

    @Nullable
    private Player master;

    private int ticksRunning;
    private int pathRecalcTimer;

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

        // Find master
        this.master = NpcGoalHelper.findCommandingPlayer(npc);
        if (this.master == null) {
            return false;
        }

        // Find nearest item
        this.targetItem = findNearestItem();
        return this.targetItem != null;
    }

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

        // In FETCHING phase, item must still exist
        if (this.phase == FetchPhase.FETCHING) {
            if (this.targetItem == null || !this.targetItem.isAlive()) {
                // Item gone, find new one or abort
                this.targetItem = findNearestItem();
                if (this.targetItem == null) {
                    return false;
                }
            }
        }

        return true;
    }

    @Override
    public void start() {
        this.phase = FetchPhase.FETCHING;
        this.ticksRunning = 0;
        this.pathRecalcTimer = 0;
    }

    @Override
    public void stop() {
        // Check if we completed successfully (returned item to master)
        if (this.phase == FetchPhase.RETURNING && this.master != null) {
            double dist = npc.distanceTo(master);
            if (dist <= DELIVER_DISTANCE) {
                // Success! Award XP
            }
        }

        // Cancel command
        npc.cancelCommand();
        this.targetItem = null;
        this.master = null;
        npc.getNavigation().stop();
    }

    @Override
    public void tick() {
        this.ticksRunning++;

        switch (this.phase) {
            case FETCHING -> tickFetching();
            case RETURNING -> tickReturning();
            default -> {
            }
        }
    }

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

        // Look at item
        npc.getLookControl().setLookAt(targetItem, 10.0F, 30.0F);

        // 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;
        }

        // Look at master
        npc.getLookControl().setLookAt(master, 10.0F, 30.0F);

        // 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) {
            deliverItem();
        }
    }

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

        // Give item to NPC's hand (uses custom EntityDamsel method, not vanilla)
        npc.setMainHandItem(targetItem.getItem().copy());
        targetItem.discard();
        this.targetItem = null;

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

        // Switch to returning phase
        this.phase = FetchPhase.RETURNING;
        this.pathRecalcTimer = 0;
    }

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

        // Drop item at master's feet (uses custom EntityDamsel method)
        ItemStack heldItem = npc.getMainHandItem();
        if (!heldItem.isEmpty()) {
            // Give to player or drop
            if (!master.getInventory().add(heldItem.copy())) {
                // Inventory full, drop it
                npc.spawnAtLocation(heldItem.copy());
            }
            npc.setMainHandItem(ItemStack.EMPTY);
        }

        // Command complete - stop() will award XP
    }

    @Nullable
    private ItemEntity findNearestItem() {
        AABB searchArea = npc.getBoundingBox().inflate(SEARCH_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;
    }
}
