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

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.state.ICaptor;
import java.util.EnumSet;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.goal.Goal;

/**
 * AI Goal for EntityMaid to fetch a captive from their cell.
 *
 * Behavior:
 * - Active when state is FETCHING
 * - If cell has DELIVERY markers: navigate to delivery point
 * - If no delivery markers: navigate toward cell with larger acceptance zone
 * - Teleport prisoner TO maid (never maid into cell)
 * - Capture + transition to DELIVERING
 */
public class MaidFetchCaptiveGoal extends Goal {

    private final EntityMaid maid;

    /** Distance for capture when navigating to delivery point */
    private static final double DELIVERY_CAPTURE_DIST = 3.0;

    /** Distance for capture when no delivery point (maid outside cell walls) */
    private static final double NO_DELIVERY_CAPTURE_DIST = 8.0;

    /** Maximum ticks before giving up */
    private static final int MAX_FETCH_TICKS = 1200; // 60 seconds

    /** If stuck for this long, just capture directly (TP prisoner out) */
    private static final int STUCK_CAPTURE_TICKS = 200; // 10 seconds

    /** Where the maid navigates to (delivery point or cell spawn) */
    private BlockPos navigationTarget;

    /** Whether the cell has a delivery point */
    private boolean hasDeliveryPoint;

    /** Target cell data */
    private CellDataV2 targetCell;

    /** Cell spawn point (for prisoner reference) */
    private BlockPos cellSpawnPoint;

    /** Target captive entity */
    private LivingEntity captiveEntity;

    /** Ticks spent fetching */
    private int fetchTicks = 0;

    /** Stuck detection */
    private int stuckTicks = 0;
    private BlockPos lastProgressPos = null;

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

    @Override
    public boolean canUse() {
        if (maid.getMaidState() != MaidState.FETCHING) {
            return false;
        }
        if (maid.isTiedUp()) {
            return false;
        }
        if (maid.hasCaptives()) {
            return false;
        }

        UUID captiveUUID = maid.getTargetCaptiveUUID();
        if (captiveUUID == null) {
            return false;
        }
        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return false;
        }

        // Find the cell containing this captive
        CellRegistryV2 registry = CellRegistryV2.get(serverLevel);
        CellDataV2 cell = registry.findCellByPrisoner(captiveUUID);
        if (cell == null) {
            TiedUpMod.LOGGER.warn(
                "[MaidFetchCaptiveGoal] Captive {} not found in any cell",
                captiveUUID.toString().substring(0, 8)
            );
            maid.completeTask();
            return false;
        }

        this.targetCell = cell;
        this.cellSpawnPoint = cell.getCorePos();

        // Check for delivery point
        BlockPos deliveryPos = cell.getDeliveryPoint();
        if (deliveryPos != null) {
            this.navigationTarget = deliveryPos;
            this.hasDeliveryPoint = true;
        } else {
            // No delivery point — navigate toward cell spawn, larger acceptance
            this.navigationTarget = cellSpawnPoint;
            this.hasDeliveryPoint = false;
        }

        // Find captive entity
        var entity = serverLevel.getEntity(captiveUUID);
        if (!(entity instanceof LivingEntity living) || !living.isAlive()) {
            maid.completeTask();
            return false;
        }
        this.captiveEntity = living;

        return true;
    }

    @Override
    public boolean canContinueToUse() {
        if (maid.getMaidState() != MaidState.FETCHING) {
            return false;
        }
        if (maid.isTiedUp()) {
            return false;
        }
        if (
            navigationTarget == null ||
            captiveEntity == null ||
            !captiveEntity.isAlive()
        ) {
            return false;
        }
        if (fetchTicks > MAX_FETCH_TICKS) {
            TiedUpMod.LOGGER.warn(
                "[MaidFetchCaptiveGoal] {} fetch timed out",
                maid.getNpcName()
            );
            maid.completeTask();
            return false;
        }
        return true;
    }

    @Override
    public void start() {
        fetchTicks = 0;
        stuckTicks = 0;
        lastProgressPos = null;

        TiedUpMod.LOGGER.info(
            "[MaidFetchCaptiveGoal] {} starting fetch — {} at {}",
            maid.getNpcName(),
            hasDeliveryPoint ? "delivery point" : "cell approach",
            navigationTarget != null
                ? navigationTarget.toShortString()
                : "unknown"
        );
    }

    @Override
    public void stop() {
        navigationTarget = null;
        cellSpawnPoint = null;
        targetCell = null;
        captiveEntity = null;
        fetchTicks = 0;
        maid.getNavigation().stop();
        // Only reset to IDLE if still in FETCHING state.
        // capturePrisoner() may have already transitioned to DELIVERING.
        if (maid.getMaidState() == MaidState.FETCHING) {
            maid.setMaidState(MaidState.IDLE);
        }
    }

    @Override
    public void tick() {
        if (navigationTarget == null || captiveEntity == null) {
            return;
        }

        fetchTicks++;

        // Look at navigation target direction
        maid
            .getLookControl()
            .setLookAt(
                navigationTarget.getX() + 0.5,
                navigationTarget.getY() + 1,
                navigationTarget.getZ() + 0.5
            );

        // Check distance to navigation target
        double distToTarget = maid
            .position()
            .distanceTo(
                new net.minecraft.world.phys.Vec3(
                    navigationTarget.getX() + 0.5,
                    navigationTarget.getY(),
                    navigationTarget.getZ() + 0.5
                )
            );

        double captureDistance = hasDeliveryPoint
            ? DELIVERY_CAPTURE_DIST
            : NO_DELIVERY_CAPTURE_DIST;

        if (distToTarget <= captureDistance) {
            // Close enough — capture (TP prisoner to maid)
            capturePrisoner();
            return;
        }

        // Navigate toward target — re-path every 20 ticks
        if (fetchTicks % 20 == 0) {
            maid
                .getNavigation()
                .moveTo(
                    navigationTarget.getX() + 0.5,
                    navigationTarget.getY(),
                    navigationTarget.getZ() + 0.5,
                    1.0
                );
        }

        // Stuck detection: if no progress, capture directly (TP prisoner OUT to maid)
        BlockPos currentPos = maid.blockPosition();
        if (
            lastProgressPos == null ||
            currentPos.distManhattan(lastProgressPos) >= 2
        ) {
            lastProgressPos = currentPos;
            stuckTicks = 0;
        } else {
            stuckTicks++;
        }

        if (stuckTicks >= STUCK_CAPTURE_TICKS) {
            TiedUpMod.LOGGER.info(
                "[MaidFetchCaptiveGoal] {} stuck for {}s — capturing directly (TP prisoner to maid)",
                maid.getNpcName(),
                STUCK_CAPTURE_TICKS / 20
            );
            capturePrisoner();
        }
    }

    /**
     * Capture the prisoner from the cell.
     * Prisoner is teleported TO the maid's position.
     * Uses PrisonerService.extractFromCell() for atomic state management.
     */
    private void capturePrisoner() {
        if (captiveEntity == null || targetCell == null) {
            return;
        }
        if (!(maid.level() instanceof ServerLevel serverLevel)) {
            return;
        }

        // 1. Extract from cell via PrisonerService (PrisonerManager + CellRegistry + leash)
        boolean extracted =
            com.tiedup.remake.prison.service.PrisonerService.get().extractFromCell(
                serverLevel,
                captiveEntity,
                (ICaptor) maid,
                targetCell
            );

        if (extracted) {
            // 2. Teleport prisoner TO maid (never maid to cell)
            captiveEntity.teleportTo(maid.getX(), maid.getY(), maid.getZ());

            TiedUpMod.LOGGER.info(
                "[MaidFetchCaptiveGoal] {} fetched {} from cell",
                maid.getNpcName(),
                captiveEntity.getName().getString()
            );
            maid.transitionToDelivering();
        } else {
            // Extraction failed — PrisonerService handles its own rollback
            TiedUpMod.LOGGER.warn(
                "[MaidFetchCaptiveGoal] {} extraction failed for {}",
                maid.getNpcName(),
                captiveEntity.getName().getString()
            );
            maid.completeTask();
        }
    }
}
