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

import com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory;
import com.tiedup.remake.entities.EntityDamsel;
import com.tiedup.remake.entities.EntityKidnapper;
import java.util.EnumSet;
import java.util.List;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.util.DefaultRandomPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;

/**
 * AI Goal: Damsel flees from nearby players and kidnappers.
 *
 *
 * <p><b>Behavior:</b></p>
 * <ul>
 *   <li>Only active if damsel is NOT tied up</li>
 *   <li>Only active if damsel is NOT blindfolded (can't see player)</li>
 *   <li>Only active if damsel is NOT collared (enslaved shouldn't flee)</li>
 *   <li>Flees from players AND kidnappers within maxDistance</li>
 *   <li>Sprint speed when threat is very close</li>
 * </ul>
 */
public class DamselFleeFromPlayerGoal extends Goal {

    private final EntityDamsel damsel;
    private final float maxDistance;
    private final double walkSpeedModifier;
    private final double sprintSpeedModifier;
    private LivingEntity fleeTarget; // Can be Player or EntityKidnapper
    private Path path;

    /** Radius for dialogue broadcast */
    private static final int DIALOGUE_RADIUS = 20;

    /** Sprint cooldown in ticks (15-20 seconds between sprints) */
    private static final int SPRINT_COOLDOWN_MIN = 300;
    private static final int SPRINT_COOLDOWN_MAX = 400;

    /** How long sprint lasts in ticks (1-2 seconds) */
    private static final int SPRINT_DURATION = 30;

    /** Current sprint cooldown timer */
    private int sprintCooldown = 0;

    /** Current sprint duration timer */
    private int sprintTimer = 0;

    /** Whether currently sprinting */
    private boolean isSprinting = false;

    /** Path recalculation delay */
    private int pathRecalcDelay = 0;

    /** How often to recalculate path (in ticks) */
    private static final int PATH_RECALC_INTERVAL = 10;

    /** Cooldown for canUse() threat scanning - prevents expensive search every tick */
    private int canUseCooldown = 0;

    /**
     * Create flee from player goal.
     *
     * @param damsel Damsel entity
     * @param maxDistance Max distance to detect and flee from players (blocks)
     * @param walkSpeed Walking speed multiplier
     * @param sprintSpeed Sprint speed multiplier (when player very close)
     */
    public DamselFleeFromPlayerGoal(
        EntityDamsel damsel,
        float maxDistance,
        double walkSpeed,
        double sprintSpeed
    ) {
        this.damsel = damsel;
        this.maxDistance = maxDistance;
        this.walkSpeedModifier = walkSpeed;
        this.sprintSpeedModifier = sprintSpeed;
        this.setFlags(EnumSet.of(Goal.Flag.MOVE));
    }

    @Override
    public boolean canUse() {
        // Throttle threat scanning - only check every 10 ticks (0.5 sec)
        if (--this.canUseCooldown > 0) {
            return false;
        }
        this.canUseCooldown = 10;

        // Can't flee if tied up, blindfolded, or collared
        if (damsel.isTiedUp() || damsel.isBlindfolded() || damsel.hasCollar()) {
            return false;
        }

        // Find nearest threat (player or kidnapper)
        this.fleeTarget = findNearestThreat();
        if (fleeTarget == null) {
            return false;
        }

        // Calculate flee path (away from threat)
        Vec3 fleePos = DefaultRandomPos.getPosAway(
            damsel,
            16, // Range
            7, // Y variance
            fleeTarget.position()
        );

        if (fleePos == null) {
            return false;
        }

        this.path = damsel
            .getNavigation()
            .createPath(fleePos.x, fleePos.y, fleePos.z, 0);
        return this.path != null;
    }

    /**
     * Find the nearest threat to flee from.
     * Prioritizes kidnappers over players.
     * Ignores the savior (player who freed this damsel).
     *
     * @return Nearest threat (kidnapper or player), or null if none
     */
    private LivingEntity findNearestThreat() {
        AABB searchBox = damsel.getBoundingBox().inflate(maxDistance);

        // First check for kidnappers (higher threat)
        List<EntityKidnapper> kidnappers = damsel
            .level()
            .getEntitiesOfClass(
                EntityKidnapper.class,
                searchBox,
                k -> k.isAlive() && damsel.distanceTo(k) <= maxDistance
            );

        if (!kidnappers.isEmpty()) {
            // Return closest kidnapper
            EntityKidnapper closest = null;
            double closestDist = Double.MAX_VALUE;
            for (EntityKidnapper k : kidnappers) {
                double dist = damsel.distanceToSqr(k);
                if (dist < closestDist) {
                    closestDist = dist;
                    closest = k;
                }
            }
            return closest;
        }

        // Then check for players (excluding savior)
        List<Player> players = damsel
            .level()
            .getEntitiesOfClass(
                Player.class,
                searchBox,
                p ->
                    p.isAlive() &&
                    damsel.distanceTo(p) <= maxDistance &&
                    !damsel.isSavior(p) // Don't flee from savior
            );

        if (players.isEmpty()) {
            return null;
        }

        // Return closest player
        Player closest = null;
        double closestDist = Double.MAX_VALUE;
        for (Player p : players) {
            double dist = damsel.distanceToSqr(p);
            if (dist < closestDist) {
                closestDist = dist;
                closest = p;
            }
        }
        return closest;
    }

    @Override
    public boolean canContinueToUse() {
        // Stop if got captured/restrained
        if (damsel.isTiedUp() || damsel.isBlindfolded() || damsel.hasCollar()) {
            return false;
        }

        // Stop if target gone
        if (fleeTarget == null || !fleeTarget.isAlive()) {
            return false;
        }

        // Continue as long as threat is within range (don't check isDone - we recalc in tick)
        return fleeTarget.distanceToSqr(damsel) < (maxDistance * maxDistance);
    }

    @Override
    public void start() {
        damsel.getNavigation().moveTo(this.path, this.walkSpeedModifier);

        // Reset sprint state when starting to flee
        this.isSprinting = false;
        this.sprintTimer = 0;
        this.sprintCooldown = 0; // Allow immediate first sprint
        this.pathRecalcDelay = PATH_RECALC_INTERVAL; // Don't recalc immediately

        // Broadcast flee dialogue to nearby players (with cooldown)
        if (!damsel.level().isClientSide() && !damsel.isGagged()) {
            damsel.talkToPlayersInRadiusWithCooldown(
                DialogueCategory.DAMSEL_FLEE,
                DIALOGUE_RADIUS
            );
        }
    }

    @Override
    public void stop() {
        this.fleeTarget = null;
        this.path = null;
        this.isSprinting = false;
        this.sprintTimer = 0;
        damsel.getNavigation().stop();
    }

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

        // Recalculate flee path periodically for smooth movement
        // Only recalc if navigation is done or timer expired
        boolean needsNewPath =
            damsel.getNavigation().isDone() || --pathRecalcDelay <= 0;

        if (needsNewPath) {
            pathRecalcDelay = PATH_RECALC_INTERVAL;

            // Update threat target (might have changed)
            LivingEntity newThreat = findNearestThreat();
            if (newThreat != null) {
                this.fleeTarget = newThreat;
            }

            // Calculate new flee position
            Vec3 fleePos = DefaultRandomPos.getPosAway(
                damsel,
                16,
                7,
                fleeTarget.position()
            );

            if (fleePos != null) {
                // Move directly to flee position
                damsel
                    .getNavigation()
                    .moveTo(
                        fleePos.x,
                        fleePos.y,
                        fleePos.z,
                        isSprinting ? sprintSpeedModifier : walkSpeedModifier
                    );
            } else {
                // Fallback: move directly away from threat
                Vec3 awayDir = damsel
                    .position()
                    .subtract(fleeTarget.position())
                    .normalize();
                Vec3 fallbackPos = damsel.position().add(awayDir.scale(8));
                damsel
                    .getNavigation()
                    .moveTo(
                        fallbackPos.x,
                        damsel.getY(),
                        fallbackPos.z,
                        isSprinting ? sprintSpeedModifier : walkSpeedModifier
                    );
            }
        }

        // Decrement sprint cooldown
        if (sprintCooldown > 0) {
            sprintCooldown--;
        }

        // Handle sprint duration
        if (isSprinting) {
            sprintTimer--;
            if (sprintTimer <= 0) {
                // Sprint ended, start cooldown
                isSprinting = false;
                sprintCooldown =
                    SPRINT_COOLDOWN_MIN +
                    damsel
                        .getRandom()
                        .nextInt(SPRINT_COOLDOWN_MAX - SPRINT_COOLDOWN_MIN);
            }
        }

        // Check if should start sprinting (threat very close + not on cooldown)
        double distSqr = damsel.distanceToSqr(fleeTarget);
        double sprintThreshold = 4.0 * 4.0;

        if (!isSprinting && sprintCooldown <= 0 && distSqr < sprintThreshold) {
            // Start sprint
            isSprinting = true;
            sprintTimer = SPRINT_DURATION;
        }

        // Apply speed modifier
        if (isSprinting) {
            damsel.getNavigation().setSpeedModifier(sprintSpeedModifier);
        } else {
            damsel.getNavigation().setSpeedModifier(walkSpeedModifier);
        }
    }
}
