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

import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.dialogue.EntityDialogueManager;
import com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory;
import com.tiedup.remake.entities.EntityKidnapper;
import com.tiedup.remake.prison.PrisonerManager;
import com.tiedup.remake.prison.PrisonerRecord;
import com.tiedup.remake.state.IRestrainable;
import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.util.tasks.ItemTask;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.EnumSet;
import java.util.List;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;

/**
 * AI Goal for EntityKidnapper to wait for a worker to complete a job.
 *
 *
 * This goal activates when:
 * - Kidnapper has assigned a job (isWaitingForJobToBeCompleted)
 * - Worker is tracked by UUID (freed from slavery, wearing shock collar)
 *
 * Behavior:
 * 1. Wait for worker to bring the required items
 * 2. If timeout, shock worker (via collar)
 * 3. After multiple failures, give up and flee
 *
 * Based on original EntityAIWaitForJob from 1.12.2
 */
public class KidnapperWaitForJobGoal extends Goal {

    private final EntityKidnapper kidnapper;

    /** Minimum wait time for job completion (ticks) - 3 minutes */
    private static final int MIN_JOB_TIME = 3600;

    /** Maximum wait time for job completion (ticks) - 5 minutes */
    private static final int MAX_JOB_TIME = 6000;

    /** Time between reminder announcements (ticks) - 30 seconds */
    private static final int REMINDER_INTERVAL = 600;

    /** Current wait timer */
    private int waitTimer;

    /** Total wait duration for this job */
    private int waitDuration;

    /** Timer for reminders */
    private int reminderTimer;

    /** Current job task */
    private ItemTask currentJob;

    /** Number of failed attempts */
    private int failCount;

    /** Maximum failures before giving up */
    private static final int MAX_FAILURES = 3;

    /**
     * Create a new wait for job goal.
     *
     * @param kidnapper The kidnapper entity
     */
    public KidnapperWaitForJobGoal(EntityKidnapper kidnapper) {
        this.kidnapper = kidnapper;
        this.waitTimer = 0;
        this.waitDuration = 0;
        this.reminderTimer = 0;
        this.currentJob = null;
        this.failCount = 0;
        // Only block MOVE, allow LOOK so kidnapper can look around
        this.setFlags(EnumSet.of(Goal.Flag.MOVE));
    }

    @Override
    public boolean canUse() {
        // Must be waiting for job (has job and worker UUID)
        if (!this.kidnapper.isWaitingForJobToBeCompleted()) {
            return false;
        }

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

        // Must not be in get out state
        if (this.kidnapper.isGetOutState()) {
            return false;
        }

        // Must not be selling (shouldn't happen with job, but safety check)
        if (this.kidnapper.isSellingCaptive()) {
            return false;
        }

        return true;
    }

    @Override
    public boolean canContinueToUse() {
        // Stop if no longer waiting for job
        if (!this.kidnapper.isWaitingForJobToBeCompleted()) {
            return false;
        }

        // Stop if tied up
        if (this.kidnapper.isTiedUp()) {
            return false;
        }

        // Stop if in get out state
        if (this.kidnapper.isGetOutState()) {
            return false;
        }

        return true;
    }

    @Override
    public void start() {
        this.waitTimer = 0;
        this.waitDuration =
            MIN_JOB_TIME +
            this.kidnapper.getRandom().nextInt(MAX_JOB_TIME - MIN_JOB_TIME);
        this.reminderTimer = 0;
        this.currentJob = this.kidnapper.getCurrentJob();

        // Stop moving
        this.kidnapper.getNavigation().stop();

        // Announce job to worker
        announceJob();

        TiedUpMod.LOGGER.debug(
            "[KidnapperWaitForJobGoal] {} waiting for job completion ({}s timeout)",
            this.kidnapper.getNpcName(),
            this.waitDuration / 20
        );
    }

    @Override
    public void stop() {
        this.waitTimer = 0;
        this.currentJob = null;
    }

    @Override
    public void tick() {
        this.waitTimer++;
        this.reminderTimer++;

        // Look at nearby players (or specifically the job worker)
        Player worker = this.kidnapper.getJobWorker();

        // Check if worker died or lost their collar (respawned = no collar = job abandoned)
        if (worker != null && !workerHasJobCollar(worker)) {
            TiedUpMod.LOGGER.debug(
                "[KidnapperWaitForJobGoal] {} - worker {} no longer has collar (died/escaped?), abandoning job",
                this.kidnapper.getNpcName(),
                worker.getName().getString()
            );
            jobAbandoned();
            return;
        }

        if (worker != null && this.kidnapper.distanceTo(worker) <= 8.0) {
            this.kidnapper.getLookControl().setLookAt(worker, 30.0F, 30.0F);
        } else {
            // Look at any nearby player
            List<Player> nearbyPlayers =
                this.kidnapper.level().getEntitiesOfClass(
                    Player.class,
                    this.kidnapper.getBoundingBox().inflate(8.0)
                );
            if (!nearbyPlayers.isEmpty()) {
                this.kidnapper.getLookControl().setLookAt(
                    nearbyPlayers.get(0),
                    30.0F,
                    30.0F
                );
            }
        }

        // Periodic reminder
        if (this.reminderTimer >= REMINDER_INTERVAL) {
            remindWorker();
            this.reminderTimer = 0;
        }

        // Check if worker completed job
        if (checkJobCompleted()) {
            jobCompleted();
            return;
        }

        // Check timeout
        if (this.waitTimer >= this.waitDuration) {
            jobFailed();
        }
    }

    /**
     * Check if the worker still has their job collar.
     * If they died and respawned, they won't have it anymore.
     *
     * @param worker The worker to check
     * @return true if worker still has a shock collar
     */
    private boolean workerHasJobCollar(Player worker) {
        IRestrainable state = KidnappedHelper.getKidnappedState(worker);
        if (state == null) {
            return false;
        }
        return state.hasCollar();
    }

    /**
     * Handle job abandonment (worker died/escaped without completing).
     * Clears job state without punishment since worker is gone.
     */
    private void jobAbandoned() {
        TiedUpMod.LOGGER.debug(
            "[KidnapperWaitForJobGoal] {} - job abandoned, clearing state",
            this.kidnapper.getNpcName()
        );

        this.kidnapper.clearCurrentJob();
        this.kidnapper.setGetOutState(true);
        this.failCount = 0;
    }

    /**
     * Announce the job to the worker.
     */
    private void announceJob() {
        Player worker = this.kidnapper.getJobWorker();
        if (worker == null || this.currentJob == null) return;

        // Talk to worker about the job (with item name)
        String jobDialogue = EntityDialogueManager.getJobAssignmentDialogue(
            this.currentJob.toDisplayString()
        );
        this.kidnapper.talkTo(worker, jobDialogue);

        // System message with item
        SystemMessageManager.sendJobAssigned(
            worker,
            this.currentJob.toDisplayString()
        );

        TiedUpMod.LOGGER.debug(
            "[KidnapperWaitForJobGoal] Job assigned: {} must fetch {}",
            worker.getName().getString(),
            this.currentJob.toDisplayString()
        );
    }

    /**
     * Remind worker about the job.
     */
    private void remindWorker() {
        Player worker = this.kidnapper.getJobWorker();
        if (worker == null) return;

        // Talk to worker about hurrying up
        this.kidnapper.talkTo(worker, DialogueCategory.JOB_HURRY);
    }

    /**
     * Check if the worker has completed the job.
     * Worker must be holding the required items and be nearby.
     *
     * @return true if job is complete
     */
    private boolean checkJobCompleted() {
        Player worker = this.kidnapper.getJobWorker();
        if (worker == null || this.currentJob == null) return false;

        // Only check if worker is nearby (5 blocks instead of 3 for more forgiving detection)
        if (this.kidnapper.distanceTo(worker) > 5.0f) {
            return false;
        }

        // Check main hand first
        ItemStack mainHand = worker.getMainHandItem();
        if (this.currentJob.isCompletedBy(mainHand)) {
            return true;
        }

        // Check off hand
        ItemStack offHand = worker.getOffhandItem();
        if (this.currentJob.isCompletedBy(offHand)) {
            return true;
        }

        // Check inventory (player doesn't need to hold the item)
        for (int i = 0; i < worker.getInventory().getContainerSize(); i++) {
            ItemStack stack = worker.getInventory().getItem(i);
            if (this.currentJob.isCompletedBy(stack)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Handle job completion - take items, remove collar, free worker.
     */
    private void jobCompleted() {
        Player worker = this.kidnapper.getJobWorker();
        if (worker == null || this.currentJob == null) return;

        // Consume items from worker (check hands first, then inventory)
        ItemStack mainHand = worker.getMainHandItem();
        if (this.currentJob.isCompletedBy(mainHand)) {
            this.currentJob.consumeFrom(mainHand);
        } else {
            ItemStack offHand = worker.getOffhandItem();
            if (this.currentJob.isCompletedBy(offHand)) {
                this.currentJob.consumeFrom(offHand);
            } else {
                // Check inventory for the items
                for (
                    int i = 0;
                    i < worker.getInventory().getContainerSize();
                    i++
                ) {
                    ItemStack stack = worker.getInventory().getItem(i);
                    if (this.currentJob.isCompletedBy(stack)) {
                        this.currentJob.consumeFrom(stack);
                        break;
                    }
                }
            }
        }

        // Reward dialogue
        this.kidnapper.talkTo(worker, DialogueCategory.JOB_COMPLETE);

        // System message
        SystemMessageManager.sendToPlayer(
            worker,
            MessageCategory.SLAVE_JOB_COMPLETE
        );

        // Remove the shock collar from worker
        IRestrainable workerState = KidnappedHelper.getKidnappedState(worker);
        if (workerState != null) {
            // Unregister from CollarRegistry before removal
            unregisterJobCollar(worker);

            // Force remove the locked collar
            workerState.forceUnequip(BodyRegionV2.NECK);
            TiedUpMod.LOGGER.debug(
                "[KidnapperWaitForJobGoal] Removed collar from {}",
                worker.getName().getString()
            );
        }

        // Release from central PrisonerManager — no grace period for personal tasks
        if (
            this.kidnapper.level() instanceof
                net.minecraft.server.level.ServerLevel serverLevel
        ) {
            PrisonerManager manager = PrisonerManager.get(serverLevel);
            manager.release(worker.getUUID(), serverLevel.getGameTime(), 0);
        }

        TiedUpMod.LOGGER.debug(
            "[KidnapperWaitForJobGoal] {} completed job",
            worker.getName().getString()
        );

        // Clear job and enter flee state
        this.kidnapper.clearCurrentJob();
        this.kidnapper.setGetOutState(true);
        this.failCount = 0;
    }

    /**
     * Handle job failure - shock worker, warn, then kill on final failure.
     */
    private void jobFailed() {
        Player worker = this.kidnapper.getJobWorker();

        this.failCount++;

        String workerName =
            worker != null ? worker.getName().getString() : "worker";
        TiedUpMod.LOGGER.debug(
            "[KidnapperWaitForJobGoal] {} failed job (attempt {}/{})",
            workerName,
            this.failCount,
            MAX_FAILURES
        );

        if (worker == null) {
            // Worker disconnected - clean up and give up
            // NOTE: We can't remove the collar directly since the player is offline,
            // but we unregister it from CollarRegistry so it won't be tracked anymore
            TiedUpMod.LOGGER.debug(
                "[KidnapperWaitForJobGoal] Worker disconnected, clearing job and unregistering collar"
            );
            this.kidnapper.clearCurrentJob();
            this.kidnapper.setGetOutState(true);
            return;
        }

        IRestrainable workerState = KidnappedHelper.getKidnappedState(worker);

        if (this.failCount >= MAX_FAILURES) {
            // Final failure - kill the worker
            TiedUpMod.LOGGER.debug(
                "[KidnapperWaitForJobGoal] {} killed {} after {} failures",
                this.kidnapper.getNpcName(),
                workerName,
                this.failCount
            );

            // Dialogue: killing
            this.kidnapper.talkTo(worker, DialogueCategory.JOB_KILL);

            // System message
            SystemMessageManager.sendToPlayer(
                worker,
                MessageCategory.SLAVE_JOB_KILLED
            );

            // Unregister collar from CollarRegistry before killing
            unregisterJobCollar(worker);

            // Remove collar before killing
            if (workerState != null) {
                workerState.forceUnequip(BodyRegionV2.NECK);
            }

            // Kill the worker
            worker.hurt(
                worker.damageSources().mobAttack(this.kidnapper),
                Float.MAX_VALUE
            );

            this.kidnapper.clearCurrentJob();
            this.kidnapper.setGetOutState(true);
        } else if (this.failCount == MAX_FAILURES - 1) {
            // Second to last attempt - warn about death
            TiedUpMod.LOGGER.debug(
                "[KidnapperWaitForJobGoal] {} warning {} - next failure is death",
                this.kidnapper.getNpcName(),
                workerName
            );

            // Shock as punishment
            if (workerState != null) {
                workerState.shockKidnapped("Job failure", 3.0f); // Stronger shock
            }

            // Dialogue: death warning
            this.kidnapper.talkTo(worker, DialogueCategory.JOB_LAST_CHANCE);

            // System message
            SystemMessageManager.sendToPlayer(
                worker,
                MessageCategory.SLAVE_JOB_LAST_CHANCE
            );

            // Reset timer for final attempt
            this.waitTimer = 0;
            this.waitDuration =
                MIN_JOB_TIME +
                this.kidnapper.getRandom().nextInt(MAX_JOB_TIME - MIN_JOB_TIME);
            this.reminderTimer = 0;
        } else {
            // Normal failure - shock and remind
            if (workerState != null) {
                workerState.shockKidnapped("Job failure", 2.0f);
            }

            // Dialogue: failed
            this.kidnapper.talkTo(worker, DialogueCategory.JOB_FAILED);

            // System message
            SystemMessageManager.sendToPlayer(
                worker,
                MessageCategory.SLAVE_JOB_FAILED
            );

            // Reset timer for another attempt
            this.waitTimer = 0;
            this.waitDuration =
                MIN_JOB_TIME +
                this.kidnapper.getRandom().nextInt(MAX_JOB_TIME - MIN_JOB_TIME);
            this.reminderTimer = 0;

            // Remind about the job
            remindWorker();
        }
    }

    /**
     * Unregister a job collar from the CollarRegistry.
     * Called when the job is completed or worker is killed.
     *
     * @param worker The worker whose collar is being removed
     */
    private void unregisterJobCollar(Player worker) {
        if (worker == null || this.kidnapper.level().isClientSide()) {
            return;
        }

        if (
            !(this.kidnapper.level() instanceof
                    net.minecraft.server.level.ServerLevel serverLevel)
        ) {
            return;
        }

        com.tiedup.remake.state.CollarRegistry registry =
            com.tiedup.remake.state.CollarRegistry.get(serverLevel);
        if (registry == null) {
            return;
        }

        // Unregister the worker's collar
        registry.unregisterWearer(worker.getUUID());

        TiedUpMod.LOGGER.debug(
            "[KidnapperWaitForJobGoal] Unregistered job collar for {}",
            worker.getName().getString()
        );
    }
}
