package com.tiedup.remake.events.camp;

import com.tiedup.remake.cells.CampMaidManager;
import com.tiedup.remake.cells.CampOwnership;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.EntityMaid;
import com.tiedup.remake.entities.EntitySlaveTrader;
import com.tiedup.remake.entities.ModEntities;
// Prison system v2
import com.tiedup.remake.prison.PrisonerManager;
import com.tiedup.remake.prison.service.EscapeMonitorService;
import java.util.List;
import java.util.UUID;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

/**
 * Handles camp management tasks including maid respawn.
 *
 * When a maid dies:
 * - Camp remains alive (trader still exists)
 * - Prisoners are paused (no new tasks)
 * - After 5 minutes (6000 ticks), a new maid spawns
 * - Prisoners are notified and labor resumes
 */
@Mod.EventBusSubscriber(
    modid = TiedUpMod.MOD_ID,
    bus = Mod.EventBusSubscriber.Bus.FORGE
)
public class CampManagementHandler {

    // Check every 100 ticks (5 seconds) - optimized for performance
    private static final int CHECK_INTERVAL_TICKS = 100;

    private static int tickCounter = 0;

    /**
     * Periodically check for camps needing maid respawn.
     */
    @SubscribeEvent
    public static void onServerTick(TickEvent.ServerTickEvent event) {
        if (event.phase != TickEvent.Phase.END) return;

        tickCounter++;
        if (tickCounter < CHECK_INTERVAL_TICKS) return;
        tickCounter = 0;

        // Process all worlds
        for (ServerLevel level : event.getServer().getAllLevels()) {
            processCampManagement(level);
        }
    }

    /**
     * Process camp management for a specific level.
     */
    private static void processCampManagement(ServerLevel level) {
        // IMPORTANT: Always use server-level registry to avoid dimension fragmentation
        CampOwnership ownership = CampOwnership.get(level.getServer());
        long currentTime = level.getGameTime();

        // Get camps that need maid respawn
        List<UUID> campsNeedingMaid =
            CampMaidManager.getCampsNeedingMaidRespawn(currentTime, level);

        for (UUID campId : campsNeedingMaid) {
            spawnNewMaidForCamp(level, ownership, campId);
        }

        // Prison system v2 - tick escape service (handles escape detection)
        EscapeMonitorService.get().tick(level.getServer(), currentTime);

        // Prison system v2 - tick protection expiry
        PrisonerManager.get(level).tickProtectionExpiry(currentTime);
    }

    /**
     * Spawn a new maid for a camp that needs one.
     *
     * @param level The server level
     * @param ownership The camp ownership data
     * @param campId The camp UUID
     */
    private static void spawnNewMaidForCamp(
        ServerLevel level,
        CampOwnership ownership,
        UUID campId
    ) {
        CampOwnership.CampData campData = ownership.getCamp(campId);
        if (campData == null || !campData.isAlive()) {
            return;
        }

        BlockPos center = campData.getCenter();
        if (center == null) {
            TiedUpMod.LOGGER.warn(
                "[CampManagementHandler] Cannot spawn maid - camp {} has no center position",
                campId.toString().substring(0, 8)
            );
            return;
        }

        UUID traderUUID = campData.getTraderUUID();
        if (traderUUID == null) {
            TiedUpMod.LOGGER.warn(
                "[CampManagementHandler] Cannot spawn maid - camp {} has no trader",
                campId.toString().substring(0, 8)
            );
            return;
        }

        // Find the trader entity
        Entity traderEntity = level.getEntity(traderUUID);
        EntitySlaveTrader trader = null;
        if (traderEntity instanceof EntitySlaveTrader t) {
            trader = t;
        } else {
            // Trader not loaded - search near camp center
            trader = findTraderNearPosition(level, center, 50);
        }

        if (trader == null) {
            TiedUpMod.LOGGER.warn(
                "[CampManagementHandler] Cannot spawn maid - trader not found for camp {}",
                campId.toString().substring(0, 8)
            );
            return;
        }

        // Create new maid
        EntityMaid maid = ModEntities.MAID.get().create(level);
        if (maid == null) {
            TiedUpMod.LOGGER.error(
                "[CampManagementHandler] Failed to create maid entity for camp {}",
                campId.toString().substring(0, 8)
            );
            return;
        }

        // Find spawn position near trader (slightly offset)
        BlockPos spawnPos = findSafeSpawnPosition(
            level,
            trader.blockPosition()
        );

        // Position the maid
        maid.moveTo(
            spawnPos.getX() + 0.5,
            spawnPos.getY(),
            spawnPos.getZ() + 0.5,
            trader.getYRot() + 180, // Face opposite direction from trader
            0
        );

        // Link maid to trader
        maid.setMasterTraderUUID(trader.getUUID());

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

        // Update camp ownership with new maid
        CampMaidManager.assignNewMaid(campId, maid.getUUID(), level);

        // Update trader's maid reference
        trader.setMaidUUID(maid.getUUID());

        TiedUpMod.LOGGER.info(
            "[CampManagementHandler] Spawned replacement maid {} for camp {} at {}",
            maid.getNpcName(),
            campId.toString().substring(0, 8),
            spawnPos.toShortString()
        );

        // Notify prisoners
        notifyPrisonersOfNewMaid(level, campId, maid.getNpcName());
    }

    /**
     * Find a trader near the given position.
     */
    private static EntitySlaveTrader findTraderNearPosition(
        ServerLevel level,
        BlockPos pos,
        int radius
    ) {
        List<EntitySlaveTrader> traders = level.getEntitiesOfClass(
            EntitySlaveTrader.class,
            new net.minecraft.world.phys.AABB(pos).inflate(radius)
        );
        return traders.isEmpty() ? null : traders.get(0);
    }

    /**
     * Find a safe position to spawn the maid.
     * Tries positions around the target, prioritizing nearby valid spots.
     */
    private static BlockPos findSafeSpawnPosition(
        ServerLevel level,
        BlockPos targetPos
    ) {
        // Try offsets in a spiral pattern
        int[][] offsets = {
            { 1, 0 },
            { 0, 1 },
            { -1, 0 },
            { 0, -1 },
            { 1, 1 },
            { -1, 1 },
            { -1, -1 },
            { 1, -1 },
            { 2, 0 },
            { 0, 2 },
            { -2, 0 },
            { 0, -2 },
        };

        for (int[] offset : offsets) {
            BlockPos testPos = targetPos.offset(offset[0], 0, offset[1]);

            // Check if position is safe (solid ground, air above)
            if (isValidSpawnPosition(level, testPos)) {
                return testPos;
            }

            // Try one block down
            BlockPos downPos = testPos.below();
            if (isValidSpawnPosition(level, downPos)) {
                return downPos;
            }

            // Try one block up
            BlockPos upPos = testPos.above();
            if (isValidSpawnPosition(level, upPos)) {
                return upPos;
            }
        }

        // Fallback: use target position
        return targetPos;
    }

    /**
     * Check if a position is valid for spawning.
     */
    private static boolean isValidSpawnPosition(
        ServerLevel level,
        BlockPos pos
    ) {
        // Ground must be solid
        if (!level.getBlockState(pos.below()).isSolid()) {
            return false;
        }

        // Position and above must be passable (air or similar)
        if (!level.getBlockState(pos).isAir()) {
            return false;
        }

        if (!level.getBlockState(pos.above()).isAir()) {
            return false;
        }

        return true;
    }

    /**
     * Notify all prisoners of this camp that a new maid has arrived.
     */
    private static void notifyPrisonersOfNewMaid(
        ServerLevel level,
        UUID campId,
        String maidName
    ) {
        PrisonerManager manager = PrisonerManager.get(level);
        for (UUID prisonerId : manager.getPrisonersInCamp(campId)) {
            ServerPlayer player = level
                .getServer()
                .getPlayerList()
                .getPlayer(prisonerId);
            if (player != null) {
                player.sendSystemMessage(
                    Component.translatable(
                        "msg.tiedup.event.new_maid_arrived",
                        maidName
                    ).withStyle(ChatFormatting.GOLD)
                );
            }
        }
    }
}
