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

import com.tiedup.remake.entities.EntityMaid;
import com.tiedup.remake.entities.EntitySlaveTrader;
import java.util.EnumSet;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.ai.goal.Goal;

/**
 * AI Goal for EntityMaid to follow their master trader.
 *
 * Behavior:
 * - Active when state is IDLE (or DEFENDING)
 * - Maintains close distance to trader
 * - Wanders around trader when he is stationary (instead of letting random stroll goal take over)
 * - Uses smooth movement to avoid jittering
 */
public class MaidFollowTraderGoal extends Goal {

    private final EntityMaid maid;

    /** Distance at which to start following */
    private static final double START_FOLLOW_DISTANCE = 10.0;

    /** Distance to maintain from trader (stop following when reached) */
    private static final double STOP_FOLLOW_DISTANCE = 4.0;

    /** Maximum distance to wander from trader when idle */
    private static final double MAX_WANDER_DISTANCE = 7.0;

    /** Minimum distance for wander points */
    private static final double MIN_WANDER_DISTANCE = 3.0;

    /** Ticks between path recalculations when following */
    private int pathRecalcCooldown = 0;

    /** Ticks trader has been stationary */
    private int traderStationaryTicks = 0;

    /** Last known trader position for stationary detection */
    private BlockPos lastTraderPos = null;

    /** Ticks before considering trader stationary (3 seconds) */
    private static final int STATIONARY_THRESHOLD = 60;

    /** Current wander target */
    private BlockPos wanderTarget = null;

    /** Ticks until next wander target (prevents constant recalculation) */
    private int wanderCooldown = 0;

    /** Cooldown after goal stops before it can restart (prevents jitter) */
    private int restartCooldown = 0;

    /** Random for wandering */
    private final java.util.Random random = new java.util.Random();

    public MaidFollowTraderGoal(EntityMaid maid) {
        this.maid = maid;
        this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
    }

    @Override
    public boolean canUse() {
        // Restart cooldown to prevent jitter
        if (restartCooldown > 0) {
            restartCooldown--;
            return false;
        }

        // Must have a master trader
        if (maid.getMasterTraderUUID() == null) {
            return false;
        }

        // Must be in a state that allows following
        if (!maid.getMaidState().shouldFollowTrader()) {
            return false;
        }

        // Must not be freed
        if (maid.isFreed()) {
            return false;
        }

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

        // Trader must exist and be loaded
        EntitySlaveTrader trader = maid.getMasterTrader();
        if (trader == null || !trader.isAlive()) {
            return false;
        }

        // Activate when too far from trader
        double distSq = maid.distanceToSqr(trader);
        return distSq > START_FOLLOW_DISTANCE * START_FOLLOW_DISTANCE;
    }

    @Override
    public boolean canContinueToUse() {
        if (maid.getMasterTraderUUID() == null) {
            return false;
        }

        if (!maid.getMaidState().shouldFollowTrader()) {
            return false;
        }

        if (maid.isFreed() || maid.isTiedUp()) {
            return false;
        }

        EntitySlaveTrader trader = maid.getMasterTrader();
        if (trader == null || !trader.isAlive()) {
            return false;
        }

        // Keep running while within reasonable distance
        // This prevents the goal from stopping and letting random stroll take over
        double distSq = maid.distanceToSqr(trader);
        return distSq <= START_FOLLOW_DISTANCE * START_FOLLOW_DISTANCE * 4; // 20 blocks
    }

    @Override
    public void start() {
        pathRecalcCooldown = 0;
        wanderTarget = null;
        wanderCooldown = 0;
        traderStationaryTicks = 0;
    }

    @Override
    public void stop() {
        maid.getNavigation().stop();
        wanderTarget = null;
        // Set restart cooldown to prevent immediate re-activation (jitter)
        restartCooldown = 40; // 2 seconds
    }

    @Override
    public void tick() {
        EntitySlaveTrader trader = maid.getMasterTrader();
        if (trader == null) {
            return;
        }

        double distSq = maid.distanceToSqr(trader);
        double distance = Math.sqrt(distSq);

        // Check if trader is stationary
        BlockPos traderPos = trader.blockPosition();
        if (lastTraderPos != null && lastTraderPos.equals(traderPos)) {
            traderStationaryTicks++;
        } else {
            traderStationaryTicks = 0;
            lastTraderPos = traderPos;
            // Trader moved - clear wander target
            wanderTarget = null;
        }

        // Decrement cooldowns
        if (pathRecalcCooldown > 0) pathRecalcCooldown--;
        if (wanderCooldown > 0) wanderCooldown--;

        // Determine behavior based on distance and trader state
        if (distance > START_FOLLOW_DISTANCE) {
            // Too far - actively follow
            if (pathRecalcCooldown <= 0) {
                pathRecalcCooldown = 20;
                double speed = distance > 15.0 ? 1.0 : 0.8;
                maid.getNavigation().moveTo(trader, speed);
            }
            // Look at trader while following
            maid.getLookControl().setLookAt(trader, 30.0F, 30.0F);
        } else if (distance > STOP_FOLLOW_DISTANCE) {
            // Medium distance - slow approach
            if (pathRecalcCooldown <= 0) {
                pathRecalcCooldown = 40; // Slower recalc
                maid.getNavigation().moveTo(trader, 0.5);
            }
        } else if (traderStationaryTicks >= STATIONARY_THRESHOLD) {
            // Close to trader and trader is stationary - wander around
            tickWander(trader);
        } else {
            // Close to trader but trader is moving - stop and wait
            if (maid.getNavigation().isInProgress()) {
                maid.getNavigation().stop();
            }
            // Look at trader occasionally
            if (random.nextInt(40) == 0) {
                maid.getLookControl().setLookAt(trader, 10.0F, 40.0F);
            }
        }
    }

    /**
     * Handle wandering behavior when close to stationary trader.
     */
    private void tickWander(EntitySlaveTrader trader) {
        // Check if we need a new wander target
        if (
            wanderTarget == null || wanderCooldown <= 0 || reachedWanderTarget()
        ) {
            selectNewWanderTarget(trader);
            wanderCooldown = 100 + random.nextInt(100); // 5-10 seconds
        }

        // Move towards wander target
        if (wanderTarget != null && !maid.getNavigation().isInProgress()) {
            maid
                .getNavigation()
                .moveTo(
                    wanderTarget.getX() + 0.5,
                    wanderTarget.getY(),
                    wanderTarget.getZ() + 0.5,
                    0.5 // Slow wander speed
                );
        }

        // Look around occasionally (not always at trader)
        if (random.nextInt(80) == 0) {
            if (random.nextBoolean()) {
                maid.getLookControl().setLookAt(trader, 10.0F, 40.0F);
            } else {
                // Look in random direction
                double angle = random.nextDouble() * Math.PI * 2;
                maid
                    .getLookControl()
                    .setLookAt(
                        maid.getX() + Math.cos(angle) * 5,
                        maid.getEyeY(),
                        maid.getZ() + Math.sin(angle) * 5,
                        10.0F,
                        40.0F
                    );
            }
        }
    }

    /**
     * Check if maid reached current wander target.
     */
    private boolean reachedWanderTarget() {
        if (wanderTarget == null) return true;
        double distSq = maid.distanceToSqr(
            wanderTarget.getX() + 0.5,
            wanderTarget.getY(),
            wanderTarget.getZ() + 0.5
        );
        return distSq < 4.0; // Within 2 blocks
    }

    /**
     * Select a new wander target around the trader.
     */
    private void selectNewWanderTarget(EntitySlaveTrader trader) {
        // Pick a random point within wander range of trader
        double angle = random.nextDouble() * 2 * Math.PI;
        double radius =
            MIN_WANDER_DISTANCE +
            random.nextDouble() * (MAX_WANDER_DISTANCE - MIN_WANDER_DISTANCE);

        int targetX = (int) (trader.getX() + Math.cos(angle) * radius);
        int targetZ = (int) (trader.getZ() + Math.sin(angle) * radius);
        int targetY = (int) trader.getY();

        wanderTarget = new BlockPos(targetX, targetY, targetZ);
    }

    @Override
    public boolean requiresUpdateEveryTick() {
        return false; // Don't need every tick, default interval is fine
    }
}
