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

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.dialogue.DialogueBridge;
import com.tiedup.remake.entities.EntityKidnapper;
import com.tiedup.remake.entities.EntityMaster;
import com.tiedup.remake.state.IRestrainable;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.EnumSet;
import java.util.List;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.player.Player;

/**
 * AI Goal for EntityMaster to approach and buy a player from a Kidnapper.
 *
 * Flow:
 * 1. Master spawns 50 blocks away
 * 2. Master walks towards the Kidnapper
 * 3. Master arrives and greets Kidnapper (RP dialogue)
 * 4. Master negotiates/inspects the "merchandise"
 * 5. Master completes purchase
 * 6. Kidnapper leaves, Master takes ownership
 */
public class MasterBuyPlayerGoal extends Goal {

    private final EntityMaster master;

    /** Search radius for kidnappers (if not pre-set) */
    private static final double SEARCH_RADIUS = 64.0;

    /** Distance to start negotiation dialogue */
    private static final double GREETING_DISTANCE = 8.0;

    /** Distance to complete purchase */
    private static final double PURCHASE_DISTANCE = 3.0;

    /** Target kidnapper selling a player */
    private EntityKidnapper targetKidnapper = null;

    /** Purchase phase */
    private PurchasePhase phase = PurchasePhase.APPROACHING;

    /** Timer for current phase */
    private int phaseTimer = 0;

    /** Phase durations (in ticks) */
    private static final int GREETING_DURATION = 60; // 3 seconds
    private static final int INSPECT_DURATION = 80; // 4 seconds
    private static final int NEGOTIATE_DURATION = 60; // 3 seconds
    private static final int PURCHASE_DURATION = 60; // 3 seconds

    /** Whether we've said the approach dialogue */
    private boolean hasSaidApproach = false;

    /** Pathfinding recalculation cooldown to avoid path thrashing */
    private int pathRecalcCooldown = 0;

    private enum PurchasePhase {
        APPROACHING, // Walking to kidnapper
        GREETING, // Arrived, greeting
        INSPECTING, // Looking at the merchandise
        NEGOTIATING, // Discussing price
        PURCHASING, // Completing purchase
        COMPLETE, // Done
    }

    public MasterBuyPlayerGoal(EntityMaster master) {
        this.master = master;
        this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
    }

    @Override
    public boolean canUse() {
        // Preserve state for ALL phases in progress (including APPROACHING)
        // This prevents goal from restarting during approach
        if (targetKidnapper != null && phase != PurchasePhase.COMPLETE) {
            // Validate target is still valid
            if (targetKidnapper.isAlive()) {
                return true; // Continue without restarting
            }
            // Target died - reset
            targetKidnapper = null;
            phase = PurchasePhase.APPROACHING;
        }

        // Must be in purchasing state or idle without pet
        MasterState state = master.getStateManager().getCurrentState();
        if (state != MasterState.PURCHASING && state != MasterState.IDLE) {
            return false;
        }

        // Don't search if already has a pet
        if (master.hasPet()) {
            return false;
        }

        // First check if we have a pre-set selling kidnapper
        if (master.hasSellingKidnapper()) {
            targetKidnapper = master.getSellingKidnapper();
            return true;
        }

        // Otherwise search for one nearby
        targetKidnapper = findSellingKidnapper();
        return targetKidnapper != null;
    }

    @Override
    public boolean canContinueToUse() {
        // Once we've started greeting, we're committed to the purchase
        if (
            phase.ordinal() >= PurchasePhase.GREETING.ordinal() &&
            phase != PurchasePhase.COMPLETE
        ) {
            // Only stop if kidnapper dies or we completed
            return targetKidnapper != null && targetKidnapper.isAlive();
        }

        // During approach phase, check if target is still valid
        if (targetKidnapper == null || !targetKidnapper.isAlive()) {
            return false;
        }

        IRestrainable captive = targetKidnapper.getCaptive();
        if (captive == null) {
            return false;
        }

        // Stop if already has pet (purchase completed)
        return !master.hasPet() && phase != PurchasePhase.COMPLETE;
    }

    @Override
    public void start() {
        // Don't reset if we're already in progress (goal was briefly interrupted or resuming)
        // This includes APPROACHING phase - only reset when starting from scratch or COMPLETE
        if (targetKidnapper != null && phase != PurchasePhase.COMPLETE) {
            TiedUpMod.LOGGER.debug(
                "[MasterBuyPlayerGoal] Resuming {} at phase {}",
                master.getNpcName(),
                phase
            );
            // Ensure state is set
            master.setMasterState(MasterState.PURCHASING);
            return;
        }

        // Starting fresh - reset everything
        this.phaseTimer = 0;
        this.phase = PurchasePhase.APPROACHING;
        this.hasSaidApproach = false;
        this.pathRecalcCooldown = 0;
        master.setMasterState(MasterState.PURCHASING);

        TiedUpMod.LOGGER.debug(
            "[MasterBuyPlayerGoal] {} approaching {} to buy player",
            master.getNpcName(),
            targetKidnapper != null ? targetKidnapper.getNpcName() : "unknown"
        );
    }

    @Override
    public void stop() {
        master.getNavigation().stop();

        // Only fully reset if purchase completed or truly failed
        if (phase == PurchasePhase.COMPLETE || master.hasPet()) {
            TiedUpMod.LOGGER.debug(
                "[MasterBuyPlayerGoal] {} completed purchase, transitioning to FOLLOWING",
                master.getNpcName()
            );
            this.targetKidnapper = null;
            this.phaseTimer = 0;
            this.phase = PurchasePhase.APPROACHING;
            this.hasSaidApproach = false;
            master.clearSellingKidnapper();
            master.setMasterState(MasterState.FOLLOWING);
        }
        // Otherwise keep state for resume (goal was just temporarily interrupted)
    }

    @Override
    public void tick() {
        if (targetKidnapper == null) return;

        // Look at kidnapper
        master.getLookControl().setLookAt(targetKidnapper, 30.0F, 30.0F);

        double distSq = master.distanceToSqr(targetKidnapper);
        double dist = Math.sqrt(distSq);

        switch (phase) {
            case APPROACHING -> tickApproaching(dist);
            case GREETING -> tickGreeting();
            case INSPECTING -> tickInspecting();
            case NEGOTIATING -> tickNegotiating();
            case PURCHASING -> tickPurchasing();
            case COMPLETE -> {
            } // Do nothing
        }
    }

    private void tickApproaching(double dist) {
        // Approach dialogue when getting close (once)
        if (!hasSaidApproach && dist < 30) {
            IRestrainable captive = targetKidnapper.getCaptive();
            if (
                captive != null &&
                captive.asLivingEntity() instanceof Player player
            ) {
                DialogueBridge.talkTo(master, player, "purchase.interested");
            }
            hasSaidApproach = true;
        }

        if (dist > GREETING_DISTANCE) {
            // Recalculate path every 20 ticks (1 second) instead of every tick
            // This prevents path thrashing which dramatically slows movement
            if (pathRecalcCooldown <= 0) {
                master.getNavigation().moveTo(targetKidnapper, 1.0);
                pathRecalcCooldown = 20;
            } else {
                pathRecalcCooldown--;
            }
        } else {
            // Arrived - start greeting
            master.getNavigation().stop();
            phase = PurchasePhase.GREETING;
            phaseTimer = 0;
            pathRecalcCooldown = 0;

            // Greeting dialogue
            IRestrainable captive = targetKidnapper.getCaptive();
            if (
                captive != null &&
                captive.asLivingEntity() instanceof Player player
            ) {
                DialogueBridge.talkTo(master, player, "idle.greeting_stranger");
            }

            TiedUpMod.LOGGER.debug(
                "[MasterBuyPlayerGoal] {} arrived, greeting {}",
                master.getNpcName(),
                targetKidnapper.getNpcName()
            );
        }
    }

    private void tickGreeting() {
        phaseTimer++;

        // Move closer during greeting
        double dist = Math.sqrt(master.distanceToSqr(targetKidnapper));
        if (dist > PURCHASE_DISTANCE) {
            master.getNavigation().moveTo(targetKidnapper, 0.5);
        }

        if (phaseTimer >= GREETING_DURATION) {
            phase = PurchasePhase.INSPECTING;
            phaseTimer = 0;

            // Kidnapper offers sale dialogue
            IRestrainable captive = targetKidnapper.getCaptive();
            if (
                captive != null &&
                captive.asLivingEntity() instanceof Player player
            ) {
                targetKidnapper.talkTo(
                    player,
                    com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory.SALE_OFFER
                );
            }

            TiedUpMod.LOGGER.debug(
                "[MasterBuyPlayerGoal] {} inspecting merchandise",
                master.getNpcName()
            );
        }
    }

    private void tickInspecting() {
        phaseTimer++;

        // Look at the captive during inspection
        IRestrainable captive = targetKidnapper.getCaptive();
        if (captive != null) {
            master
                .getLookControl()
                .setLookAt(captive.asLivingEntity(), 30.0F, 30.0F);
        }

        if (phaseTimer >= INSPECT_DURATION) {
            phase = PurchasePhase.NEGOTIATING;
            phaseTimer = 0;

            // Master negotiates
            if (
                captive != null &&
                captive.asLivingEntity() instanceof Player player
            ) {
                DialogueBridge.talkTo(master, player, "purchase.negotiating");
            }

            TiedUpMod.LOGGER.debug(
                "[MasterBuyPlayerGoal] {} negotiating",
                master.getNpcName()
            );
        }
    }

    private void tickNegotiating() {
        phaseTimer++;

        // Look back at kidnapper
        master.getLookControl().setLookAt(targetKidnapper, 30.0F, 30.0F);

        if (phaseTimer >= NEGOTIATE_DURATION) {
            phase = PurchasePhase.PURCHASING;
            phaseTimer = 0;

            // Sale complete dialogue from kidnapper
            IRestrainable captive = targetKidnapper.getCaptive();
            if (
                captive != null &&
                captive.asLivingEntity() instanceof Player player
            ) {
                targetKidnapper.talkTo(
                    player,
                    com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory.SALE_COMPLETE
                );
            }

            TiedUpMod.LOGGER.debug(
                "[MasterBuyPlayerGoal] {} completing purchase",
                master.getNpcName()
            );
        }
    }

    private void tickPurchasing() {
        phaseTimer++;

        if (phaseTimer >= PURCHASE_DURATION) {
            completePurchase();
            phase = PurchasePhase.COMPLETE;
        }
    }

    /**
     * Find a nearby kidnapper who is selling a player.
     */
    private EntityKidnapper findSellingKidnapper() {
        List<EntityKidnapper> kidnappers = master
            .level()
            .getEntitiesOfClass(
                EntityKidnapper.class,
                master.getBoundingBox().inflate(SEARCH_RADIUS),
                k -> {
                    if (!k.isAlive() || k.isTiedUp()) return false;
                    IRestrainable captive = k.getCaptive();
                    if (captive == null) return false;
                    if (!captive.isForSell()) return false;
                    // Only buy players, not NPCs
                    return captive.asLivingEntity() instanceof Player;
                }
            );

        if (kidnappers.isEmpty()) return null;

        // Return closest one
        return kidnappers
            .stream()
            .min((a, b) ->
                Double.compare(master.distanceToSqr(a), master.distanceToSqr(b))
            )
            .orElse(null);
    }

    /**
     * Complete the purchase - take the player from kidnapper.
     * Properly transfers the captive with their bindings to the Master.
     */
    private void completePurchase() {
        if (targetKidnapper == null) return;

        IRestrainable captive = targetKidnapper.getCaptive();
        if (captive == null) return;

        if (!(captive.asLivingEntity() instanceof ServerPlayer player)) {
            TiedUpMod.LOGGER.warn(
                "[MasterBuyPlayerGoal] Captive is not a player!"
            );
            return;
        }

        TiedUpMod.LOGGER.debug(
            "[MasterBuyPlayerGoal] {} bought {} from {}",
            master.getNpcName(),
            player.getName().getString(),
            targetKidnapper.getNpcName()
        );

        // Purchase complete dialogue to player
        DialogueBridge.talkTo(master, player, "purchase.complete");

        // Cancel the sale before transfer
        captive.cancelSale();

        // IMPORTANT: Enable captive transfer flag on Kidnapper BEFORE transfer
        // This is required for transferCaptivityTo() to work
        targetKidnapper.setAllowCaptiveTransferFlag(true);

        // Use transferCaptivityTo for proper leash transfer
        // This detaches the leash from Kidnapper and attaches it to Master
        captive.transferCaptivityTo(master);

        // Disable transfer flag after transfer
        targetKidnapper.setAllowCaptiveTransferFlag(false);

        // Set up pet relationship in Master's state manager
        master.setPetPlayer(player);

        // IMPORTANT: Detach leash after purchase
        // The leash should only be attached during specific activities (walks, etc.)
        // Pet play mode uses collar control, not constant leashing
        if (
            player instanceof com.tiedup.remake.state.IPlayerLeashAccess access
        ) {
            access.tiedup$detachLeash();
            TiedUpMod.LOGGER.debug(
                "[MasterBuyPlayerGoal] Detached leash from {} after purchase",
                player.getName().getString()
            );
        }

        // Replace shock collar with choke collar for pet play
        master.putPetCollar(player);

        // Remove bindings (arms/legs) but keep the collar
        // The pet starts fresh without restraints (events can add them later)
        removeBindingsFromNewPet(captive);

        // Collaring dialogue
        DialogueBridge.talkTo(master, player, "purchase.collaring");

        // Notify kidnapper that purchase is complete via the WaitForBuyer goal
        var buyerGoal = targetKidnapper.getWaitForBuyerGoal();
        if (buyerGoal != null) {
            buyerGoal.onMasterPurchaseComplete();
        } else {
            // Fallback: just set get out state directly
            targetKidnapper.setGetOutState(true);
        }

        // Introduction dialogue
        DialogueBridge.talkTo(master, player, "purchase.introduction");

        TiedUpMod.LOGGER.debug(
            "[MasterBuyPlayerGoal] Transfer complete - {} now owns {}",
            master.getNpcName(),
            player.getName().getString()
        );

        targetKidnapper = null;
    }

    /**
     * Remove bindings from the new pet (but keep the collar and clothes).
     * The pet starts with only the choke collar - events may add restraints later.
     *
     * <p><b>Note:</b> We cannot use {@code untie(false)} because that clears ALL slots
     * including the collar. Instead, we manually remove specific regions.</p>
     *
     * @param captive The captive IRestrainable state
     */
    private void removeBindingsFromNewPet(IRestrainable captive) {
        if (!(captive.asLivingEntity() instanceof ServerPlayer player)) {
            return;
        }

        // Manually remove each region EXCEPT neck (collar) and torso (clothes)
        com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper.unequipFromRegion(
            player,
            com.tiedup.remake.v2.BodyRegionV2.ARMS,
            true
        );
        com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper.unequipFromRegion(
            player,
            com.tiedup.remake.v2.BodyRegionV2.MOUTH,
            true
        );
        com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper.unequipFromRegion(
            player,
            com.tiedup.remake.v2.BodyRegionV2.EYES,
            true
        );
        com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper.unequipFromRegion(
            player,
            com.tiedup.remake.v2.BodyRegionV2.EARS,
            true
        );
        com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper.unequipFromRegion(
            player,
            com.tiedup.remake.v2.BodyRegionV2.HANDS,
            true
        );

        // V1 speed reduction handled by MovementStyleManager (V2 tick-based).
        // See H6 fix — removing V1 calls prevents double stacking.

        TiedUpMod.LOGGER.debug(
            "[MasterBuyPlayerGoal] Removed bindings from new pet {}",
            captive.getKidnappedName()
        );
    }
}
