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

import com.tiedup.remake.client.state.PetBedClientState;
import com.tiedup.remake.entities.AbstractTiedUpNpc;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.bondage.movement.MovementStyle;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;

/**
 * Resolves the current {@link AnimationContext} for players and NPCs based on their state.
 *
 * <p>This is a pure function with no side effects -- it reads entity state and returns
 * the appropriate animation context. The resolution priority is:
 * <ol>
 *   <li><b>Sitting</b> (pet bed for players, pose for NPCs) -- highest priority posture</li>
 *   <li><b>Kneeling</b> (NPCs only)</li>
 *   <li><b>Struggling</b> (standing struggle if not sitting/kneeling)</li>
 *   <li><b>Sneaking</b> (players only)</li>
 *   <li><b>Walking</b> (horizontal movement detected)</li>
 *   <li><b>Standing idle</b> (fallback)</li>
 * </ol>
 *
 * <p>For players, the "sitting" state is determined by the client-side pet bed cache
 * ({@link PetBedClientState}) rather than entity data, since pet bed state is not
 * synced via entity data accessors.</p>
 */
@OnlyIn(Dist.CLIENT)
public final class AnimationContextResolver {

    private AnimationContextResolver() {}

    /**
     * Resolve the animation context for a player based on their bind state and movement.
     *
     * <p>Priority chain:
     * <ol>
     *   <li>Sitting (pet bed/furniture) -- highest priority posture</li>
     *   <li>Struggling -- standing struggle if not sitting</li>
     *   <li>Movement style -- style-specific idle/walk based on movement</li>
     *   <li>Sneaking</li>
     *   <li>Walking</li>
     *   <li>Standing idle -- fallback</li>
     * </ol>
     *
     * @param player     the player entity (must not be null)
     * @param state      the player's bind state, or null if not bound
     * @param activeStyle the active movement style from client state, or null
     * @return the resolved animation context, never null
     */
    public static AnimationContext resolve(
        Player player,
        @Nullable PlayerBindState state,
        @Nullable MovementStyle activeStyle
    ) {
        boolean sitting = PetBedClientState.get(player.getUUID()) != 0;
        boolean struggling = state != null && state.isStruggling();
        boolean sneaking = player.isCrouching();
        boolean moving =
            player.getDeltaMovement().horizontalDistanceSqr() > 1.0E-6;

        if (sitting) {
            return struggling
                ? AnimationContext.SIT_STRUGGLE
                : AnimationContext.SIT_IDLE;
        }
        if (struggling) {
            return AnimationContext.STAND_STRUGGLE;
        }
        if (activeStyle != null) {
            return resolveStyleContext(activeStyle, moving);
        }
        if (sneaking) {
            return AnimationContext.STAND_SNEAK;
        }
        if (moving) {
            return AnimationContext.STAND_WALK;
        }
        return AnimationContext.STAND_IDLE;
    }

    /**
     * Map a movement style + moving flag to the appropriate AnimationContext.
     */
    private static AnimationContext resolveStyleContext(
        MovementStyle style,
        boolean moving
    ) {
        return switch (style) {
            case SHUFFLE -> moving
                ? AnimationContext.SHUFFLE_WALK
                : AnimationContext.SHUFFLE_IDLE;
            case HOP -> moving
                ? AnimationContext.HOP_WALK
                : AnimationContext.HOP_IDLE;
            case WADDLE -> moving
                ? AnimationContext.WADDLE_WALK
                : AnimationContext.WADDLE_IDLE;
            case CRAWL -> moving
                ? AnimationContext.CRAWL_MOVE
                : AnimationContext.CRAWL_IDLE;
        };
    }

    /**
     * Resolve the animation context for a Damsel NPC based on pose and movement.
     *
     * <p>Unlike players, NPCs support kneeling as a distinct posture and do not sneak.</p>
     *
     * @param entity the damsel entity (must not be null)
     * @return the resolved animation context, never null
     */
    public static AnimationContext resolveNpc(AbstractTiedUpNpc entity) {
        boolean sitting = entity.isSitting();
        boolean kneeling = entity.isKneeling();
        boolean struggling = entity.isStruggling();
        boolean moving =
            entity.getDeltaMovement().horizontalDistanceSqr() > 1.0E-6;

        if (sitting) {
            return struggling
                ? AnimationContext.SIT_STRUGGLE
                : AnimationContext.SIT_IDLE;
        }
        if (kneeling) {
            return struggling
                ? AnimationContext.KNEEL_STRUGGLE
                : AnimationContext.KNEEL_IDLE;
        }
        if (struggling) {
            return AnimationContext.STAND_STRUGGLE;
        }
        if (moving) {
            return AnimationContext.STAND_WALK;
        }
        return AnimationContext.STAND_IDLE;
    }
}
