package com.tiedup.remake.events.lifecycle;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.IV2BondageItem;
import com.tiedup.remake.v2.bondage.capability.V2BondageEquipmentProvider;
import java.util.Map;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameRules;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

/**
 * Event handler for capability attachment and persistence.
 * Handles attaching V2 bondage equipment to players and preserving it across death/respawn.
 */
@Mod.EventBusSubscriber(
    modid = TiedUpMod.MOD_ID,
    bus = Mod.EventBusSubscriber.Bus.FORGE
)
public class CapabilityEventHandler {

    private static final ResourceLocation V2_BONDAGE_EQUIPMENT_ID =
        ResourceLocation.fromNamespaceAndPath(
            TiedUpMod.MOD_ID,
            "v2_bondage_equipment"
        );

    /**
     * Attach V2 bondage equipment capability to all players.
     */
    @SubscribeEvent
    public static void onAttachCapabilities(
        AttachCapabilitiesEvent<Entity> event
    ) {
        if (!(event.getObject() instanceof Player)) {
            return;
        }

        // V2 bondage equipment capability
        V2BondageEquipmentProvider v2Provider =
            new V2BondageEquipmentProvider();
        event.addCapability(V2_BONDAGE_EQUIPMENT_ID, v2Provider);
        event.addListener(v2Provider::invalidate);
    }

    /**
     * Handle bondage equipment on player clone (death/respawn or dimension change).
     *
     * <p>Behavior:
     * <ul>
     *   <li>Death without keepInventory: Fire onUnequipped for all items, don't copy</li>
     *   <li>Death with keepInventory: Preserve via serialize/deserialize round-trip</li>
     *   <li>Dimension change (End portal): Preserve via serialize/deserialize round-trip</li>
     * </ul>
     *
     * IMPORTANT: Must call reviveCaps() on original player to access capabilities
     * after the entity has been marked as removed.
     */
    @SubscribeEvent
    public static void onPlayerClone(PlayerEvent.Clone event) {
        // Revive capabilities on old player (required after entity removal)
        event.getOriginal().reviveCaps();

        Player oldPlayer = event.getOriginal();
        Player newPlayer = event.getEntity();

        if (event.isWasDeath()) {
            boolean keepInventory = oldPlayer
                .level()
                .getGameRules()
                .getBoolean(GameRules.RULE_KEEPINVENTORY);

            // === V2 Death Handling ===
            oldPlayer
                .getCapability(V2BondageEquipmentProvider.V2_BONDAGE_EQUIPMENT)
                .ifPresent(oldV2 -> {
                    if (keepInventory) {
                        // keepInventory: serialize/deserialize round-trip
                        newPlayer
                            .getCapability(
                                V2BondageEquipmentProvider.V2_BONDAGE_EQUIPMENT
                            )
                            .ifPresent(newV2 -> {
                                CompoundTag saved = oldV2.serializeNBT();
                                newV2.deserializeNBT(saved);
                                // Fire onEquipped for each item on the new player
                                for (Map.Entry<
                                    BodyRegionV2,
                                    ItemStack
                                > entry : newV2.getAllEquipped().entrySet()) {
                                    ItemStack stack = entry.getValue();
                                    if (
                                        stack.getItem() instanceof
                                            IV2BondageItem v2Item
                                    ) {
                                        v2Item.onEquipped(stack, newPlayer);
                                    }
                                }
                                TiedUpMod.LOGGER.debug(
                                    "[CapabilityEventHandler] V2 preserved on death (keepInventory)"
                                );
                            });
                    } else {
                        // No keepInventory: fire onUnequipped for each V2 item
                        for (Map.Entry<BodyRegionV2, ItemStack> entry : oldV2
                            .getAllEquipped()
                            .entrySet()) {
                            ItemStack stack = entry.getValue();
                            if (
                                stack.getItem() instanceof IV2BondageItem v2Item
                            ) {
                                v2Item.onUnequipped(stack, oldPlayer);
                            }
                        }
                        TiedUpMod.LOGGER.debug(
                            "[CapabilityEventHandler] V2 cleared on death (no keepInventory)"
                        );
                    }
                });

            // Forge 1.20.1 doesn't auto-copy getPersistentData() across death.
            // Manually preserve the reconnection tag so death-without-
            // keepInventory can't free a locked player.
            net.minecraft.nbt.CompoundTag oldPersistent = oldPlayer.getPersistentData();
            if (oldPersistent.contains("tiedup_locked_furniture", net.minecraft.nbt.Tag.TAG_COMPOUND)) {
                newPlayer.getPersistentData().put(
                    "tiedup_locked_furniture",
                    oldPersistent.getCompound("tiedup_locked_furniture").copy()
                );
            }

            // Invalidate old player caps to prevent memory leak
            event.getOriginal().invalidateCaps();
            return;
        }

        // DIMENSION CHANGE (End portal, etc.): Preserve all equipment

        // Reconnection tag: only copy when the new player doesn't already
        // carry it (Forge's default clone copies persistent data, but we
        // defend against teleport paths that bypass it).
        net.minecraft.nbt.CompoundTag oldPersistentDim = oldPlayer.getPersistentData();
        if (oldPersistentDim.contains("tiedup_locked_furniture", net.minecraft.nbt.Tag.TAG_COMPOUND)
            && !newPlayer.getPersistentData().contains("tiedup_locked_furniture", net.minecraft.nbt.Tag.TAG_COMPOUND)) {
            newPlayer.getPersistentData().put(
                "tiedup_locked_furniture",
                oldPersistentDim.getCompound("tiedup_locked_furniture").copy()
            );
        }

        // === V2 Dimension Change ===
        oldPlayer
            .getCapability(V2BondageEquipmentProvider.V2_BONDAGE_EQUIPMENT)
            .ifPresent(oldV2 -> {
                newPlayer
                    .getCapability(
                        V2BondageEquipmentProvider.V2_BONDAGE_EQUIPMENT
                    )
                    .ifPresent(newV2 -> {
                        CompoundTag saved = oldV2.serializeNBT();
                        newV2.deserializeNBT(saved);
                        // Fire onEquipped for each item on the new player
                        for (Map.Entry<BodyRegionV2, ItemStack> entry : newV2
                            .getAllEquipped()
                            .entrySet()) {
                            ItemStack stack = entry.getValue();
                            if (
                                stack.getItem() instanceof IV2BondageItem v2Item
                            ) {
                                v2Item.onEquipped(stack, newPlayer);
                            }
                        }
                        TiedUpMod.LOGGER.debug(
                            "[CapabilityEventHandler] Preserved V2 equipment on dimension change"
                        );
                    });
            });

        // Invalidate old player caps to prevent memory leak
        event.getOriginal().invalidateCaps();
    }
}
