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

import com.tiedup.remake.blocks.entity.MarkerBlockEntity;
import com.tiedup.remake.cells.CellDataV2;
import com.tiedup.remake.cells.CellRegistryV2;
import com.tiedup.remake.cells.MarkerType;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.ai.kidnapper.KidnapperState;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.jetbrains.annotations.Nullable;

/**
 * KidnapperCellManager - Manages cell-related queries and interactions.
 *
 * Handles:
 * - Finding cells containing specific prisoners
 * - Querying nearby cells with occupants
 * - Cell boundary detection (isInsideCell)
 * - Patrol marker discovery
 * - Cell breach response
 *
 * <p><b>Medium complexity</b> - Cell registry integration and boundary calculations.</p>
 */
public class KidnapperCellManager {

    // FIELDS

    /** Host callbacks */
    private final ICellHost host;

    // CONSTRUCTOR

    public KidnapperCellManager(ICellHost host) {
        this.host = host;
    }

    // CELL QUERIES

    /**
     * Find the cell containing a specific prisoner.
     *
     * @param prisonerId UUID of the prisoner
     * @return The cell data, or null if not found
     */
    @Nullable
    public CellDataV2 findCellContainingPrisoner(UUID prisonerId) {
        if (!(host.level() instanceof ServerLevel serverLevel)) return null;
        CellRegistryV2 registry = CellRegistryV2.get(serverLevel);
        return registry.findCellByPrisoner(prisonerId);
    }

    /**
     * Get all nearby cells (within 32 blocks) that have prisoners.
     *
     * @return List of occupied cells near this kidnapper
     */
    public List<CellDataV2> getNearbyCellsWithPrisoners() {
        List<CellDataV2> result = new ArrayList<>();
        if (!(host.level() instanceof ServerLevel serverLevel)) {
            return result;
        }
        CellRegistryV2 registry = CellRegistryV2.get(serverLevel);
        List<CellDataV2> nearbyCells = registry.findCellsNear(
            host.blockPosition(),
            32
        );
        for (CellDataV2 cell : nearbyCells) {
            if (cell.isOccupied()) {
                result.add(cell);
            }
        }
        return result;
    }

    // CELL BOUNDARY DETECTION

    /**
     * Check if a position is inside a cell's boundaries.
     * Uses wall markers to calculate bounds, or defaults to 5-block radius from spawn point.
     *
     * @param pos Position to check
     * @param cell Cell data
     * @return true if position is inside the cell
     */
    public boolean isInsideCell(BlockPos pos, CellDataV2 cell) {
        java.util.Set<BlockPos> walls = cell.getWallBlocks();

        if (walls.isEmpty()) {
            // No wall markers - use default radius from core position
            BlockPos spawn = cell.getCorePos();
            double distSq = pos.distSqr(spawn);
            return distSq <= 25; // 5 block radius default
        }

        // Calculate bounding box from wall markers
        int minX = Integer.MAX_VALUE,
            minY = Integer.MAX_VALUE,
            minZ = Integer.MAX_VALUE;
        int maxX = Integer.MIN_VALUE,
            maxY = Integer.MIN_VALUE,
            maxZ = Integer.MIN_VALUE;

        for (BlockPos wall : walls) {
            minX = Math.min(minX, wall.getX());
            minY = Math.min(minY, wall.getY());
            minZ = Math.min(minZ, wall.getZ());
            maxX = Math.max(maxX, wall.getX());
            maxY = Math.max(maxY, wall.getY());
            maxZ = Math.max(maxZ, wall.getZ());
        }

        // Check if position is within bounds (with 1-block tolerance on sides, 3-block tolerance on top)
        return (
            pos.getX() >= minX - 1 &&
            pos.getX() <= maxX + 1 &&
            pos.getY() >= minY - 1 &&
            pos.getY() <= maxY + 3 &&
            pos.getZ() >= minZ - 1 &&
            pos.getZ() <= maxZ + 1
        );
    }

    // PATROL MARKERS

    /**
     * Get all patrol markers from nearby cells.
     *
     * @param radius Search radius in blocks
     * @return List of patrol marker positions
     */
    public List<BlockPos> getNearbyPatrolMarkers(int radius) {
        List<BlockPos> result = new ArrayList<>();
        if (!(host.level() instanceof ServerLevel serverLevel)) return result;

        BlockPos center = host.blockPosition();
        int chunkRadius = (radius + 15) >> 4;
        int centerCX = center.getX() >> 4;
        int centerCZ = center.getZ() >> 4;

        for (
            int cx = centerCX - chunkRadius;
            cx <= centerCX + chunkRadius;
            cx++
        ) {
            for (
                int cz = centerCZ - chunkRadius;
                cz <= centerCZ + chunkRadius;
                cz++
            ) {
                net.minecraft.world.level.chunk.LevelChunk chunk = serverLevel
                    .getChunkSource()
                    .getChunkNow(cx, cz);
                if (chunk == null) continue;

                for (BlockEntity be : chunk.getBlockEntities().values()) {
                    if (!(be instanceof MarkerBlockEntity marker)) continue;
                    if (marker.getMarkerType() != MarkerType.PATROL) continue;

                    BlockPos pos = be.getBlockPos();
                    if (center.distSqr(pos) <= (double) radius * radius) {
                        result.add(pos);
                    }
                }
            }
        }
        return result;
    }

    // CELL BREACH RESPONSE

    /**
     * Called when a cell wall is breached (broken).
     * Kidnappers react to breaches in occupied cells or their own camp cells.
     *
     * @param breachPos Position where wall was broken
     * @param cellId UUID of the cell that was breached
     */
    public void onCellBreach(BlockPos breachPos, UUID cellId) {
        if (cellId == null) return;

        boolean shouldReact = false;
        CellDataV2 cell = null;

        // Check if this is an occupied cell
        if (host.level() instanceof ServerLevel serverLevel) {
            CellRegistryV2 registry = CellRegistryV2.get(serverLevel);
            cell = registry.getCell(cellId);

            if (cell != null && cell.isOccupied()) {
                shouldReact = true;
            }
        }

        // Also react if it's our associated structure
        if (
            host.getAssociatedStructure() != null &&
            host.getAssociatedStructure().equals(cellId)
        ) {
            shouldReact = true;
        }

        if (shouldReact) {
            TiedUpMod.LOGGER.info(
                "[KidnapperCellManager] {} detected wall breach at {}, investigating",
                host.getNpcName(),
                breachPos.toShortString()
            );

            // Navigate to breach location
            host
                .getNavigation()
                .moveTo(
                    breachPos.getX() + 0.5,
                    breachPos.getY(),
                    breachPos.getZ() + 0.5,
                    1.2
                );

            // Enter alert state
            host.setCurrentState(KidnapperState.ALERT);

            // Trigger dialogue
            host.talkToPlayersInRadius(
                com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory.CAPTURE_ESCAPE,
                20
            );
        }
    }

    // NBT SERIALIZATION

    /**
     * Save cell manager data to NBT.
     * Note: Cell manager uses only transient runtime state.
     */
    public void saveToNBT(net.minecraft.nbt.CompoundTag tag) {
        // Cell manager uses only transient runtime state
        // No persistent data needs to be saved
    }

    /**
     * Load cell manager data from NBT.
     * Note: Cell manager uses only transient runtime state.
     */
    public void loadFromNBT(net.minecraft.nbt.CompoundTag tag) {
        // Cell manager uses only transient runtime state
        // No persistent data needs to be loaded
    }
}
