package com.tiedup.remake.entities.damsel.components;

import com.tiedup.remake.util.RotationSmoother;
import dev.kosmx.playerAnim.api.layered.AnimationStack;
import dev.kosmx.playerAnim.api.layered.IAnimation;
import dev.kosmx.playerAnim.impl.animation.AnimationApplier;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;

/**
 * Manages all animation-related systems for EntityDamsel:
 * - IAnimatedPlayer implementation (PlayerAnimator mod)
 * - Animation stack management
 * - Pose management (sitting, kneeling, dog, struggling, trembling)
 * - Dog pose rotation smoothing
 *
 */
public class DamselAnimationController {

    private final IAnimationHost host;

    // ANIMATION SYSTEM

    /** Animation stack for PlayerAnimator mod */
    private final AnimationStack animationStack = new AnimationStack();

    /** Animation applier for model rendering */
    private final AnimationApplier animationApplier = new AnimationApplier(
        animationStack
    );

    /** Stored animations by ID */
    private final Map<ResourceLocation, IAnimation> storedAnimations =
        new HashMap<>();

    // DOG POSE ROTATION SMOOTHING

    /** Rotation smoother for DOG pose (prevents sudden spinning) */
    private final RotationSmoother dogPoseRotationSmoother =
        new RotationSmoother();

    /** Track DOG pose state for transition detection */
    private boolean wasInDogPose = false;

    // CONSTRUCTOR

    public DamselAnimationController(IAnimationHost host) {
        this.host = host;
    }

    // TICK LOGIC

    /**
     * Tick animation systems BEFORE super.tick().
     * Call this from EntityDamsel.tick() BEFORE super.tick().
     *
     * Handles:
     * - DOG pose transition detection (initializes smoother)
     */
    public void tickAnimationBeforeSuperTick() {
        // DOG pose: Detect transition to initialize smoother
        boolean inDogPose = host.isDogPose();
        if (inDogPose && !wasInDogPose) {
            // Just entered DOG pose - initialize smoother to current rotation
            this.dogPoseRotationSmoother.setCurrent(host.getYBodyRot());
        }
        this.wasInDogPose = inDogPose;
    }

    /**
     * Tick DOG pose rotation smoothing AFTER super.tick().
     * Call this from EntityDamsel.tick() AFTER super.tick().
     *
     * FIX: Must run AFTER super.tick() because:
     * 1. super.tick() calculates new yBodyRot based on movement direction
     * 2. We smooth that final calculated value
     * 3. If we smooth before, Minecraft overwrites our smoothed value
     *
     * FIX: Only set yBodyRot, not yBodyRotO:
     * - yBodyRotO is the "old" rotation from last tick, used for interpolation
     * - Setting both to the same value breaks inter-frame interpolation
     * - Minecraft's renderer uses lerp(yBodyRotO, yBodyRot, partialTicks)
     * - If both are equal, there's no interpolation = visual snapping
     */
    public void tickDogPoseRotationSmoothing() {
        if (!host.isDogPose()) {
            return;
        }

        // Get the target rotation (what Minecraft calculated based on movement)
        float targetRot = host.getYBodyRot();

        // Smooth towards target (0.15 = 15% per tick, faster but still smooth)
        float smoothedRot = this.dogPoseRotationSmoother.smooth(
            targetRot,
            0.15f
        );

        // Only set yBodyRot - let yBodyRotO be managed by Minecraft for interpolation
        host.setYBodyRot(smoothedRot);
    }

    /**
     * Tick animation stack (client-side only).
     * Call this from EntityDamsel.tick() AFTER tickAnimationBeforeSuperTick().
     *
     * @return true if animation tick was handled (client-side), false if server should continue
     */
    public boolean tickAnimationStack() {
        // Client-side: tick animation stack
        if (host.level().isClientSide) {
            this.animationStack.tick();
            return true; // Signal to caller to return early
        }
        return false; // Server-side, continue with tick logic
    }

    // IANIMATEDPLAYER INTERFACE

    /**
     * Get the animation stack.
     * Required by IAnimatedPlayer interface.
     *
     * @return AnimationStack for this entity
     */
    public AnimationStack getAnimationStack() {
        return this.animationStack;
    }

    /**
     * Get the animation applier for model rendering.
     * Required by IAnimatedPlayer interface.
     *
     * <p>The AnimationApplier is used by DamselModel.setupAnim() to apply
     * current animation transforms to model parts via emote.updatePart().
     *
     * @return AnimationApplier for this entity
     */
    public AnimationApplier playerAnimator_getAnimation() {
        return this.animationApplier;
    }

    /**
     * Get a stored animation by ID.
     * Required by IAnimatedPlayer interface.
     *
     * @param id Animation identifier
     * @return The stored animation, or null if not found
     */
    @Nullable
    public IAnimation playerAnimator_getAnimation(ResourceLocation id) {
        return this.storedAnimations.get(id);
    }

    /**
     * Store an animation by ID.
     * Required by IAnimatedPlayer interface.
     *
     * @param id Animation identifier
     * @param animation Animation to store, or null to remove
     * @return The previously stored animation, or null
     */
    @Nullable
    public IAnimation playerAnimator_setAnimation(
        ResourceLocation id,
        @Nullable IAnimation animation
    ) {
        if (animation == null) {
            return this.storedAnimations.remove(id);
        }
        return this.storedAnimations.put(id, animation);
    }

    // POSE MANAGEMENT

    /**
     * Check if NPC is currently sitting.
     * @return true if in sitting pose
     */
    public boolean isSitting() {
        return host.isSittingFromData();
    }

    /**
     * Set sitting pose state.
     * @param sitting true to enter sitting pose
     */
    public void setSitting(boolean sitting) {
        host.setSittingToData(sitting);
        // Clear kneeling if sitting
        if (sitting) {
            host.setKneelingToData(false);
        }
    }

    /**
     * Check if NPC is currently kneeling.
     * @return true if in kneeling pose
     */
    public boolean isKneeling() {
        return host.isKneelingFromData();
    }

    /**
     * Set kneeling pose state.
     * @param kneeling true to enter kneeling pose
     */
    public void setKneeling(boolean kneeling) {
        host.setKneelingToData(kneeling);
        // Clear sitting if kneeling
        if (kneeling) {
            host.setSittingToData(false);
        }
    }

    /**
     * Check if NPC is in any pose (sitting, kneeling, or dog).
     * @return true if in a pose
     */
    public boolean isInPose() {
        return this.isSitting() || this.isKneeling() || host.isDogPose();
    }

    /**
     * Check if NPC is in DOG pose (based on equipped bind).
     * @return true if equipped bind has DOG pose type
     */
    public boolean isDogPose() {
        return host.isDogPose();
    }

    /**
     * Check if NPC is currently struggling against restraints.
     * Used for animation sync.
     * @return true if struggling
     */
    public boolean isStruggling() {
        return host.isStrugglingFromData();
    }

    /**
     * Set struggling state for animation.
     * @param struggling true if starting struggle animation
     */
    public void setStruggling(boolean struggling) {
        host.setStrugglingToData(struggling);
    }
}
