package com.tiedup.remake.compat.mca.ai.goals;

import com.tiedup.remake.compat.mca.MCABondageManager;
import com.tiedup.remake.state.IBondageState;
import java.util.EnumSet;
import java.util.List;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.util.DefaultRandomPos;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;

/**
 * AI Goal: MCA villager flees from entities of a specific class.
 *
 * Used when villager is tied but not collared (tries to escape).
 * Flees from players within detection range.
 */
public class MCAFleeGoal extends Goal {

    private final PathfinderMob mob;
    private final Class<? extends LivingEntity> fleeFromClass;
    private final float maxDistance;
    private final double walkSpeedModifier;
    private final double sprintSpeedModifier;
    private LivingEntity fleeTarget;
    private int pathRecalcDelay;

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

    /**
     * Create flee goal.
     *
     * @param mob The MCA villager mob (must be PathfinderMob)
     * @param fleeFromClass Class of entities to flee from
     * @param maxDistance Detection range
     * @param walkSpeed Walking speed multiplier
     * @param sprintSpeed Sprint speed multiplier (when threat is close)
     */
    public MCAFleeGoal(
        PathfinderMob mob,
        Class<? extends LivingEntity> fleeFromClass,
        float maxDistance,
        double walkSpeed,
        double sprintSpeed
    ) {
        this.mob = mob;
        this.fleeFromClass = fleeFromClass;
        this.maxDistance = maxDistance;
        this.walkSpeedModifier = walkSpeed;
        this.sprintSpeedModifier = sprintSpeed;
        this.setFlags(EnumSet.of(Goal.Flag.MOVE));
    }

    @Override
    public boolean canUse() {
        // Only flee if NOT collared (collared villagers obey)
        IBondageState state = MCABondageManager.getInstance().getKidnappedState(
            mob
        );
        if (state == null) return false;
        if (state.hasCollar()) return false;

        // Find nearest threat
        this.fleeTarget = findNearestThreat();
        if (fleeTarget == null) return false;

        // Can we find a path away?
        Vec3 fleePos = DefaultRandomPos.getPosAway(
            mob,
            16, // Range
            7, // Y variance
            fleeTarget.position()
        );

        return fleePos != null;
    }

    /**
     * Find the nearest entity to flee from.
     */
    private LivingEntity findNearestThreat() {
        AABB searchBox = mob.getBoundingBox().inflate(maxDistance);

        List<? extends LivingEntity> threats = mob
            .level()
            .getEntitiesOfClass(
                fleeFromClass,
                searchBox,
                entity ->
                    entity != mob &&
                    entity.isAlive() &&
                    mob.distanceTo(entity) <= maxDistance
            );

        if (threats.isEmpty()) return null;

        // Return closest
        LivingEntity closest = null;
        double closestDist = Double.MAX_VALUE;
        for (LivingEntity entity : threats) {
            double dist = mob.distanceToSqr(entity);
            if (dist < closestDist) {
                closestDist = dist;
                closest = entity;
            }
        }
        return closest;
    }

    @Override
    public boolean canContinueToUse() {
        // Stop if got collared
        IBondageState state = MCABondageManager.getInstance().getKidnappedState(
            mob
        );
        if (state != null && state.hasCollar()) return false;

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

        // Continue as long as threat is within range
        return fleeTarget.distanceToSqr(mob) < (maxDistance * maxDistance);
    }

    @Override
    public void start() {
        pathRecalcDelay = 0;
        navigateAway();
    }

    @Override
    public void stop() {
        this.fleeTarget = null;
        mob.getNavigation().stop();
    }

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

        // Recalculate path periodically
        if (--pathRecalcDelay <= 0) {
            pathRecalcDelay = PATH_RECALC_INTERVAL;

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

            navigateAway();
        }

        // Sprint if threat is very close
        double distSqr = mob.distanceToSqr(fleeTarget);
        double sprintThreshold = 4.0 * 4.0;

        if (distSqr < sprintThreshold) {
            mob.getNavigation().setSpeedModifier(sprintSpeedModifier);
        } else {
            mob.getNavigation().setSpeedModifier(walkSpeedModifier);
        }
    }

    /**
     * Navigate away from the current threat.
     */
    private void navigateAway() {
        Vec3 fleePos = DefaultRandomPos.getPosAway(
            mob,
            16,
            7,
            fleeTarget.position()
        );

        if (fleePos != null) {
            mob
                .getNavigation()
                .moveTo(fleePos.x, fleePos.y, fleePos.z, walkSpeedModifier);
        } else {
            // Fallback: move directly away
            Vec3 awayDir = mob
                .position()
                .subtract(fleeTarget.position())
                .normalize();
            Vec3 fallbackPos = mob.position().add(awayDir.scale(8));
            mob
                .getNavigation()
                .moveTo(
                    fallbackPos.x,
                    mob.getY(),
                    fallbackPos.z,
                    walkSpeedModifier
                );
        }
    }
}
