package com.tiedup.remake.client.animation.render;

import com.tiedup.remake.client.renderer.layers.ClothesRenderHelper;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
import com.tiedup.remake.items.clothes.ClothesProperties;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.BodyRegionV2;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

/**
 * Hide player arms and outer layers based on bondage/clothes state.
 *
 * <p>Responsibilities (after extraction of dog pose, pet bed, and held items):
 * <ul>
 *   <li>Hide arms for wrap/latex_sack poses</li>
 *   <li>Hide outer layers (hat, jacket, sleeves, pants) based on clothes settings</li>
 * </ul>
 *
 * <p>Uses Pre/Post pattern to temporarily modify and restore state.
 */
@OnlyIn(Dist.CLIENT)
@Mod.EventBusSubscriber(
    modid = TiedUpMod.MOD_ID,
    bus = Mod.EventBusSubscriber.Bus.FORGE,
    value = Dist.CLIENT
)
public class PlayerArmHideEventHandler {

    /**
     * Stored layer visibility to restore after rendering.
     * Key: Player entity ID (int), Value: [hat, jacket, leftSleeve, rightSleeve, leftPants, rightPants].
     * Presence in the map is also the sentinel for "Post must restore layers".
     */
    private static final Int2ObjectMap<boolean[]> storedLayers =
        new Int2ObjectOpenHashMap<>();

    /**
     * Entity ids whose arm visibility we hid in Pre, so Post only restores
     * what we touched. Unconditional restore would clobber arm-hide state
     * set by other mods on the shared {@link PlayerModel}.
     */
    private static final IntSet hiddenArmEntities = new IntOpenHashSet();

    /**
     * Before player render:
     * - Hide arms for wrap/latex_sack poses
     * - Hide outer layers based on clothes settings
     *
     * <p>LOW priority + {@code isCanceled} guard: skip mutation when any
     * earlier-priority canceller fired. Paired Post uses
     * {@code receiveCanceled = true} + sentinel maps so mutations get
     * restored even if a downstream canceller skips the normal Post path
     * (Forge gates Post firing on the final canceled state).</p>
     */
    @SubscribeEvent(priority = EventPriority.LOW)
    public static void onRenderPlayerPre(RenderPlayerEvent.Pre event) {
        if (event.isCanceled()) {
            return;
        }

        Player player = event.getEntity();
        if (!(player instanceof AbstractClientPlayer clientPlayer)) {
            return;
        }

        if (player.isRemoved() || !player.isAlive()) {
            return;
        }

        PlayerBindState state = PlayerBindState.getInstance(player);
        if (state == null) {
            return;
        }

        PlayerModel<?> model = event.getRenderer().getModel();

        // === HIDE ARMS (wrap/latex_sack poses) ===
        if (state.hasArmsBound()) {
            ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
            if (!bind.isEmpty()) {
                PoseType poseType = PoseTypeHelper.getPoseType(bind);

                // Only hide arms for wrap/sack poses (arms are covered by the item)
                if (
                    poseType == PoseType.WRAP || poseType == PoseType.LATEX_SACK
                ) {
                    model.leftArm.visible = false;
                    model.rightArm.visible = false;
                    model.leftSleeve.visible = false;
                    model.rightSleeve.visible = false;
                    hiddenArmEntities.add(player.getId());
                }
            }
        }

        // === HIDE WEARER LAYERS (clothes settings) ===
        ItemStack clothes = state.getEquipment(BodyRegionV2.TORSO);
        if (!clothes.isEmpty()) {
            ClothesProperties props =
                ClothesRenderHelper.getPropsForLayerHiding(
                    clothes,
                    clientPlayer
                );
            if (props != null) {
                boolean[] savedLayers = ClothesRenderHelper.hideWearerLayers(
                    model,
                    props
                );
                if (savedLayers != null) {
                    storedLayers.put(player.getId(), savedLayers);
                }
            }
        }
    }

    /**
     * After player render: restore visibility only for state we actually
     * mutated. {@code receiveCanceled=true} so we fire even when a
     * downstream canceller cancelled the paired Pre — otherwise mutations
     * stay applied forever.
     */
    @SubscribeEvent(receiveCanceled = true)
    public static void onRenderPlayerPost(RenderPlayerEvent.Post event) {
        Player player = event.getEntity();
        if (!(player instanceof AbstractClientPlayer)) {
            return;
        }

        PlayerModel<?> model = event.getRenderer().getModel();

        // === RESTORE ARM VISIBILITY (only if we hid them) ===
        if (hiddenArmEntities.remove(player.getId())) {
            model.leftArm.visible = true;
            model.rightArm.visible = true;
            model.leftSleeve.visible = true;
            model.rightSleeve.visible = true;
        }

        // === RESTORE WEARER LAYERS ===
        boolean[] savedLayers = storedLayers.remove(player.getId());
        if (savedLayers != null) {
            ClothesRenderHelper.restoreWearerLayers(model, savedLayers);
        }
    }

    /** Drop tracked state for an entity leaving the level. */
    public static void onEntityLeave(int entityId) {
        storedLayers.remove(entityId);
        hiddenArmEntities.remove(entityId);
    }

    /** Drop all tracked state; called on world unload. */
    public static void clearAll() {
        storedLayers.clear();
        hiddenArmEntities.clear();
    }
}
