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 java.util.Map;
import java.util.stream.Collectors;
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.EntityType;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;

/**
 * AI Goal: NPC breeds animals in a zone around a chest hub.
 *
 * <p>Takes food from chest, feeds pairs of same-species animals to breed them.</p>
 * <p>Cap: max 8 animals per species in zone to prevent lag.</p>
 */
public class NpcBreedCommandGoal extends Goal {

    private final EntityDamsel npc;
    private final double speedModifier;

    private static final int WORK_RADIUS = 16;
    private static final double INTERACT_DISTANCE = 2.0;
    private static final int SCAN_INTERVAL = 60;
    private static final int IDLE_TIMEOUT = 400;
    private static final int XP_INTERVAL = 1200;
    private static final int FEED_COOLDOWN = 30;
    private static final int MAX_ANIMALS_PER_SPECIES = 8;
    private static final float WORK_INTENSITY = 0.7f;

    private enum BreedPhase {
        SCANNING,
        MOVING,
        FEEDING,
        IDLE,
    }

    private BreedPhase phase;

    @Nullable
    private BlockPos chestPos;

    @Nullable
    private Animal targetAnimal;

    @Nullable
    private Animal partnerAnimal;

    @Nullable
    private Player master;

    @Nullable
    private ItemStack heldFood;

    private int scanTimer;
    private int idleTimer;
    private int xpTimer;
    private int pathRecalcTimer;
    private int feedTimer;
    private boolean fedFirst;

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

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

    @Override
    public boolean canContinueToUse() {
        if (npc.getActiveCommand() != NpcCommand.BREED) {
            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 = BreedPhase.SCANNING;
        this.scanTimer = 0;
        this.idleTimer = 0;
        this.xpTimer = 0;
        this.pathRecalcTimer = 0;
        this.feedTimer = 0;
        this.targetAnimal = null;
        this.partnerAnimal = null;
        this.heldFood = null;
        this.fedFirst = false;
    }

    @Override
    public void stop() {
        this.chestPos = null;
        this.targetAnimal = null;
        this.partnerAnimal = null;
        this.master = null;
        this.heldFood = null;
        npc.getNavigation().stop();
    }

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

        switch (this.phase) {
            case SCANNING -> tickScanning();
            case MOVING -> tickMoving();
            case FEEDING -> tickFeeding();
            case IDLE -> tickIdle();
        }
    }

    private void tickScanning() {
        if (--this.scanTimer <= 0) {
            float efficiency = NpcGoalHelper.getJobEfficiency(
                npc,
                NpcCommand.BREED
            );
            this.scanTimer = (int) (SCAN_INTERVAL / efficiency);

            // Find a breedable pair
            if (findBreedablePair()) {
                // Try to get food from chest
                this.heldFood = takeFoodFromChest();
                if (this.heldFood != null) {
                    this.phase = BreedPhase.MOVING;
                    this.idleTimer = 0;
                    this.fedFirst = false;
                } else {
                    // No suitable food in chest
                    this.idleTimer += SCAN_INTERVAL;
                    if (this.idleTimer >= IDLE_TIMEOUT) {
                        this.phase = BreedPhase.IDLE;
                    }
                }
            } else {
                this.idleTimer += SCAN_INTERVAL;
                if (this.idleTimer >= IDLE_TIMEOUT) {
                    this.phase = BreedPhase.IDLE;
                }
            }
        }
    }

    private void tickMoving() {
        Animal target = this.fedFirst ? this.partnerAnimal : this.targetAnimal;

        if (target == null || !target.isAlive()) {
            this.phase = BreedPhase.SCANNING;
            this.scanTimer = 0;
            return;
        }

        if (--this.pathRecalcTimer <= 0) {
            this.pathRecalcTimer = 10;
            npc.getNavigation().moveTo(target, speedModifier);
        }

        npc.getLookControl().setLookAt(target, 30.0f, 30.0f);

        double dist = npc.distanceToSqr(target);
        if (dist <= INTERACT_DISTANCE * INTERACT_DISTANCE) {
            npc.getNavigation().stop();
            this.phase = BreedPhase.FEEDING;
            this.feedTimer = FEED_COOLDOWN;
        }
    }

    private void tickFeeding() {
        Animal target = this.fedFirst ? this.partnerAnimal : this.targetAnimal;

        if (target == null || !target.isAlive()) {
            this.phase = BreedPhase.SCANNING;
            this.scanTimer = 0;
            return;
        }

        npc.getLookControl().setLookAt(target, 30.0f, 30.0f);

        if (--this.feedTimer > 0) {
            return;
        }

        // Feed the animal
        target.setInLove(null);

        npc
            .level()
            .playSound(
                null,
                target.getX(),
                target.getY(),
                target.getZ(),
                SoundEvents.GENERIC_EAT,
                SoundSource.NEUTRAL,
                1.0f,
                1.0f
            );

        if (!this.fedFirst) {
            // Feed the partner next
            this.fedFirst = true;
            this.phase = BreedPhase.MOVING;
            this.pathRecalcTimer = 0;
        } else {
            // Both fed, breeding triggered
            awardXP(2);

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

            this.targetAnimal = null;
            this.partnerAnimal = null;
            this.heldFood = null;
            this.phase = BreedPhase.SCANNING;
            this.scanTimer = 40; // Short delay
        }
    }

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

            if (findBreedablePair()) {
                this.heldFood = takeFoodFromChest();
                if (this.heldFood != null) {
                    this.phase = BreedPhase.MOVING;
                    this.idleTimer = 0;
                    this.fedFirst = false;
                }
            }
        }
    }

    private boolean findBreedablePair() {
        Level level = npc.level();

        // Find all animals in zone
        List<Animal> animals = level.getEntitiesOfClass(
            Animal.class,
            npc.getBoundingBox().inflate(WORK_RADIUS),
            animal ->
                animal.isAlive() && !animal.isBaby() && animal.canFallInLove()
        );

        if (animals.size() < 2) return false;

        // Group by species type
        Map<EntityType<?>, List<Animal>> bySpecies = animals
            .stream()
            .collect(Collectors.groupingBy(Animal::getType));

        for (var entry : bySpecies.entrySet()) {
            List<Animal> speciesAnimals = entry.getValue();

            // Check cap
            if (speciesAnimals.size() >= MAX_ANIMALS_PER_SPECIES) {
                continue; // Too many of this species already
            }

            // Need at least 2 non-baby, non-in-love animals
            List<Animal> breedable = speciesAnimals
                .stream()
                .filter(a -> !a.isBaby() && a.canFallInLove())
                .toList();

            if (breedable.size() >= 2) {
                // Check if we have food for this species in the chest
                if (hasFoodForAnimal(breedable.get(0))) {
                    this.targetAnimal = breedable.get(0);
                    this.partnerAnimal = breedable.get(1);
                    return true;
                }
            }
        }

        return false;
    }

    private boolean hasFoodForAnimal(Animal animal) {
        if (chestPos == null) return false;

        Container container = NpcGoalHelper.getChestContainer(
            npc.level(),
            chestPos
        );
        if (container == null) return false;

        for (int i = 0; i < container.getContainerSize(); i++) {
            ItemStack stack = container.getItem(i);
            if (
                !stack.isEmpty() &&
                animal.isFood(stack) &&
                stack.getCount() >= 2
            ) {
                return true;
            }
        }

        return false;
    }

    @Nullable
    private ItemStack takeFoodFromChest() {
        if (chestPos == null || targetAnimal == null) return null;

        Container container = NpcGoalHelper.getChestContainer(
            npc.level(),
            chestPos
        );
        if (container == null) return null;

        for (int i = 0; i < container.getContainerSize(); i++) {
            ItemStack stack = container.getItem(i);
            if (
                !stack.isEmpty() &&
                targetAnimal.isFood(stack) &&
                stack.getCount() >= 2
            ) {
                // Take 2 food items (one for each animal)
                ItemStack taken = stack.split(2);
                if (stack.isEmpty()) {
                    container.setItem(i, ItemStack.EMPTY);
                }
                container.setChanged();
                return taken;
            }
        }

        return null;
    }

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