package com.tiedup.remake.events.lifecycle;

import com.mojang.logging.LogUtils;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.events.captivity.ForcedSeatingHandler;
import com.tiedup.remake.events.restriction.LaborToolProtectionHandler;
import com.tiedup.remake.events.restriction.PetPlayRestrictionHandler;
import com.tiedup.remake.network.PacketRateLimiter;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.slf4j.Logger;

/**
 * Handler for player disconnect events to clean up server-side resources.
 *
 * <p>This handler ensures that resources associated with disconnected players
 * are properly cleaned up to prevent memory leaks and unbounded map growth.
 *
 * <p>Phase: Server Resource Management
 *
 * <p>Cleanup includes:
 * <ul>
 *   <li>Rate limiter token buckets ({@link PacketRateLimiter})</li>
 *   <li>Future: Session data, pending tasks, etc.</li>
 * </ul>
 */
@Mod.EventBusSubscriber(
    modid = TiedUpMod.MOD_ID,
    bus = Mod.EventBusSubscriber.Bus.FORGE
)
public class PlayerDisconnectHandler {

    private static final Logger LOGGER = LogUtils.getLogger();

    /**
     * Clean up resources when a player logs out.
     *
     * <p>This event fires when a player disconnects from the server,
     * either by logging out normally or being kicked/timing out.
     *
     * @param event The player logout event
     */
    @SubscribeEvent
    public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) {
        java.util.UUID playerId = event.getEntity().getUUID();

        // NOTE: PlayerBindState.removeInstance() is called by PlayerLifecycleHandler (EventPriority.HIGH)
        // We don't duplicate it here to avoid redundant cleanup

        // Clean up rate limiter state
        PacketRateLimiter.cleanup(playerId);

        // Clean up static cooldown maps to prevent memory leaks
        com.tiedup.remake.commands.SocialCommand.cleanupPlayer(playerId);
        LaborToolProtectionHandler.cleanupPlayer(playerId);

        // BUG FIX: Memory leak cleanup for event handlers
        // Clean up ForcedSeatingHandler maps
        ForcedSeatingHandler.clearPlayer(playerId);

        // Clean up PetPlayRestrictionHandler timestamp map
        PetPlayRestrictionHandler.clearPlayer(playerId);

        // Clean up minigame sessions
        com.tiedup.remake.minigame.MiniGameSessionManager.getInstance().cleanupPlayer(
            playerId
        );

        // Clean up pet cage state
        com.tiedup.remake.v2.blocks.PetCageManager.onPlayerDisconnect(playerId);

        // Clean up pet bed state (has onPlayerDisconnect but was never wired)
        com.tiedup.remake.v2.blocks.PetBedManager.onPlayerDisconnect(playerId);

        // Clean up active conversations
        com.tiedup.remake.dialogue.conversation.ConversationManager.cleanupPlayer(
            playerId
        );

        // Clean up cell selection mode
        com.tiedup.remake.cells.CellSelectionManager.cleanup(playerId);

        // NOTE: tiedup_locked_furniture is intentionally NOT cleaned on logout —
        // it's load-bearing for NetworkEventHandler.handleFurnitureReconnection
        // (the "disconnect to escape" prevention).
        //
        // tiedup_furniture_lockpick_ctx IS cleaned: it's session-ephemeral,
        // valid only during an active lockpick mini-game. If left stale it
        // causes PacketLockpickAttempt to mis-route a later body-item
        // lockpick as a furniture pick and silently return without ending
        // the session.
        if (event.getEntity() instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
            serverPlayer.getPersistentData().remove("tiedup_furniture_lockpick_ctx");
        }

        // BUG FIX: Security - Remove labor tools from disconnecting player
        // This prevents players from keeping unbreakable tools by disconnecting
        if (
            event.getEntity() instanceof
                net.minecraft.server.level.ServerPlayer player
        ) {
            removeLaborTools(player);
        }

        // BUG FIX: Memory leak cleanup for entities
        // Clean up EntityKidnapperMerchant tradingPlayers set (O(1) reverse-lookup)
        if (
            event.getEntity().level() instanceof
                net.minecraft.server.level.ServerLevel serverLevel
        ) {
            java.util.UUID merchantUUID =
                com.tiedup.remake.entities.EntityKidnapperMerchant.getMerchantForPlayer(
                    playerId
                );
            if (merchantUUID != null) {
                net.minecraft.world.entity.Entity merchantEntity =
                    serverLevel.getEntity(merchantUUID);
                if (
                    merchantEntity instanceof
                        com.tiedup.remake.entities.EntityKidnapperMerchant merchant
                ) {
                    merchant.cleanupTradingPlayer(playerId);
                }
            }

            // Kidnapper robbery immunity: cheap per-entity Map.remove(), disconnect-only — acceptable scan
            // Uses getAllEntities since there's no UUID index for this reverse lookup
            for (net.minecraft.world.entity.Entity entity : serverLevel.getAllEntities()) {
                if (
                    entity instanceof
                        com.tiedup.remake.entities.EntityKidnapper kidnapper
                ) {
                    kidnapper.clearRobbedImmunity(playerId);
                }
            }
        }

        LOGGER.debug(
            "Cleaned up server resources for player: {} ({})",
            event.getEntity().getName().getString(),
            playerId
        );
    }

    /**
     * SECURITY: Remove all labor tools from player inventory on disconnect.
     * Prevents exploit where players disconnect to keep unbreakable tools.
     */
    private static void removeLaborTools(
        net.minecraft.server.level.ServerPlayer player
    ) {
        var inventory = player.getInventory();
        int removedCount = 0;

        for (int i = 0; i < inventory.getContainerSize(); i++) {
            net.minecraft.world.item.ItemStack stack = inventory.getItem(i);
            if (!stack.isEmpty() && stack.hasTag()) {
                net.minecraft.nbt.CompoundTag tag = stack.getTag();
                if (tag != null && tag.getBoolean("LaborTool")) {
                    inventory.setItem(
                        i,
                        net.minecraft.world.item.ItemStack.EMPTY
                    );
                    removedCount++;
                }
            }
        }

        if (removedCount > 0) {
            LOGGER.info(
                "[PlayerDisconnectHandler] Removed {} labor tools from {} on disconnect",
                removedCount,
                player.getName().getString()
            );
        }
    }
}
