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

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory;
import com.tiedup.remake.entities.EntityKidnapper;
import com.tiedup.remake.prison.PrisonerManager;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.util.KidnappedHelper;
import java.util.EnumSet;
import java.util.UUID;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.goal.Goal;

/**
 * AI Goal for EntityKidnapper to actively search for an escaped prisoner.
 *
 *
 * This goal:
 * 1. Activates when kidnapper is in ALERT state with a valid alertTarget
 * 2. Pursues the escapee at increased speed (1.2x)
 * 3. Broadcasts position to other kidnappers on start
 * 4. Times out after 60 seconds (returns to GUARD/PATROL)
 * 5. On successful capture, transitions to PUNISH state
 */
public class KidnapperAlertGoal extends Goal {

    private final EntityKidnapper kidnapper;

    /** Speed multiplier when in ALERT state */
    private static final double ALERT_SPEED_MODIFIER = 1.2;

    /** Maximum distance to pursue before giving up */
    private static final double MAX_PURSUIT_DISTANCE = 50.0;

    /** Capture distance */
    private static final double CAPTURE_DISTANCE = 2.5;

    /** Alert timeout in ticks (60 seconds) */
    private static final int ALERT_TIMEOUT = 1200;

    /** Dialogue radius */
    private static final int DIALOGUE_RADIUS = 20;

    /** Current target being pursued */
    private LivingEntity target;

    /** Ticks spent in alert mode */
    private int alertTicks;

    /** Ticks until next path recalculation */
    private int pathRecalcTicks;

    public KidnapperAlertGoal(EntityKidnapper kidnapper) {
        this.kidnapper = kidnapper;
        this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
    }

    @Override
    public boolean canUse() {
        // Must be in ALERT state
        if (this.kidnapper.getCurrentState() != KidnapperState.ALERT) {
            return false;
        }

        // Must have an alert target
        LivingEntity alertTarget = this.kidnapper.getAlertTarget();
        if (alertTarget == null || !alertTarget.isAlive()) {
            return false;
        }

        // Must not be tied up
        if (this.kidnapper.isTiedUp()) {
            return false;
        }

        // Must not already have a captive
        if (this.kidnapper.hasCaptives()) {
            return false;
        }

        this.target = alertTarget;
        return true;
    }

    @Override
    public boolean canContinueToUse() {
        if (this.target == null || !this.target.isAlive()) {
            return false;
        }

        if (this.kidnapper.isTiedUp()) {
            return false;
        }

        // Stop if we captured someone
        if (this.kidnapper.hasCaptives()) {
            return false;
        }

        // Stop if alert timed out
        if (this.alertTicks >= ALERT_TIMEOUT) {
            return false;
        }

        // Stop if target is too far
        if (this.kidnapper.distanceTo(this.target) > MAX_PURSUIT_DISTANCE) {
            return false;
        }

        // Stop if target was captured by someone else
        IBondageState state = KidnappedHelper.getKidnappedState(this.target);
        if (state != null && state.isCaptive()) {
            return false;
        }

        // Continue if still in ALERT state
        return this.kidnapper.getCurrentState() == KidnapperState.ALERT;
    }

    @Override
    public void start() {
        this.alertTicks = 0;
        this.pathRecalcTicks = 0;

        // Announce pursuit
        this.kidnapper.talkToPlayersInRadius(
            DialogueCategory.CAPTURE_CHASE,
            DIALOGUE_RADIUS
        );

        // Broadcast to other kidnappers
        this.kidnapper.broadcastAlert(this.target);

        TiedUpMod.LOGGER.info(
            "[KidnapperAlertGoal] {} started searching for {}",
            this.kidnapper.getNpcName(),
            this.target.getName().getString()
        );
    }

    @Override
    public void stop() {
        // Clear alert target if we're giving up (timeout or target lost)
        if (!this.kidnapper.hasCaptives()) {
            this.kidnapper.setAlertTarget(null);

            // Return to IDLE state - will be picked up by GUARD or PATROL
            if (this.kidnapper.getCurrentState() == KidnapperState.ALERT) {
                this.kidnapper.setCurrentState(KidnapperState.IDLE);
            }

            if (this.alertTicks >= ALERT_TIMEOUT) {
                TiedUpMod.LOGGER.info(
                    "[KidnapperAlertGoal] {} gave up searching after timeout",
                    this.kidnapper.getNpcName()
                );
            }
        }

        this.target = null;
        this.alertTicks = 0;
        this.kidnapper.getNavigation().stop();
    }

    @Override
    public void tick() {
        if (this.target == null) {
            return;
        }

        this.alertTicks++;
        this.pathRecalcTicks++;

        // Always look at target
        this.kidnapper.getLookControl().setLookAt(this.target, 30.0F, 30.0F);

        double distance = this.kidnapper.distanceTo(this.target);

        // Recalculate path periodically (every 10 ticks for responsive pursuit)
        if (this.pathRecalcTicks >= 10) {
            this.pathRecalcTicks = 0;
            this.kidnapper.getNavigation().moveTo(
                this.target,
                ALERT_SPEED_MODIFIER
            );
        }

        // Attempt capture if close enough
        if (distance <= CAPTURE_DISTANCE) {
            attemptCapture();
        }

        // Periodic re-broadcast every 10 seconds to keep other kidnappers updated
        if (this.alertTicks % 200 == 0 && this.alertTicks > 0) {
            this.kidnapper.broadcastAlert(this.target);
        }
    }

    /**
     * Attempt to capture the target.
     */
    private void attemptCapture() {
        // First check if target is still suitable (not protected by grace/labor)
        if (!this.kidnapper.isSuitableTarget(this.target)) {
            TiedUpMod.LOGGER.debug(
                "[KidnapperAlertGoal] {} - target {} is protected, aborting capture",
                this.kidnapper.getNpcName(),
                this.target.getName().getString()
            );
            this.kidnapper.setAlertTarget(null);
            this.kidnapper.setCurrentState(KidnapperState.IDLE);
            return;
        }

        IBondageState state = KidnappedHelper.getKidnappedState(this.target);
        if (state == null) {
            return;
        }

        // Check if target is still tied up
        if (state.isTiedUp()) {
            // Quick recapture via PrisonerService (atomic: PrisonerManager + leash)
            boolean recaptured = false;
            if (
                this.kidnapper.level() instanceof
                    net.minecraft.server.level.ServerLevel serverLevel
            ) {
                recaptured =
                    com.tiedup.remake.prison.service.PrisonerService.get().capture(
                        serverLevel,
                        this.target,
                        this.kidnapper
                    );
            }
            if (recaptured) {
                this.kidnapper.talkToPlayersInRadius(
                    DialogueCategory.CAPTURE_ENSLAVED,
                    DIALOGUE_RADIUS
                );

                TiedUpMod.LOGGER.info(
                    "[KidnapperAlertGoal] {} recaptured {}!",
                    this.kidnapper.getNpcName(),
                    this.target.getName().getString()
                );

                // Transition to PUNISH state
                this.kidnapper.setCurrentState(KidnapperState.PUNISH);
                this.kidnapper.setAlertTarget(null);
            }
        } else {
            // Target is no longer tied - set as hunt target for normal capture
            this.kidnapper.setTarget(this.target);
            this.kidnapper.setAlertTarget(null);
            this.kidnapper.setCurrentState(KidnapperState.CAPTURE);
        }
    }

    @Override
    public boolean requiresUpdateEveryTick() {
        return false; // Every other tick is fine for pursuit
    }
}
