package com.tiedup.remake.mixin.client;

import com.tiedup.remake.client.animation.BondageAnimationManager;
import com.tiedup.remake.client.animation.StaticPoseApplier;
import com.tiedup.remake.client.animation.util.AnimationIdBuilder;
import com.tiedup.remake.compat.mca.MCACompat;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.v2.bondage.BindModeHelper;
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import dev.kosmx.playerAnim.impl.IAnimatedPlayer;
import dev.kosmx.playerAnim.impl.animation.AnimationApplier;
import java.util.UUID;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

/**
 * Mixin for MCA's VillagerEntityBaseModelMCA to apply tied poses.
 *
 * <p>This mixin injects at the end of setupAnim (m_6973_) to override MCA's default
 * animations when a villager is tied up. Without this, the bondage render layer
 * shows the tied pose but the underlying MCA model still shows normal walking/idle
 * animations.
 *
 * <p>Uses @Pseudo annotation - mixin is optional and will be skipped if MCA is not installed.
 *
 * <p>Target class: net.mca.client.model.VillagerEntityBaseModelMCA
 * <p>Target method: setupAnim (MCP name, remapped from Yarn by Architectury)
 */
@Pseudo
@Mixin(
    targets = "forge.net.mca.client.model.VillagerEntityBaseModelMCA",
    remap = false
)
public class MixinVillagerEntityBaseModelMCA<T extends LivingEntity> {

    // Note: Tick tracking moved to MCAAnimationTickCache for cleanup on world unload

    /**
     * Inject at the end of setupAnim to apply tied pose after MCA has set its animations.
     *
     * <p>This completely overrides arm/leg positions when the villager is tied up.
     *
     * <p>Method signature: void setupAnim(T entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch)
     * <p>Note: MCA uses Architectury which remaps Yarn's method names to Forge/MCP names
     */
    @Inject(method = "m_6973_", at = @At("TAIL"), remap = false)
    private void tiedup$applyTiedPose(
        T villager,
        float limbSwing,
        float limbSwingAmount,
        float ageInTicks,
        float netHeadYaw,
        float headPitch,
        CallbackInfo ci
    ) {
        // Only process on client side
        if (villager.level() == null || !villager.level().isClientSide()) {
            return;
        }

        // Check if MCA is loaded and this villager is tied
        if (!MCACompat.isMCALoaded()) {
            return;
        }

        IBondageState state = MCACompat.getKidnappedState(villager);
        if (state == null || !state.isTiedUp()) {
            return;
        }

        // Get pose info from bind item
        ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
        PoseType poseType = PoseTypeHelper.getPoseType(bind);

        // Derive bound state from V2 regions, fallback to V1 bind mode NBT
        boolean armsBound = V2EquipmentHelper.isRegionOccupied(
            villager,
            BodyRegionV2.ARMS
        );
        boolean legsBound = V2EquipmentHelper.isRegionOccupied(
            villager,
            BodyRegionV2.LEGS
        );

        if (!armsBound && !legsBound && BindModeHelper.isBindItem(bind)) {
            armsBound = BindModeHelper.hasArmsBound(bind);
            legsBound = BindModeHelper.hasLegsBound(bind);
        }

        // MCA doesn't track struggling state - use false for now
        // TODO: Add struggling support to MCA integration
        boolean isStruggling = false;

        // Cast this mixin to HumanoidModel to apply pose
        // MCA's VillagerEntityBaseModelMCA extends HumanoidModel
        @SuppressWarnings("unchecked")
        HumanoidModel<?> model = (HumanoidModel<?>) (Object) this;

        // Check if villager supports PlayerAnimator (via our mixin)
        if (villager instanceof IAnimatedPlayer animated) {
            // Build animation ID and play animation
            String animId = AnimationIdBuilder.build(
                poseType,
                armsBound,
                legsBound,
                null,
                isStruggling,
                true
            );
            BondageAnimationManager.playAnimation(villager, animId);

            // Dedup: tick the animation stack once per game tick, not per
            // render frame. Use tickCount (discrete server tick), not the
            // partial-tick-interpolated ageInTicks.
            int currentTick = villager.tickCount;
            UUID entityId = villager.getUUID();
            int lastTick =
                com.tiedup.remake.client.animation.tick.MCAAnimationTickCache.getLastTick(
                    entityId
                );

            if (lastTick != currentTick) {
                // New game tick - tick the animation
                animated.getAnimationStack().tick();
                com.tiedup.remake.client.animation.tick.MCAAnimationTickCache.setLastTick(
                    entityId,
                    currentTick
                );
            }

            // Apply animation transforms to model parts
            AnimationApplier emote = animated.playerAnimator_getAnimation();
            if (emote != null && emote.isActive()) {
                // Use correct PlayerAnimator part names (torso, not body)
                emote.updatePart("head", model.head);
                emote.updatePart("torso", model.body);
                emote.updatePart("leftArm", model.leftArm);
                emote.updatePart("rightArm", model.rightArm);
                emote.updatePart("leftLeg", model.leftLeg);
                emote.updatePart("rightLeg", model.rightLeg);

                // Force rotations using setRotation to ensure they're applied
                model.rightArm.setRotation(
                    model.rightArm.xRot,
                    model.rightArm.yRot,
                    model.rightArm.zRot
                );
                model.leftArm.setRotation(
                    model.leftArm.xRot,
                    model.leftArm.yRot,
                    model.leftArm.zRot
                );
                model.rightLeg.setRotation(
                    model.rightLeg.xRot,
                    model.rightLeg.yRot,
                    model.rightLeg.zRot
                );
                model.leftLeg.setRotation(
                    model.leftLeg.xRot,
                    model.leftLeg.yRot,
                    model.leftLeg.zRot
                );
                model.body.setRotation(
                    model.body.xRot,
                    model.body.yRot,
                    model.body.zRot
                );
                model.head.setRotation(
                    model.head.xRot,
                    model.head.yRot,
                    model.head.zRot
                );
            } else {
                // Fallback to static poses if animation not active
                StaticPoseApplier.applyStaticPose(
                    model,
                    poseType,
                    armsBound,
                    legsBound
                );
            }
        } else {
            // Fallback: entity doesn't support PlayerAnimator, use static poses
            StaticPoseApplier.applyStaticPose(
                model,
                poseType,
                armsBound,
                legsBound
            );
        }

        // Hide arms for WRAP/LATEX_SACK poses (like DamselModel does)
        if (poseType == PoseType.WRAP || poseType == PoseType.LATEX_SACK) {
            model.leftArm.visible = false;
            model.rightArm.visible = false;
        }
    }
}
