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

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.EntityLaborGuard;
import java.util.EnumSet;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;

/**
 * AI Goal for EntityLaborGuard to follow their assigned prisoner.
 *
 * Adapted from MasterFollowPlayerGoal.
 * Maintains distance of 3-12 blocks, teleports at 32 blocks.
 */
public class GuardFollowPrisonerGoal extends Goal {

    private final EntityLaborGuard guard;

    private static final double MIN_DISTANCE = 3.0;
    private static final double IDEAL_DISTANCE = 6.0;
    private static final double MAX_DISTANCE = 12.0;
    private static final double TELEPORT_DISTANCE = 32.0;
    private static final double FOLLOW_SPEED = 1.0;
    private static final int PATH_RECALC_COOLDOWN = 10;
    /** Ticks without making progress toward prisoner before force-teleporting */
    private static final int STUCK_TELEPORT_TICKS = 100; // 5 seconds

    private int pathRecalcCooldown = 0;
    private int stuckTicks = 0;
    private BlockPos lastProgressPos = null;

    public GuardFollowPrisonerGoal(EntityLaborGuard guard) {
        this.guard = guard;
        this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
    }

    @Override
    public boolean canUse() {
        if (guard.isTiedUp()) {
            return false;
        }

        ServerPlayer prisoner = guard.getPrisoner();
        if (prisoner == null || !prisoner.isAlive()) {
            return false;
        }

        return true;
    }

    @Override
    public boolean canContinueToUse() {
        ServerPlayer prisoner = guard.getPrisoner();
        if (prisoner == null || !prisoner.isAlive()) {
            return false;
        }

        if (guard.isTiedUp()) {
            return false;
        }

        return true;
    }

    @Override
    public void start() {
        this.pathRecalcCooldown = 0;
        this.stuckTicks = 0;
        this.lastProgressPos = null;

        TiedUpMod.LOGGER.debug(
            "[GuardFollowPrisonerGoal] {} started following prisoner",
            guard.getNpcName()
        );
    }

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

        TiedUpMod.LOGGER.debug(
            "[GuardFollowPrisonerGoal] {} stopped following prisoner",
            guard.getNpcName()
        );
    }

    @Override
    public void tick() {
        ServerPlayer prisoner = guard.getPrisoner();
        if (prisoner == null) return;

        // Check for dimension change
        if (!guard.level().dimension().equals(prisoner.level().dimension())) {
            return;
        }

        // Look at prisoner
        guard.getLookControl().setLookAt(prisoner, 30.0F, 30.0F);

        double distSq = guard.distanceToSqr(prisoner);

        // Teleport if too far
        if (distSq > TELEPORT_DISTANCE * TELEPORT_DISTANCE) {
            teleportNearPrisoner(prisoner);
            this.stuckTicks = 0;
            this.lastProgressPos = null;
            return;
        }

        // Stuck detection: if guard isn't making progress toward prisoner, teleport
        // This catches cases where guard spawns in a cell or behind walls
        if (distSq > IDEAL_DISTANCE * IDEAL_DISTANCE) {
            BlockPos currentPos = guard.blockPosition();
            if (
                lastProgressPos == null ||
                currentPos.distManhattan(lastProgressPos) >= 2
            ) {
                lastProgressPos = currentPos;
                stuckTicks = 0;
            } else {
                stuckTicks++;
            }
            if (stuckTicks >= STUCK_TELEPORT_TICKS) {
                TiedUpMod.LOGGER.info(
                    "[GuardFollowPrisonerGoal] {} stuck for {}s, teleporting to prisoner",
                    guard.getNpcName(),
                    STUCK_TELEPORT_TICKS / 20
                );
                teleportNearPrisoner(prisoner);
                stuckTicks = 0;
                lastProgressPos = null;
                return;
            }
        } else {
            stuckTicks = 0;
        }

        // Update path periodically
        this.pathRecalcCooldown--;
        if (this.pathRecalcCooldown <= 0) {
            this.pathRecalcCooldown = PATH_RECALC_COOLDOWN;

            if (distSq < MIN_DISTANCE * MIN_DISTANCE) {
                // Too close - stop
                guard.getNavigation().stop();
            } else if (distSq > IDEAL_DISTANCE * IDEAL_DISTANCE) {
                // Beyond ideal distance - follow
                guard.getNavigation().moveTo(prisoner, FOLLOW_SPEED);
            } else if (!guard.getNavigation().isInProgress()) {
                // At ideal distance and idle - wander near prisoner
                wanderNearPrisoner(prisoner);
            }
        }
    }

    private void wanderNearPrisoner(ServerPlayer prisoner) {
        double angle = guard.getRandom().nextDouble() * Math.PI * 2;
        double distance =
            IDEAL_DISTANCE + (guard.getRandom().nextDouble() - 0.5) * 2;

        double x = prisoner.getX() + Math.cos(angle) * distance;
        double z = prisoner.getZ() + Math.sin(angle) * distance;

        int groundY = findSafeGroundY(
            prisoner.level(),
            (int) x,
            (int) z,
            (int) prisoner.getY() + 10
        );
        double y = groundY;

        guard.getNavigation().moveTo(x, y, z, FOLLOW_SPEED * 0.6);
    }

    private void teleportNearPrisoner(ServerPlayer prisoner) {
        double angle = guard.getRandom().nextDouble() * Math.PI * 2;
        double distance = MIN_DISTANCE + guard.getRandom().nextDouble() * 2;

        double x = prisoner.getX() + Math.cos(angle) * distance;
        double z = prisoner.getZ() + Math.sin(angle) * distance;

        int groundY = findSafeGroundY(
            prisoner.level(),
            (int) x,
            (int) z,
            (int) prisoner.getY() + 10
        );
        double y = groundY;

        guard.teleportTo(x, y, z);

        TiedUpMod.LOGGER.debug(
            "[GuardFollowPrisonerGoal] {} teleported near prisoner",
            guard.getNpcName()
        );
    }

    private int findSafeGroundY(Level level, int x, int z, int startY) {
        BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(
            x,
            startY,
            z
        );

        for (int y = startY; y > level.getMinBuildHeight(); y--) {
            pos.setY(y);
            BlockState below = level.getBlockState(pos.below());
            BlockState at = level.getBlockState(pos);
            BlockState above = level.getBlockState(pos.above());

            if (below.isSolid() && !at.isSolid() && !above.isSolid()) {
                return y;
            }
        }
        return startY;
    }
}
