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

import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.entities.EntityDamsel;
import com.tiedup.remake.items.base.IHasResistance;
import com.tiedup.remake.personality.PersonalityState;
import com.tiedup.remake.personality.PersonalityType;
import com.tiedup.remake.util.TiedUpSounds;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
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.phys.AABB;

/**
 * AI Goal: NPC attempts to struggle free from restraints.
 *
 * Personality System Phase I: Struggle System
 *
 * <p><b>Behavior:</b></p>
 * <ul>
 *   <li>Only active when NPC is tied up (has bind item)</li>
 *   <li>Ticks the struggle timer from PersonalityState</li>
 *   <li>When timer expires, attempts to struggle</li>
 *   <li>Success chance based on personality, training, mood, bind resistance</li>
 *   <li>On success: decrease bind resistance, free if resistance = 0</li>
 *   <li>On failure: gain training XP, reset timer</li>
 *   <li>Plays struggle animation during attempt</li>
 * </ul>
 */
public class NpcStruggleGoal extends Goal {

    private final EntityDamsel npc;

    /** Duration of struggle animation in ticks (must match animation JSON endTick) */
    private static final int STRUGGLE_ANIMATION_TICKS = 200; // 10 seconds (~2.5 animation loops)

    /** Radius to check for captor/allies */
    private static final double CHECK_RADIUS = 16.0;

    /** Current struggle state */
    private enum StrugglePhase {
        WAITING,
        ANIMATING,
        RESOLVING,
    }

    private StrugglePhase phase = StrugglePhase.WAITING;

    /** Animation countdown */
    private int animationTicks;

    /** Cached success roll result (calculated at start of animation) */
    private boolean pendingSuccess;

    public NpcStruggleGoal(EntityDamsel npc) {
        this.npc = npc;
        // Don't use movement flags - struggling doesn't require movement control
        this.setFlags(EnumSet.noneOf(Goal.Flag.class));
    }

    @Override
    public boolean canUse() {
        // Check game rule
        if (
            !SettingsAccessor.isNpcStruggleEnabled(npc.level().getGameRules())
        ) {
            return false;
        }

        // Must be tied up
        if (!npc.isTiedUp()) {
            return false;
        }

        // Check if bind can be struggled out of
        ItemStack bind = npc.getEquipment(BodyRegionV2.ARMS);
        if (bind.isEmpty()) {
            return false;
        }
        if (bind.getItem() instanceof IHasResistance resistanceItem) {
            if (!resistanceItem.canBeStruggledOut(bind)) {
                return false;
            }
        }

        // Must have personality state
        PersonalityState state = npc.getPersonalityState();
        if (state == null) {
            return false;
        }

        // MASOCHIST personality rarely struggles
        if (state.getPersonality() == PersonalityType.MASOCHIST) {
            if (npc.getRandom().nextFloat() > 0.2f) {
                return false; // 80% chance to skip struggle
            }
        }

        return true;
    }

    @Override
    public boolean canContinueToUse() {
        // Stop if no longer tied
        if (!npc.isTiedUp()) {
            return false;
        }

        // Continue if animating
        if (
            phase == StrugglePhase.ANIMATING || phase == StrugglePhase.RESOLVING
        ) {
            return true;
        }

        return canUse();
    }

    @Override
    public void start() {
        this.phase = StrugglePhase.WAITING;
        this.animationTicks = 0;

        // Fix: initialize timer on first bind to prevent immediate struggle attempt
        PersonalityState state = npc.getPersonalityState();
        if (state != null && state.getStruggleTimer() <= 0) {
            int baseInterval = getBaseInterval();
            state.resetStruggleTimer(baseInterval);
        }
    }

    @Override
    public void stop() {
        // Clear struggle animation
        npc.setStruggling(false);
        this.phase = StrugglePhase.WAITING;
    }

    @Override
    public void tick() {
        PersonalityState state = npc.getPersonalityState();
        if (state == null) return;

        switch (phase) {
            case WAITING -> tickWaiting(state);
            case ANIMATING -> tickAnimating(state);
            case RESOLVING -> tickResolving(state);
        }
    }

    /**
     * Wait phase: tick timer until struggle attempt.
     */
    private void tickWaiting(PersonalityState state) {
        // Tick the struggle timer
        if (state.tickStruggleTimer()) {
            // Timer expired - start struggle attempt
            beginStruggleAttempt(state);
        }
    }

    /**
     * Begin a struggle attempt - calculate success and start animation.
     */
    private void beginStruggleAttempt(PersonalityState state) {
        // Calculate success chance
        float bindResistance = getBindResistance();
        boolean captorNearby = isCaptorNearby();
        boolean allyNearby = isAllyNearby();

        float successChance = state.calculateStruggleChance(
            bindResistance,
            captorNearby,
            allyNearby
        );
        this.pendingSuccess = npc.getRandom().nextFloat() < successChance;

        // Start animation
        this.phase = StrugglePhase.ANIMATING;
        this.animationTicks = STRUGGLE_ANIMATION_TICKS;
        npc.setStruggling(true);

        // Play struggle sound
        TiedUpSounds.playStruggleSound(npc);
    }

    /**
     * Animation phase: play struggle animation.
     */
    private void tickAnimating(PersonalityState state) {
        if (--this.animationTicks <= 0) {
            // Animation finished, resolve result
            this.phase = StrugglePhase.RESOLVING;
        }
    }

    /**
     * Resolve phase: apply struggle result.
     */
    private void tickResolving(PersonalityState state) {
        // Stop animation
        npc.setStruggling(false);

        if (this.pendingSuccess) {
            handleStruggleSuccess(state);
        } else {
            handleStruggleFailure(state);
        }

        // Back to waiting
        this.phase = StrugglePhase.WAITING;
    }

    /**
     * Handle successful struggle - decrease bind resistance.
     */
    private void handleStruggleSuccess(PersonalityState state) {
        ItemStack bind = npc.getEquipment(BodyRegionV2.ARMS);
        if (bind.isEmpty()) return;

        if (bind.getItem() instanceof IHasResistance resistanceItem) {
            int newResistance = resistanceItem.decreaseResistance(bind, npc);

            // Update the bind item in entity (stores the modified resistance)
            npc.replaceEquipment(BodyRegionV2.ARMS, bind, false);

            if (newResistance <= 0) {
                // Broke free!
                handleEscape(state);
            } else {
                // Loosened but not free
                state.recordStruggleResult(true, getBaseInterval());
            }
        }
    }

    /**
     * Handle full escape - remove bind item.
     */
    private void handleEscape(PersonalityState state) {
        // Remove bind and drop it
        ItemStack bind = npc.unequip(BodyRegionV2.ARMS);
        if (!bind.isEmpty()) {
            npc.spawnAtLocation(bind);
        }

        // Record escape
        state.recordStruggleResult(true, getBaseInterval());

        // Personality-specific behavior after escape
        PersonalityType personality = state.getPersonality();
        if (
            personality == PersonalityType.FIERCE ||
            personality == PersonalityType.DEFIANT
        ) {
            // Aggressive personalities might try to flee or attack
            // (Could trigger flee behavior here)
        }
    }

    /**
     * Handle failed struggle - gain XP, reset timer.
     */
    private void handleStruggleFailure(PersonalityState state) {
        state.recordStruggleResult(false, getBaseInterval());

        // Training XP is awarded in recordStruggleResult
        // Timer is reset in recordStruggleResult
    }

    /**
     * Get the base struggle interval from game rules.
     */
    private int getBaseInterval() {
        return SettingsAccessor.getNpcStruggleInterval(
            npc.level().getGameRules()
        );
    }

    /**
     * Get the resistance value of the current bind item.
     */
    private float getBindResistance() {
        ItemStack bind = npc.getEquipment(BodyRegionV2.ARMS);
        if (bind.isEmpty()) return 1.0f;

        if (bind.getItem() instanceof IHasResistance resistanceItem) {
            return resistanceItem.getCurrentResistance(bind, npc);
        }
        return 100.0f; // Default high resistance
    }

    /**
     * Check if the captor (collar owner) is nearby.
     */
    private boolean isCaptorNearby() {
        if (!npc.hasCollar()) return false;

        // Get collar owner UUID
        ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
        if (collar.isEmpty()) return false;

        if (com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collar)) {
            List<UUID> ownerUUIDs = com.tiedup.remake.v2.bondage.CollarHelper.getOwners(collar);
            if (!ownerUUIDs.isEmpty()) {
                // Check if any owner is nearby
                List<Player> players = npc
                    .level()
                    .getEntitiesOfClass(
                        Player.class,
                        npc.getBoundingBox().inflate(CHECK_RADIUS)
                    );
                for (Player player : players) {
                    if (ownerUUIDs.contains(player.getUUID())) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Get the first captor UUID (collar owner) if available.
     */
    @Nullable
    private UUID getCaptorUUID() {
        if (!npc.hasCollar()) return null;

        ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
        if (collar.isEmpty()) return null;

        if (com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collar)) {
            List<UUID> ownerUUIDs = com.tiedup.remake.v2.bondage.CollarHelper.getOwners(collar);
            if (!ownerUUIDs.isEmpty()) {
                return ownerUUIDs.get(0);
            }
        }
        return null;
    }

    /**
     * Check if an ally NPC (another captured damsel) is nearby.
     */
    private boolean isAllyNearby() {
        List<EntityDamsel> nearbyDamsels = npc
            .level()
            .getEntitiesOfClass(
                EntityDamsel.class,
                npc.getBoundingBox().inflate(CHECK_RADIUS),
                d -> d != npc && !d.isTiedUp() // Free damsels only
            );
        return !nearbyDamsels.isEmpty();
    }

    @Override
    public boolean requiresUpdateEveryTick() {
        return true;
    }
}
