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

import com.tiedup.remake.cells.CellDataV2;
import com.tiedup.remake.cells.CellRegistryV2;
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.service.BondageService;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.util.KidnapperAIHelper;
import java.util.EnumSet;
import java.util.Set;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.ai.goal.Goal;

/**
 * Goal: Extract prisoners from cells for labor.
 *
 * Responsibility: ONE thing - extract IMPRISONED prisoners with PENDING_EXTRACTION phase.
 *
 * Simple flow:
 * 1. Find prisoner in PENDING_EXTRACTION
 * 2. Navigate to their cell
 * 3. Extract them (save bondage, remove restraints, give tools)
 * 4. Transition to WORKING state
 */
public class MaidExtractGoal extends Goal {

    private static final double CELL_REACH_DISTANCE = 4.0;
    private static final int MAX_DURATION_TICKS = 600; // 30 seconds timeout
    private static final int STUCK_TELEPORT_TICKS = 200; // 10 seconds without progress → teleport

    private final EntityMaid maid;

    // Current extraction state
    private UUID targetPrisonerId;
    private BlockPos targetCellPos;
    private int ticksActive;
    private boolean navigationStarted;
    private int stuckTicks;
    private BlockPos lastProgressPos;

    public MaidExtractGoal(EntityMaid maid) {
        this.maid = maid;
        this.setFlags(EnumSet.of(Flag.MOVE)); // Uses movement
    }

    /** Tick counter for periodic diagnostic logging */
    private int diagCounter = 0;

    @Override
    public boolean canUse() {
        // Periodic diagnostic: log which check blocks every 10 seconds
        boolean diag = (++diagCounter % 200 == 0);

        if (maid.isFreed() || maid.isTiedUp()) {
            if (diag) TiedUpMod.LOGGER.warn(
                "[MaidExtractGoal] BLOCKED: freed={} tiedUp={}",
                maid.isFreed(),
                maid.isTiedUp()
            );
            return false;
        }
        if (maid.getMasterTraderUUID() == null) {
            if (diag) TiedUpMod.LOGGER.warn(
                "[MaidExtractGoal] BLOCKED: getMasterTraderUUID()=null"
            );
            return false;
        }
        if (maid.getMaidState().isBusy()) {
            if (diag) TiedUpMod.LOGGER.warn(
                "[MaidExtractGoal] BLOCKED: maidState={} (isBusy=true)",
                maid.getMaidState()
            );
            return false;
        }
        if (!(maid.level() instanceof ServerLevel level)) {
            return false;
        }

        UUID campId = maid.getCampUUID();
        if (campId == null) {
            return false;
        }

        // Find a prisoner needing extraction
        PrisonerManager manager = PrisonerManager.get(level);
        Set<UUID> prisoners = manager.getPrisonersInCampWithState(
            campId,
            PrisonerState.IMPRISONED
        );

        for (UUID prisonerId : prisoners) {
            LaborRecord labor = manager.getLaborRecord(prisonerId);
            if (labor.getPhase() == LaborRecord.WorkPhase.PENDING_EXTRACTION) {
                PrisonerRecord record = manager.getRecord(prisonerId);
                UUID cellId = record.getCellId();
                if (cellId != null) {
                    CellDataV2 cell = CellRegistryV2.get(level).getCell(cellId);
                    if (cell != null) {
                        this.targetPrisonerId = prisonerId;
                        this.targetCellPos =
                            KidnapperAIHelper.getDeliveryOrSpawnPoint(
                                cell,
                                level
                            );
                        return true;
                    }
                }
            }
        }

        // Diagnostic: log why no prisoner found (only for players, NPCs don't get labor tasks)
        if (diag) {
            Set<UUID> allPrisoners = manager.getPrisonersInCamp(campId);
            boolean hasPlayers = false;
            for (UUID pid : allPrisoners) {
                // Only log players — NPCs stay in IDLE forever (no labor)
                if (level.getServer().getPlayerList().getPlayer(pid) != null) {
                    hasPlayers = true;
                    PrisonerRecord rec = manager.getRecord(pid);
                    LaborRecord lab = manager.getLaborRecord(pid);
                    TiedUpMod.LOGGER.warn(
                        "[MaidExtractGoal] BLOCKED: Player {} state={}, phase={} (need IMPRISONED+PENDING_EXTRACTION)",
                        pid.toString().substring(0, 8),
                        rec.getState(),
                        lab.getPhase()
                    );
                }
            }
            if (!hasPlayers && allPrisoners.isEmpty()) {
                TiedUpMod.LOGGER.warn(
                    "[MaidExtractGoal] BLOCKED: No prisoners found in camp {}",
                    campId.toString().substring(0, 8)
                );
            }
        }

        return false;
    }

    @Override
    public void start() {
        ticksActive = 0;
        navigationStarted = false;
        stuckTicks = 0;
        lastProgressPos = null;

        maid.setMaidState(
            com.tiedup.remake.entities.ai.maid.MaidState.EXTRACTING_PRISONER
        );

        TiedUpMod.LOGGER.debug(
            "[MaidExtractGoal] {} starting extraction of {}",
            maid.getNpcName(),
            targetPrisonerId != null
                ? targetPrisonerId.toString().substring(0, 8)
                : "null"
        );
    }

    @Override
    public void tick() {
        ticksActive++;

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

        // Timeout check
        if (ticksActive > MAX_DURATION_TICKS) {
            TiedUpMod.LOGGER.warn("[MaidExtractGoal] Extraction timed out");
            stop();
            return;
        }

        if (targetCellPos == null) return;

        // Check if we've arrived
        double distance = maid
            .position()
            .distanceTo(
                new net.minecraft.world.phys.Vec3(
                    targetCellPos.getX() + 0.5,
                    targetCellPos.getY(),
                    targetCellPos.getZ() + 0.5
                )
            );

        if (distance <= CELL_REACH_DISTANCE) {
            // At cell - perform extraction
            performExtraction(level);
            stop();
            return;
        }

        // Navigate to cell - re-path every 20 ticks
        if (!navigationStarted || ticksActive % 20 == 0) {
            maid
                .getNavigation()
                .moveTo(
                    targetCellPos.getX() + 0.5,
                    targetCellPos.getY(),
                    targetCellPos.getZ() + 0.5,
                    1.0
                );
            navigationStarted = true;
        }

        // Stuck detection: teleport to cell if no progress
        BlockPos currentPos = maid.blockPosition();
        if (
            lastProgressPos == null ||
            currentPos.distManhattan(lastProgressPos) >= 2
        ) {
            lastProgressPos = currentPos;
            stuckTicks = 0;
        } else {
            stuckTicks++;
        }
        if (stuckTicks >= STUCK_TELEPORT_TICKS) {
            TiedUpMod.LOGGER.info(
                "[MaidExtractGoal] {} stuck for {}s, teleporting to cell",
                maid.getNpcName(),
                STUCK_TELEPORT_TICKS / 20
            );
            maid.teleportTo(
                targetCellPos.getX() + 0.5,
                targetCellPos.getY() + 1,
                targetCellPos.getZ() + 0.5
            );
            stuckTicks = 0;
            lastProgressPos = null;
        }
    }

    /**
     * Perform the extraction sequence.
     */
    private void performExtraction(ServerLevel level) {
        if (targetPrisonerId == null) {
            return;
        }

        ServerPlayer prisoner = level
            .getServer()
            .getPlayerList()
            .getPlayer(targetPrisonerId);
        if (prisoner == null) {
            TiedUpMod.LOGGER.warn(
                "[MaidExtractGoal] Prisoner offline during extraction"
            );
            return;
        }

        PrisonerManager manager = PrisonerManager.get(level);
        LaborRecord labor = manager.getLaborRecord(targetPrisonerId);
        LaborTask task = labor.getTask();

        if (task == null) {
            TiedUpMod.LOGGER.warn(
                "[MaidExtractGoal] No task assigned during extraction"
            );
            return;
        }

        IBondageState cap = KidnappedHelper.getKidnappedState(prisoner);
        if (cap == null) {
            return;
        }

        long currentTime = level.getGameTime();

        // 1. Save bondage state
        CompoundTag bondageSnapshot = BondageService.get().saveSnapshot(cap);
        labor.setBondageSnapshot(bondageSnapshot);

        // 2. Remove restraints for labor
        BondageService.get().removeForLabor(cap);

        // 3. Give equipment
        task.giveEquipment(prisoner);

        // 3.5. Teleport prisoner out of cell to maid's position
        BlockPos maidPos = maid.blockPosition();
        com.tiedup.remake.util.teleport.Position teleportTarget =
            new com.tiedup.remake.util.teleport.Position(
                maidPos.above(),
                level.dimension()
            );
        com.tiedup.remake.util.teleport.TeleportHelper.teleportEntity(
            prisoner,
            teleportTarget
        );

        TiedUpMod.LOGGER.debug(
            "[MaidExtractGoal] Teleported {} to maid at {}",
            prisoner.getName().getString(),
            maidPos.toShortString()
        );

        // 4. Transition prisoner state: IMPRISONED → WORKING (do this BEFORE updating labor record)
        //    If this fails (e.g., state changed between ticks), abort before modifying labor record
        UUID cellId = null;
        PrisonerRecord record = manager.getRecord(targetPrisonerId);
        if (record != null) {
            cellId = record.getCellId();
        }
        if (!manager.extract(targetPrisonerId, currentTime)) {
            TiedUpMod.LOGGER.warn(
                "[MaidExtractGoal] manager.extract() FAILED for {} — aborting extraction",
                targetPrisonerId.toString().substring(0, 8)
            );
            return;
        }

        // 5. Update labor record (only after successful state transition)
        labor.startWorking(currentTime);
        labor.updateActivity(
            currentTime,
            prisoner.blockPosition().getX(),
            prisoner.blockPosition().getY(),
            prisoner.blockPosition().getZ()
        );
        if (cellId != null) {
            CellRegistryV2.get(level).releasePrisoner(
                cellId,
                targetPrisonerId,
                level.getServer()
            );
        }

        // 6. Spawn guard if no guard already assigned
        if (labor.getGuardId() == null) {
            spawnGuard(level, prisoner, labor, currentTime);
        }

        // 7. Notify prisoner
        prisoner.sendSystemMessage(
            net.minecraft.network.chat.Component.translatable(
                "goal.tiedup.maid_extract.extracted",
                task.getDescription()
            ).withStyle(net.minecraft.ChatFormatting.YELLOW)
        );

        TiedUpMod.LOGGER.info(
            "[MaidExtractGoal] Extracted {} for task '{}'",
            prisoner.getName().getString(),
            task.getDescription()
        );
    }

    /**
     * Spawn a guard entity near the prisoner at a safe position.
     */
    private void spawnGuard(
        ServerLevel level,
        ServerPlayer prisoner,
        LaborRecord labor,
        long currentTime
    ) {
        com.tiedup.remake.entities.EntityLaborGuard guard =
            new com.tiedup.remake.entities.EntityLaborGuard(
                com.tiedup.remake.entities.ModEntities.LABOR_GUARD.get(),
                level
            );

        // Spawn guard near the maid (not the prisoner, who may still be in the cell)
        BlockPos maidPos = maid.blockPosition();
        BlockPos safePos = findSafeSpawnPos(level, maidPos);

        guard.setPos(
            safePos.getX() + 0.5,
            safePos.getY(),
            safePos.getZ() + 0.5
        );

        // Configure guard
        guard.setPrisonerUUID(prisoner.getUUID());
        guard.setCampId(maid.getCampUUID());
        guard.setSpawnerMaidId(maid.getUUID());

        // Add to world
        level.addFreshEntity(guard);

        // Store guard ID in labor record
        labor.setGuardId(guard.getUUID());

        TiedUpMod.LOGGER.info(
            "[MaidExtractGoal] Spawned guard {} for prisoner {}",
            guard.getUUID().toString().substring(0, 8),
            prisoner.getName().getString()
        );
    }

    /**
     * Find a safe position to spawn the guard near the given position.
     * Checks that the position has 2 blocks of air and solid ground below.
     */
    private BlockPos findSafeSpawnPos(ServerLevel level, BlockPos center) {
        // First try: maid's exact position (always walkable since she's standing there)
        if (isSafeForSpawn(level, center)) {
            return center;
        }

        // Try nearby offsets: cardinal directions first, then diagonals
        int[][] offsets = {
            { 1, 0 },
            { -1, 0 },
            { 0, 1 },
            { 0, -1 },
            { 2, 0 },
            { -2, 0 },
            { 0, 2 },
            { 0, -2 },
        };

        for (int[] offset : offsets) {
            for (int dy = 0; dy <= 2; dy++) {
                BlockPos candidate = center.offset(offset[0], dy, offset[1]);
                if (isSafeForSpawn(level, candidate)) {
                    return candidate;
                }
            }
        }

        // Fallback: maid's exact position (two entities can share a block)
        return center;
    }

    /**
     * Check if a position is safe for entity spawn: solid below, 2 air blocks at feet and head.
     */
    private boolean isSafeForSpawn(ServerLevel level, BlockPos pos) {
        net.minecraft.world.level.block.state.BlockState below =
            level.getBlockState(pos.below());
        net.minecraft.world.level.block.state.BlockState atFeet =
            level.getBlockState(pos);
        net.minecraft.world.level.block.state.BlockState atHead =
            level.getBlockState(pos.above());

        return (
            below.isSolid() &&
            !atFeet.isSolid() &&
            !atFeet.liquid() &&
            !atHead.isSolid() &&
            !atHead.liquid()
        );
    }

    @Override
    public boolean canContinueToUse() {
        if (targetPrisonerId == null) {
            return false;
        }
        if (maid.isFreed() || maid.isTiedUp()) {
            return false;
        }
        return ticksActive < MAX_DURATION_TICKS;
    }

    @Override
    public void stop() {
        maid.getNavigation().stop();
        maid.setMaidState(com.tiedup.remake.entities.ai.maid.MaidState.IDLE);

        targetPrisonerId = null;
        targetCellPos = null;
        navigationStarted = false;

        TiedUpMod.LOGGER.debug("[MaidExtractGoal] Stopped");
    }
}
