package com.tiedup.remake.compat.wildfire.render;

import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.tiedup.remake.compat.wildfire.WildfireCompat;
import com.tiedup.remake.compat.wildfire.physics.NpcBreastPhysics;
import com.tiedup.remake.entities.EntityDamsel;
import com.tiedup.remake.entities.KidnapperItemSelector;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.v2.BodyRegionV2;
import com.wildfire.api.IGenderArmor;
import com.wildfire.main.Breasts;
import com.wildfire.main.GenderPlayer;
import com.wildfire.main.WildfireHelper;
import com.wildfire.main.config.GeneralClientConfig;
import com.wildfire.physics.BreastPhysics;
import com.wildfire.render.WildfireModelRenderer;
import com.wildfire.render.WildfireModelRenderer.BreastModelBox;
import com.wildfire.render.WildfireModelRenderer.OverlayModelBox;
import com.wildfire.render.WildfireModelRenderer.PositionTextureVertex;
import java.util.Locale;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.geom.EntityModelSet;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.RenderLayerParent;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.effect.MobEffectUtil;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ArmorItem;
import net.minecraft.world.item.ArmorMaterial;
import net.minecraft.world.item.DyeableLeatherItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.armortrim.ArmorTrim;
import net.minecraft.world.level.block.Blocks;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.commons.lang3.tuple.Pair;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;

/**
 * Adapted GenderLayer for EntityDamsel / EntityKidnapper.
 */
public class WildfireDamselLayer<
    T extends LivingEntity,
    M extends HumanoidModel<T>
> extends RenderLayer<T, M> {

    private final TextureAtlas armorTrimAtlas;
    private final RenderLayerParent<T, M> renderer;

    private BreastModelBox lBreast, rBreast;
    private final OverlayModelBox lBreastWear, rBreastWear;
    private final BreastModelBox lBoobArmor, rBoobArmor;

    private float preBreastSize = 0f;

    public WildfireDamselLayer(
        RenderLayerParent<T, M> renderer,
        EntityModelSet modelSet
    ) {
        super(renderer);
        this.renderer = renderer;

        this.armorTrimAtlas = Minecraft.getInstance()
            .getModelManager()
            .getAtlas(Sheets.ARMOR_TRIMS_SHEET);

        lBreast = new BreastModelBox(
            64,
            64,
            16,
            17,
            -4F,
            0.0F,
            0F,
            4,
            5,
            4,
            0.0F,
            false
        );
        rBreast = new BreastModelBox(
            64,
            64,
            20,
            17,
            0,
            0.0F,
            0F,
            4,
            5,
            4,
            0.0F,
            false
        );

        lBreastWear = new OverlayModelBox(
            true,
            64,
            64,
            17,
            34,
            -4F,
            0.0F,
            0F,
            4,
            5,
            3,
            0.0F,
            false
        );
        rBreastWear = new OverlayModelBox(
            false,
            64,
            64,
            21,
            34,
            0,
            0.0F,
            0F,
            4,
            5,
            3,
            0.0F,
            false
        );

        lBoobArmor = new BreastModelBox(
            64,
            32,
            16,
            17,
            -4F,
            0.0F,
            0F,
            4,
            5,
            3,
            0.0F,
            false
        );
        rBoobArmor = new BreastModelBox(
            64,
            32,
            20,
            17,
            0,
            0.0F,
            0F,
            4,
            5,
            3,
            0.0F,
            false
        );
    }

    // Helper to get armor resource
    public ResourceLocation getArmorResource(
        T entity,
        ItemStack stack,
        EquipmentSlot slot,
        @Nullable String type
    ) {
        ArmorItem item = (ArmorItem) stack.getItem();
        String texture = item.getMaterial().getName();
        String domain = "minecraft";
        int idx = texture.indexOf(':');
        if (idx != -1) {
            domain = texture.substring(0, idx);
            texture = texture.substring(idx + 1);
        }
        String s1 = String.format(
            Locale.ROOT,
            "%s:textures/models/armor/%s_layer_%d%s.png",
            domain,
            texture,
            (slot == EquipmentSlot.LEGS ? 2 : 1),
            type == null ? "" : String.format(Locale.ROOT, "_%s", type)
        );

        s1 = ForgeHooksClient.getArmorTexture(entity, stack, s1, slot, type);
        return ResourceLocation.tryParse(s1);
    }

    private static final ResourceLocation DEFAULT_TEXTURE =
        ResourceLocation.withDefaultNamespace(
            "textures/entity/player/wide/steve.png"
        );

    private ResourceLocation getEntityTexture(T entity) {
        // Use ISkinnedEntity interface if available (EntityDamsel/EntityKidnapper)
        if (
            entity instanceof com.tiedup.remake.entities.ISkinnedEntity skinned
        ) {
            ResourceLocation tex = skinned.getSkinTexture();
            if (tex != null) return tex;
        }

        // Fallback to renderer
        if (
            this.renderer instanceof
                net.minecraft.client.renderer.entity.EntityRenderer
        ) {
            ResourceLocation rendererTex = (
                (net.minecraft.client.renderer.entity.EntityRenderer<
                    T
                >) this.renderer
            ).getTextureLocation(entity);
            if (rendererTex != null) return rendererTex;
        }

        return DEFAULT_TEXTURE;
    }

    @Override
    public void render(
        @Nonnull PoseStack matrixStack,
        @Nonnull MultiBufferSource bufferSource,
        int packedLightIn,
        @Nonnull T ent,
        float limbAngle,
        float limbDistance,
        float partialTicks,
        float animationProgress,
        float headYaw,
        float headPitch
    ) {
        // FIX: Check if Wildfire config is initialized before accessing it
        if (GeneralClientConfig.INSTANCE == null) {
            return; // Config not loaded yet, skip rendering
        }

        if (
            GeneralClientConfig.INSTANCE.disableRendering.get() ||
            ent.isSpectator()
        ) {
            return;
        }

        try {
            GenderPlayer plr = WildfireCompat.getGenderInfo(ent);
            if (plr == null) {
                return;
            }

            ItemStack armorStack = ent.getItemBySlot(EquipmentSlot.CHEST);
            IGenderArmor genderArmor = WildfireHelper.getArmorConfig(
                armorStack
            );
            boolean isChestplateOccupied = genderArmor.coversBreasts();

            if (
                genderArmor.alwaysHidesBreasts() ||
                (!plr.showBreastsInArmor() && isChestplateOccupied)
            ) {
                return;
            }

            HumanoidModel<T> model = this.getParentModel();

            if (!plr.getGender().canHaveBreasts()) {
                return;
            }

            Breasts breasts = plr.getBreasts();
            float breastOffsetX =
                Math.round(
                    (Math.round(breasts.getXOffset() * 100f) / 100f) * 10
                ) /
                10f;
            float breastOffsetY =
                -Math.round(
                    (Math.round(breasts.getYOffset() * 100f) / 100f) * 10
                ) /
                10f;
            float breastOffsetZ =
                -Math.round(
                    (Math.round(breasts.getZOffset() * 100f) / 100f) * 10
                ) /
                10f;

            BreastPhysics leftBreastPhysics = plr.getLeftBreastPhysics();

            // Physics Handling
            float bSize;
            float lTotal = 0,
                lTotalX = 0,
                leftBounceRotation = 0;
            float rTotal = 0,
                rTotalX = 0,
                rightBounceRotation = 0;

            if (ent instanceof net.minecraft.world.entity.player.Player) {
                // For Players, use original physics
                bSize = leftBreastPhysics.getBreastSize(partialTicks);

                lTotal = Mth.lerp(
                    partialTicks,
                    leftBreastPhysics.getPreBounceY(),
                    leftBreastPhysics.getBounceY()
                );
                lTotalX = Mth.lerp(
                    partialTicks,
                    leftBreastPhysics.getPreBounceX(),
                    leftBreastPhysics.getBounceX()
                );
                leftBounceRotation = Mth.lerp(
                    partialTicks,
                    leftBreastPhysics.getPreBounceRotation(),
                    leftBreastPhysics.getBounceRotation()
                );

                if (breasts.isUniboob()) {
                    rTotal = lTotal;
                    rTotalX = lTotalX;
                    rightBounceRotation = leftBounceRotation;
                } else {
                    BreastPhysics rightBreastPhysics =
                        plr.getRightBreastPhysics();
                    rTotal = Mth.lerp(
                        partialTicks,
                        rightBreastPhysics.getPreBounceY(),
                        rightBreastPhysics.getBounceY()
                    );
                    rTotalX = Mth.lerp(
                        partialTicks,
                        rightBreastPhysics.getPreBounceX(),
                        rightBreastPhysics.getBounceX()
                    );
                    rightBounceRotation = Mth.lerp(
                        partialTicks,
                        rightBreastPhysics.getPreBounceRotation(),
                        rightBreastPhysics.getBounceRotation()
                    );
                }
            } else {
                // For NPCs, use local physics simulation
                Pair<NpcBreastPhysics, NpcBreastPhysics> physics =
                    WildfireCompat.getNpcPhysics(ent, plr);
                if (physics != null) {
                    NpcBreastPhysics left = physics.getLeft();
                    NpcBreastPhysics right = physics.getRight();

                    // Update physics (ideally should be done in tick, but render update is acceptable for visual bounce)
                    // Note: This makes physics frame-rate dependent, but avoids complex tick handler setup
                    left.update(ent, genderArmor);
                    if (!breasts.isUniboob()) {
                        right.update(ent, genderArmor);
                    }

                    bSize = left.getBreastSize(partialTicks);

                    lTotal = Mth.lerp(
                        partialTicks,
                        left.getPreBounceY(),
                        left.getBounceY()
                    );
                    lTotalX = Mth.lerp(
                        partialTicks,
                        left.getPreBounceX(),
                        left.getBounceX()
                    );
                    leftBounceRotation = Mth.lerp(
                        partialTicks,
                        left.getPreBounceRotation(),
                        left.getBounceRotation()
                    );

                    if (breasts.isUniboob()) {
                        rTotal = lTotal;
                        rTotalX = lTotalX;
                        rightBounceRotation = leftBounceRotation;
                    } else {
                        rTotal = Mth.lerp(
                            partialTicks,
                            right.getPreBounceY(),
                            right.getBounceY()
                        );
                        rTotalX = Mth.lerp(
                            partialTicks,
                            right.getPreBounceX(),
                            right.getBounceX()
                        );
                        rightBounceRotation = Mth.lerp(
                            partialTicks,
                            right.getPreBounceRotation(),
                            right.getBounceRotation()
                        );
                    }
                } else {
                    bSize = plr.getBustSize(); // Fallback if physics creation fails
                }
            }

            float outwardAngle =
                (Math.round(breasts.getCleavage() * 100f) / 100f) * 100f;
            outwardAngle = Math.min(outwardAngle, 10);

            float reducer = 0;
            if (bSize < 0.84f) reducer++;
            if (bSize < 0.72f) reducer++;

            if (preBreastSize != bSize) {
                lBreast = new BreastModelBox(
                    64,
                    64,
                    16,
                    17,
                    -4F,
                    0.0F,
                    0F,
                    4,
                    5,
                    (int) (4 - breastOffsetZ - reducer),
                    0.0F,
                    false
                );
                rBreast = new BreastModelBox(
                    64,
                    64,
                    20,
                    17,
                    0,
                    0.0F,
                    0F,
                    4,
                    5,
                    (int) (4 - breastOffsetZ - reducer),
                    0.0F,
                    false
                );
                preBreastSize = bSize;
            }

            float overlayAlpha = ent.isInvisible() ? 0.15F : 1;
            RenderSystem.setShaderColor(1f, 1f, 1f, 1f);

            // Physics calculation handled above
            float breastSize = bSize * 1.5f;
            if (breastSize > 0.7f) breastSize = 0.7f;
            if (bSize > 0.7f) {
                breastSize = bSize;
            }

            if (breastSize < 0.02f) return;

            float zOff = 0.0625f - (bSize * 0.0625f);
            breastSize = bSize + 0.5f * Math.abs(bSize - 0.7f) * 2f;

            float resistance = plr.getArmorPhysicsOverride()
                ? 0
                : Mth.clamp(genderArmor.physicsResistance(), 0, 1);

            boolean breathingAnimation =
                resistance <= 0.5F &&
                (!ent.isUnderWater() ||
                    MobEffectUtil.hasWaterBreathing(ent) ||
                    ent
                        .level()
                        .getBlockState(
                            BlockPos.containing(
                                ent.getX(),
                                ent.getEyeY(),
                                ent.getZ()
                            )
                        )
                        .is(Blocks.BUBBLE_COLUMN));
            boolean bounceEnabled =
                plr.hasBreastPhysics() &&
                (!isChestplateOccupied || resistance < 1);

            int combineTex = LivingEntityRenderer.getOverlayCoords(ent, 0);
            ResourceLocation entityTexture = getEntityTexture(ent);

            RenderType type;
            boolean bodyVisible = !ent.isInvisible();
            if (bodyVisible) {
                type = RenderType.entityTranslucent(entityTexture);
            } else {
                if (!isChestplateOccupied) return;
                type = null;
            }

            renderBreastWithTransforms(
                ent,
                model.body,
                armorStack,
                matrixStack,
                bufferSource,
                type,
                packedLightIn,
                combineTex,
                overlayAlpha,
                bounceEnabled,
                lTotalX,
                lTotal,
                leftBounceRotation,
                breastSize,
                breastOffsetX,
                breastOffsetY,
                breastOffsetZ,
                zOff,
                outwardAngle,
                breasts.isUniboob(),
                isChestplateOccupied,
                breathingAnimation,
                true
            );
            renderBreastWithTransforms(
                ent,
                model.body,
                armorStack,
                matrixStack,
                bufferSource,
                type,
                packedLightIn,
                combineTex,
                overlayAlpha,
                bounceEnabled,
                rTotalX,
                rTotal,
                rightBounceRotation,
                breastSize,
                -breastOffsetX,
                breastOffsetY,
                breastOffsetZ,
                zOff,
                -outwardAngle,
                breasts.isUniboob(),
                isChestplateOccupied,
                breathingAnimation,
                false
            );

            RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
        } catch (Exception e) {
            com.tiedup.remake.core.TiedUpMod.LOGGER.debug(
                "[WildfireDamselLayer] Failed to render gender layer",
                e
            );
        }
    }

    private void renderBreastWithTransforms(
        T entity,
        ModelPart body,
        ItemStack armorStack,
        PoseStack matrixStack,
        MultiBufferSource bufferSource,
        @Nullable RenderType breastRenderType,
        int packedLightIn,
        int combineTex,
        float alpha,
        boolean bounceEnabled,
        float totalX,
        float total,
        float bounceRotation,
        float breastSize,
        float breastOffsetX,
        float breastOffsetY,
        float breastOffsetZ,
        float zOff,
        float outwardAngle,
        boolean uniboob,
        boolean isChestplateOccupied,
        boolean breathingAnimation,
        boolean left
    ) {
        matrixStack.pushPose();
        try {
            // Transform to Body
            matrixStack.translate(
                body.x * 0.0625f,
                body.y * 0.0625f,
                body.z * 0.0625f
            );
            if (body.zRot != 0.0F) {
                matrixStack.mulPose(
                    new Quaternionf().rotationXYZ(0f, 0f, body.zRot)
                );
            }
            if (body.yRot != 0.0F) {
                matrixStack.mulPose(
                    new Quaternionf().rotationXYZ(0f, body.yRot, 0f)
                );
            }
            if (body.xRot != 0.0F) {
                matrixStack.mulPose(
                    new Quaternionf().rotationXYZ(body.xRot, 0f, 0f)
                );
            }

            // --- Bouncing & Positioning logic same as original ---
            if (bounceEnabled) {
                matrixStack.translate(totalX / 32f, 0, 0);
                matrixStack.translate(0, total / 32f, 0);
            }

            matrixStack.translate(
                breastOffsetX * 0.0625f,
                0.05625f + (breastOffsetY * 0.0625f),
                zOff - 0.0625f * 2f + (breastOffsetZ * 0.0625f)
            );

            if (!uniboob) {
                matrixStack.translate(-0.0625f * 2 * (left ? 1 : -1), 0, 0);
            }
            if (bounceEnabled) {
                matrixStack.mulPose(
                    new Quaternionf().rotationXYZ(
                        0,
                        (float) (bounceRotation * (Math.PI / 180f)),
                        0
                    )
                );
            }
            if (!uniboob) {
                matrixStack.translate(0.0625f * 2 * (left ? 1 : -1), 0, 0);
            }

            float rotationMultiplier = 0;
            if (bounceEnabled) {
                matrixStack.translate(0, -0.035f * breastSize, 0);
                rotationMultiplier = -total / 12f;
            }
            float totalRotation = breastSize + rotationMultiplier;
            if (!bounceEnabled) {
                totalRotation = breastSize;
            }
            if (totalRotation > breastSize + 0.2F) {
                totalRotation = breastSize + 0.2F;
            }
            totalRotation = Math.min(totalRotation, 1);

            if (isChestplateOccupied) {
                matrixStack.translate(0, 0, 0.01f);
            }

            matrixStack.mulPose(
                new Quaternionf().rotationXYZ(
                    0,
                    (float) (outwardAngle * (Math.PI / 180f)),
                    0
                )
            );
            matrixStack.mulPose(
                new Quaternionf().rotationXYZ(
                    (float) (-35f * totalRotation * (Math.PI / 180f)),
                    0,
                    0
                )
            );

            if (breathingAnimation) {
                float f5 = -Mth.cos(entity.tickCount * 0.09F) * 0.45F + 0.45F;
                matrixStack.mulPose(
                    new Quaternionf().rotationXYZ(
                        (float) (f5 * (Math.PI / 180f)),
                        0,
                        0
                    )
                );
            }

            matrixStack.scale(0.9995f, 1f, 1f);

            renderBreast(
                entity,
                armorStack,
                matrixStack,
                bufferSource,
                breastRenderType,
                packedLightIn,
                combineTex,
                alpha,
                left
            );
        } catch (Exception e) {
            com.tiedup.remake.core.TiedUpMod.LOGGER.error(
                "[WildfireDamselLayer] Render error",
                e
            );
        }
        matrixStack.popPose();
    }

    private void renderBreast(
        T entity,
        ItemStack armorStack,
        PoseStack matrixStack,
        MultiBufferSource bufferSource,
        @Nullable RenderType breastRenderType,
        int packedLightIn,
        int packedOverlayIn,
        float alpha,
        boolean left
    ) {
        if (breastRenderType != null) {
            VertexConsumer vertexConsumer = bufferSource.getBuffer(
                breastRenderType
            );
            renderBox(
                left ? lBreast : rBreast,
                matrixStack,
                vertexConsumer,
                packedLightIn,
                packedOverlayIn,
                1F,
                1F,
                1F,
                alpha
            );

            // Render Wear (jacket overlay layer) - NPC skins are 64x64 and include this area
            matrixStack.translate(0, 0, -0.015f);
            matrixStack.scale(1.05f, 1.05f, 1.05f);
            renderBox(
                left ? lBreastWear : rBreastWear,
                matrixStack,
                vertexConsumer,
                packedLightIn,
                packedOverlayIn,
                1F,
                1F,
                1F,
                alpha
            );
        }

        // Armor Rendering
        if (
            !armorStack.isEmpty() &&
            armorStack.getItem() instanceof ArmorItem armorItem
        ) {
            ResourceLocation armorTexture = getArmorResource(
                entity,
                armorStack,
                EquipmentSlot.CHEST,
                null
            );
            ResourceLocation overlayTexture = null;
            float armorR = 1f;
            float armorG = 1f;
            float armorB = 1f;
            if (armorItem instanceof DyeableLeatherItem dyeableItem) {
                overlayTexture = getArmorResource(
                    entity,
                    armorStack,
                    EquipmentSlot.CHEST,
                    "overlay"
                );
                int color = dyeableItem.getColor(armorStack);
                armorR = (float) ((color >> 16) & 255) / 255.0F;
                armorG = (float) ((color >> 8) & 255) / 255.0F;
                armorB = (float) (color & 255) / 255.0F;
            }
            matrixStack.pushPose();
            matrixStack.translate(left ? 0.001f : -0.001f, 0.015f, -0.015f);
            matrixStack.scale(1.05f, 1, 1);
            WildfireModelRenderer.BreastModelBox armor = left
                ? lBoobArmor
                : rBoobArmor;
            RenderType armorType = RenderType.armorCutoutNoCull(armorTexture);
            VertexConsumer armorVertexConsumer = bufferSource.getBuffer(
                armorType
            );
            renderBox(
                armor,
                matrixStack,
                armorVertexConsumer,
                packedLightIn,
                OverlayTexture.NO_OVERLAY,
                armorR,
                armorG,
                armorB,
                1
            );
            if (overlayTexture != null) {
                RenderType overlayType = RenderType.armorCutoutNoCull(
                    overlayTexture
                );
                VertexConsumer overlayVertexConsumer = bufferSource.getBuffer(
                    overlayType
                );
                renderBox(
                    armor,
                    matrixStack,
                    overlayVertexConsumer,
                    packedLightIn,
                    OverlayTexture.NO_OVERLAY,
                    1,
                    1,
                    1,
                    1
                );
            }

            ArmorTrim.getTrim(
                entity.level().registryAccess(),
                armorStack
            ).ifPresent(trim -> {
                ArmorMaterial armorMaterial = armorItem.getMaterial();
                TextureAtlasSprite sprite = this.armorTrimAtlas.getSprite(
                    trim.outerTexture(armorMaterial)
                );
                VertexConsumer trimVertexConsumer = sprite.wrap(
                    bufferSource.getBuffer(Sheets.armorTrimsSheet())
                );
                renderBox(
                    armor,
                    matrixStack,
                    trimVertexConsumer,
                    packedLightIn,
                    OverlayTexture.NO_OVERLAY,
                    1,
                    1,
                    1,
                    1
                );
            });

            if (armorStack.hasFoil()) {
                renderBox(
                    armor,
                    matrixStack,
                    bufferSource.getBuffer(RenderType.armorEntityGlint()),
                    packedLightIn,
                    OverlayTexture.NO_OVERLAY,
                    1,
                    1,
                    1,
                    1
                );
            }

            matrixStack.popPose();
        }

        // Bondage Item Rendering - render bind texture on breasts
        ItemStack bindStack = getBondageBindItem(entity);
        if (!bindStack.isEmpty()) {
            ResourceLocation bindTexture = getBindTextureForItem(bindStack);
            if (bindTexture != null) {
                matrixStack.pushPose();
                matrixStack.translate(left ? 0.001f : -0.001f, 0.015f, -0.025f);
                matrixStack.scale(1.08f, 1.02f, 1.02f);
                WildfireModelRenderer.BreastModelBox bondageBox = left
                    ? lBoobArmor
                    : rBoobArmor;
                RenderType bindType = RenderType.entityCutoutNoCull(
                    bindTexture
                );
                VertexConsumer bindVertexConsumer = bufferSource.getBuffer(
                    bindType
                );
                renderBox(
                    bondageBox,
                    matrixStack,
                    bindVertexConsumer,
                    packedLightIn,
                    OverlayTexture.NO_OVERLAY,
                    1,
                    1,
                    1,
                    1
                );
                matrixStack.popPose();
            }
        }
    }

    /**
     * Get the bondage bind item from an entity.
     * Works for both NPCs (EntityDamsel) and Players (via IBondageState).
     */
    private ItemStack getBondageBindItem(T entity) {
        // For NPCs (AbstractTiedUpNpc and subclasses)
        if (
            entity instanceof com.tiedup.remake.entities.AbstractTiedUpNpc npc
        ) {
            return npc.getEquipment(BodyRegionV2.ARMS);
        }

        // For Players (via capability)
        if (entity instanceof net.minecraft.world.entity.player.Player player) {
            IBondageState state = KidnappedHelper.getKidnappedState(player);
            if (state != null) {
                return state.getEquipment(BodyRegionV2.ARMS);
            }
        }

        return ItemStack.EMPTY;
    }

    /**
     * Get the texture for a bondage bind item.
     * Uses same path logic as DamselBondageLayer.
     */
    @Nullable
    private ResourceLocation getBindTextureForItem(ItemStack stack) {
        if (stack.isEmpty()) {
            return null;
        }

        ResourceLocation itemId = ForgeRegistries.ITEMS.getKey(stack.getItem());
        if (itemId == null) {
            return null;
        }

        String itemName = itemId.getPath();

        // V2 items use GLB models, not texture subfolders — fallback to "binds"
        String subfolder = "binds";

        // Get color suffix from NBT (e.g., "_red", "_blue", or "" if no color)
        String colorSuffix = KidnapperItemSelector.getColorSuffix(stack);

        return ResourceLocation.fromNamespaceAndPath(
            "tiedup",
            "textures/models/bondage/" +
                subfolder +
                "/" +
                itemName +
                colorSuffix +
                ".png"
        );
    }

    private static void renderBox(
        WildfireModelRenderer.ModelBox model,
        PoseStack matrixStack,
        VertexConsumer bufferIn,
        int packedLightIn,
        int packedOverlayIn,
        float red,
        float green,
        float blue,
        float alpha
    ) {
        Matrix4f matrix4f = matrixStack.last().pose();
        Matrix3f matrix3f = matrixStack.last().normal();
        for (WildfireModelRenderer.TexturedQuad quad : model.quads) {
            Vector3f vector3f = new Vector3f(
                quad.normal.getX(),
                quad.normal.getY(),
                quad.normal.getZ()
            );
            vector3f.mul(matrix3f);
            for (PositionTextureVertex vertex : quad.vertexPositions) {
                bufferIn.vertex(
                    matrix4f,
                    vertex.x() / 16.0F,
                    vertex.y() / 16.0F,
                    vertex.z() / 16.0F
                );
                bufferIn.color(red, green, blue, alpha);
                bufferIn.uv(
                    vertex.texturePositionX(),
                    vertex.texturePositionY()
                );
                bufferIn.overlayCoords(packedOverlayIn);
                bufferIn.uv2(packedLightIn);
                bufferIn.normal(vector3f.x(), vector3f.y(), vector3f.z());
                bufferIn.endVertex();
            }
        }
    }
}
