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

import com.tiedup.remake.cells.CampOwnership;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.EntityMaid;
import com.tiedup.remake.labor.LaborTask;
import com.tiedup.remake.prison.LaborRecord;
import com.tiedup.remake.prison.PrisonerManager;
import com.tiedup.remake.prison.PrisonerRecord;
import com.tiedup.remake.prison.PrisonerState;
import com.tiedup.remake.prison.RansomRecord;
import com.tiedup.remake.state.IRestrainable;
import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.util.MessageDispatcher;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;

/**
 * Manages all prison-related interactions for an EntityMaid.
 *
 * Responsibilities:
 * - Manual prisoner check-in (task completion)
 * - Manual prisoner extraction (stuck PENDING state)
 * - Labor status display
 * - Prisoner record lookups
 * - Struggle detection and punishment
 *
 * This is a plain Java component (not a Forge capability).
 * Lifecycle is owned by EntityMaid.
 */
public class MaidPrisonInteraction {

    // CONSTANTS

    /** Maximum distance to HEAR struggle (through bars, no line of sight needed) */
    private static final double STRUGGLE_HEARING_RANGE = 6.0;

    /** Maximum distance to SEE struggle (requires line of sight) */
    private static final double STRUGGLE_VISION_RANGE = 15.0;

    // STATE

    private final EntityMaid maid;

    /** Target prisoner to approach after catching them struggling */
    @Nullable
    private ServerPlayer strugglePunishmentTarget;

    // CONSTRUCTOR

    public MaidPrisonInteraction(EntityMaid maid) {
        this.maid = maid;
    }

    // PLAYER INTERACTION DISPATCH

    /**
     * Handle prisoner-specific interactions when a player right-clicks the maid.
     * Called from EntityMaid.mobInteract() after verifying the player is a prisoner of this camp.
     *
     * @param serverPlayer The interacting player (server-side)
     * @param manager The PrisonerManager instance
     * @param record The player's prisoner record
     * @param laborRecord The player's labor record (may be null)
     * @return InteractionResult.SUCCESS if handled, null if not a prison interaction
     */
    @Nullable
    public InteractionResult handlePrisonerInteract(
        ServerPlayer serverPlayer,
        PrisonerManager manager,
        PrisonerRecord record,
        @Nullable LaborRecord laborRecord
    ) {
        LaborTask task = laborRecord != null ? laborRecord.getTask() : null;
        PrisonerState state = record.getState();

        // CASE 1: Stuck in IMPRISONED with pending task (maid failed to extract)
        if (
            state == PrisonerState.IMPRISONED &&
            laborRecord != null &&
            laborRecord.getPhase() ==
            LaborRecord.WorkPhase.PENDING_EXTRACTION &&
            serverPlayer.isShiftKeyDown()
        ) {
            handleManualExtraction(serverPlayer, manager, laborRecord);
            return InteractionResult.SUCCESS;
        }

        // CASE 2: Manual turn-in (task complete)
        if (
            state == PrisonerState.WORKING &&
            task != null &&
            serverPlayer.isShiftKeyDown()
        ) {
            // Check current progress first
            if (maid.level() instanceof ServerLevel serverLevel) {
                task.checkProgress(serverPlayer, serverLevel);
            }

            if (task.isComplete()) {
                handleManualCheckIn(serverPlayer, manager, laborRecord);
                return InteractionResult.SUCCESS;
            }
        }

        // CASE 3: Show status (normal click, not shift)
        if (!serverPlayer.isShiftKeyDown()) {
            showLaborStatus(serverPlayer, record, laborRecord);
            return InteractionResult.SUCCESS;
        }

        return null; // Not handled
    }

    // MANUAL EXTRACTION

    /**
     * Manually extract prisoner from stuck PENDING state.
     */
    private void handleManualExtraction(
        ServerPlayer player,
        PrisonerManager manager,
        LaborRecord laborRecord
    ) {
        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return;
        }

        IRestrainable cap = KidnappedHelper.getKidnappedState(player);
        if (cap == null) return;

        LaborTask task = laborRecord.getTask();
        if (task == null) return;

        // Save bondage state to laborRecord and remove restraints for labor
        CompoundTag bondageSnapshot =
            com.tiedup.remake.prison.service.BondageService.get().saveSnapshot(
                cap
            );
        laborRecord.setBondageSnapshot(bondageSnapshot);
        manager.setLaborRecord(player.getUUID(), laborRecord);
        com.tiedup.remake.prison.service.BondageService.get().removeForLabor(
            cap
        );

        task.giveEquipment(player);

        // Transition to WORKING state
        manager.transitionToWorking(
            player.getUUID(),
            serverLevel.getGameTime()
        );

        sayToPlayer(player, "Very well. Here are your tools.");
        player.sendSystemMessage(
            Component.translatable(
                "entity.tiedup.maid.manual_assign", task.getDescription()
            ).withStyle(ChatFormatting.YELLOW)
        );

        TiedUpMod.LOGGER.info(
            "[EntityMaid] Manual extraction: {} -> WORKING",
            player.getName().getString()
        );
    }

    // MANUAL CHECK-IN

    /**
     * Handle manual check-in when a working player completes their task early.
     * NOTE: Does NOT add payment - MaidReturnGoal.collectAndStartEscort() handles payment.
     * This just signals task completion and sets the phase for pickup.
     */
    private void handleManualCheckIn(
        ServerPlayer worker,
        PrisonerManager manager,
        LaborRecord laborRecord
    ) {
        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return;
        }

        LaborTask task = laborRecord.getTask();
        if (task == null) {
            return;
        }

        // Signal that task is complete - maid will pick up, collect items, and process payment
        laborRecord.setPhase(
            LaborRecord.WorkPhase.PENDING_RETURN,
            serverLevel.getGameTime()
        );
        manager.setLaborRecord(worker.getUUID(), laborRecord);

        sayToPlayer(worker, "Good. You've completed your task.");
        worker.sendSystemMessage(
            Component.translatable(
                "entity.tiedup.maid.collect_shortly"
            ).withStyle(ChatFormatting.YELLOW)
        );

        TiedUpMod.LOGGER.info(
            "[EntityMaid] {} manual check-in: {} task complete, awaiting pickup",
            maid.getNpcName(),
            worker.getName().getString()
        );
    }

    // STATUS DISPLAY

    /**
     * Show labor status to prisoner.
     */
    private void showLaborStatus(
        ServerPlayer player,
        PrisonerRecord record,
        @Nullable LaborRecord laborRecord
    ) {
        player.sendSystemMessage(
            Component.translatable("entity.tiedup.maid.labor_status_header").withStyle(
                ChatFormatting.GOLD
            )
        );

        player.sendSystemMessage(
            Component.translatable("entity.tiedup.maid.labor_status_state", record.getState().name()).withStyle(
                ChatFormatting.GRAY
            )
        );

        if (laborRecord != null && laborRecord.getTask() != null) {
            LaborTask task = laborRecord.getTask();
            player.sendSystemMessage(
                Component.translatable("entity.tiedup.maid.labor_status_task", task.getDescription()).withStyle(
                    ChatFormatting.GRAY
                )
            );
            player.sendSystemMessage(
                Component.translatable(
                    "entity.tiedup.maid.labor_status_progress", task.getProgress(), task.getQuota()
                ).withStyle(ChatFormatting.GRAY)
            );
        }

        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return;
        }

        PrisonerManager manager = PrisonerManager.get(serverLevel);
        RansomRecord ransom = manager.getRansomRecord(player.getUUID());
        int remaining = ransom != null ? ransom.getRemainingDebt() : 0;
        player.sendSystemMessage(
            Component.translatable(
                "entity.tiedup.maid.labor_status_debt", remaining
            ).withStyle(ChatFormatting.GRAY)
        );

        // Show action hints
        if (
            laborRecord != null &&
            laborRecord.getPhase() == LaborRecord.WorkPhase.PENDING_EXTRACTION
        ) {
            player.sendSystemMessage(
                Component.translatable(
                    "entity.tiedup.maid.labor_hint_start_task"
                ).withStyle(ChatFormatting.YELLOW)
            );
        } else if (
            laborRecord != null &&
            laborRecord.getTask() != null &&
            laborRecord.getTask().isComplete()
        ) {
            player.sendSystemMessage(
                Component.translatable(
                    "entity.tiedup.maid.labor_hint_turn_in"
                ).withStyle(ChatFormatting.GREEN)
            );
        }
    }

    // PRISONER RECORD ACCESS

    /**
     * Get the prisoner records for prisoners in this maid's camp.
     * Returns a map of UUID -> PrisonerRecord for all prisoners managed by this camp.
     */
    public Map<UUID, PrisonerRecord> getPrisonerRecords() {
        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return Collections.emptyMap();
        }
        UUID campId = maid.getCampUUID();
        if (campId == null) {
            return Collections.emptyMap();
        }

        PrisonerManager manager = PrisonerManager.get(serverLevel);
        Map<UUID, PrisonerRecord> result = new HashMap<>();

        // Find all prisoners in this camp
        for (UUID prisonerId : manager.getPrisonersInCamp(campId)) {
            PrisonerRecord record = manager.getPrisoner(prisonerId);
            if (record != null) {
                result.put(prisonerId, record);
            }
        }

        return result;
    }

    /**
     * Get the processed prisoners set.
     * Delegates to CampOwnership for persistence.
     */
    public Set<UUID> getProcessedPrisoners() {
        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return Collections.emptySet();
        }
        return CampOwnership.get(serverLevel).getProcessedPrisoners();
    }

    /**
     * Get the CampOwnership registry for direct access.
     * Returns null on client side.
     */
    @Nullable
    public CampOwnership getCampOwnership() {
        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return null;
        }
        return CampOwnership.get(serverLevel);
    }

    /**
     * Get the PrisonerManager for direct access.
     * Returns null on client side.
     */
    @Nullable
    public PrisonerManager getPrisonerManager() {
        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return null;
        }
        return PrisonerManager.get(serverLevel);
    }

    // STRUGGLE DETECTION

    /**
     * Called when a prisoner is detected struggling nearby.
     * Uses HYBRID detection: HEARING (close range) + VISION (far range).
     *
     * Detection modes:
     * - Within 6 blocks: Can HEAR through bars/fences (no line of sight needed)
     * - Within 15 blocks: Can SEE if line of sight is clear
     *
     * If detected:
     * 1. Shock the prisoner (punishment)
     * 2. Approach to tighten their binds (reset resistance)
     *
     * @param prisoner The player who is struggling
     */
    public void onStruggleDetected(ServerPlayer prisoner) {
        // Don't react if busy, tied, or freed
        if (maid.getMaidState().isBusy() || maid.isTiedUp() || maid.isFreed()) {
            return;
        }

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

        // HYBRID DETECTION: Hearing (close) + Vision (far)
        double distance = maid.distanceTo(prisoner);

        // HEARING: Close range - can hear through bars/fences (no LOS needed)
        boolean canHear = distance <= STRUGGLE_HEARING_RANGE;

        // VISION: Longer range - requires clear line of sight
        boolean canSee =
            distance <= STRUGGLE_VISION_RANGE &&
            maid.getSensing().hasLineOfSight(prisoner);

        if (!canHear && !canSee) {
            return; // Can't detect the struggle
        }

        // Check if this player is a prisoner of our camp
        UUID campId = maid.getCampUUID();
        if (campId == null) {
            return;
        }

        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return;
        }

        PrisonerManager manager = PrisonerManager.get(serverLevel);
        PrisonerRecord record = manager.getPrisoner(prisoner.getUUID());
        if (record == null || !campId.equals(record.getCampId())) {
            return; // Not our prisoner
        }

        IRestrainable state = KidnappedHelper.getKidnappedState(prisoner);
        if (state == null || !state.isTiedUp()) {
            return;
        }

        String detectionMethod = canHear ? "heard" : "saw";
        TiedUpMod.LOGGER.info(
            "[EntityMaid] {} {} {} struggling! Punishing... (distance: {})",
            maid.getNpcName(),
            detectionMethod,
            prisoner.getName().getString(),
            distance
        );

        // PUNISHMENT: Shock the prisoner
        state.shockKidnapped(" Stop struggling!", 2.0f);

        // TIGHTEN BINDS: Reset resistance to maximum
        tightenBinds(state, prisoner);

        // Look at the prisoner menacingly
        maid.getLookControl().setLookAt(prisoner, 30.0f, 30.0f);

        // Set as target to approach
        this.strugglePunishmentTarget = prisoner;
    }

    /**
     * Tighten the prisoner's binds by resetting their resistance to maximum.
     * This happens when a guard catches someone struggling.
     *
     * @param state The prisoner's kidnapped state
     * @param prisoner The prisoner entity
     */
    private void tightenBinds(IRestrainable state, LivingEntity prisoner) {
        com.tiedup.remake.util.RestraintApplicator.tightenBind(state, prisoner);
    }

    /**
     * Get the current struggle punishment target.
     * @return The prisoner to approach, or null if none
     */
    @Nullable
    public ServerPlayer getStrugglePunishmentTarget() {
        return this.strugglePunishmentTarget;
    }

    /**
     * Clear the struggle punishment target (after approaching or timeout).
     */
    public void clearStrugglePunishmentTarget() {
        this.strugglePunishmentTarget = null;
    }

    // HELPERS

    /**
     * Say a message to a specific player via the maid.
     */
    private void sayToPlayer(ServerPlayer player, String message) {
        MessageDispatcher.talkTo(maid, player, message);
    }
}
