package com.tiedup.remake.v2.bondage.movement;

import com.tiedup.remake.items.base.ItemBind;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
import java.util.Map;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/**
 * Resolves the winning movement style from a player's equipped bondage items.
 *
 * <p>Shared class (client + server). Deterministic: same items produce the same result.
 * The highest-severity style wins. Tiebreaker: lowest {@link BodyRegionV2#ordinal()}.</p>
 *
 * <p>The winning item's {@link MovementModifier} (if present) overrides the style's
 * default speed/jump values. Modifiers from lower-severity items are ignored.</p>
 *
 * <h3>V1 Compatibility (H6 fix)</h3>
 * <p>V1 items ({@link ItemBind}) stored in V2 capability
 * do not have data-driven definitions. This resolver provides a fallback that
 * maps V1 bind mode + pose type to a {@link MovementStyle} with speed values matching
 * the original V1 behavior, preventing double stacking between the legacy
 * {@code RestraintEffectUtils} attribute modifier and the V2 modifier.</p>
 */
public final class MovementStyleResolver {

    private MovementStyleResolver() {}

    // --- V1 fallback speed values ---
    // V1 used ADDITION(-0.09) on base 0.10 = 0.01 effective = 10% speed
    // Expressed as MULTIPLY_BASE fraction: 0.10
    private static final float V1_STANDARD_SPEED = 0.10f;

    // V1 used ADDITION(-0.10) on base 0.10 = 0.00 effective = 0% speed
    // Expressed as MULTIPLY_BASE fraction: 0.0 (fully immobile)
    private static final float V1_IMMOBILIZED_SPEED = 0.0f;

    /**
     * Resolve the winning movement style from all equipped items.
     *
     * <p>Checks V2 data-driven definitions first, then falls back to V1 {@link ItemBind}
     * introspection for items without data-driven definitions.</p>
     *
     * @param equipped map of region to ItemStack (from {@code IV2BondageEquipment.getAllEquipped()})
     * @return the resolved movement, or {@link ResolvedMovement#NONE} if no styled items
     */
    public static ResolvedMovement resolve(
        Map<BodyRegionV2, ItemStack> equipped
    ) {
        if (equipped == null || equipped.isEmpty()) {
            return ResolvedMovement.NONE;
        }

        MovementStyle bestStyle = null;
        float bestSpeed = 1.0f;
        boolean bestJumpDisabled = false;
        int bestSeverity = -1;
        int bestRegionOrdinal = Integer.MAX_VALUE;

        for (Map.Entry<BodyRegionV2, ItemStack> entry : equipped.entrySet()) {
            BodyRegionV2 region = entry.getKey();
            ItemStack stack = entry.getValue();
            if (stack.isEmpty()) continue;

            // --- Try V2 data-driven definition first ---
            DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
            if (def != null && def.movementStyle() != null) {
                MovementStyle style = def.movementStyle();
                int severity = style.getSeverity();
                int regionOrdinal = region.ordinal();

                if (
                    severity > bestSeverity ||
                    (severity == bestSeverity &&
                        regionOrdinal < bestRegionOrdinal)
                ) {
                    bestStyle = style;
                    MovementModifier mod = def.movementModifier();
                    bestSpeed = (mod != null && mod.speedMultiplier() != null)
                        ? mod.speedMultiplier()
                        : style.getDefaultSpeedMultiplier();
                    bestJumpDisabled = (mod != null &&
                        mod.jumpDisabled() != null)
                        ? mod.jumpDisabled()
                        : style.isDefaultJumpDisabled();
                    bestSeverity = severity;
                    bestRegionOrdinal = regionOrdinal;
                }
                continue;
            }

            // --- V1 fallback: ItemBind without data-driven definition ---
            V1Fallback fallback = resolveV1Fallback(stack);
            if (fallback != null) {
                int severity = fallback.style.getSeverity();
                int regionOrdinal = region.ordinal();

                if (
                    severity > bestSeverity ||
                    (severity == bestSeverity &&
                        regionOrdinal < bestRegionOrdinal)
                ) {
                    bestStyle = fallback.style;
                    bestSpeed = fallback.speed;
                    bestJumpDisabled = fallback.jumpDisabled;
                    bestSeverity = severity;
                    bestRegionOrdinal = regionOrdinal;
                }
            }
        }

        if (bestStyle == null) {
            return ResolvedMovement.NONE;
        }

        return new ResolvedMovement(bestStyle, bestSpeed, bestJumpDisabled);
    }

    // ==================== V1 Fallback ====================

    /**
     * Attempt to derive a movement style from a V1 {@link ItemBind} item.
     *
     * <p>Only items with legs bound produce a movement style. The mapping preserves
     * the original V1 speed values:</p>
     * <ul>
     *   <li>WRAP / LATEX_SACK: SHUFFLE at 0% speed (full immobilization), jump disabled</li>
     *   <li>DOG / HUMAN_CHAIR: CRAWL at V1 standard speed (10%), jump disabled</li>
     *   <li>STANDARD / STRAITJACKET: SHUFFLE at 10% speed, jump disabled</li>
     * </ul>
     *
     * @param stack the ItemStack to inspect
     * @return fallback resolution, or null if the item is not a V1 bind or legs are not bound
     */
    @Nullable
    private static V1Fallback resolveV1Fallback(ItemStack stack) {
        if (!(stack.getItem() instanceof ItemBind bindItem)) {
            return null;
        }

        if (!ItemBind.hasLegsBound(stack)) {
            return null;
        }

        PoseType poseType = bindItem.getPoseType();

        return switch (poseType) {
            case WRAP, LATEX_SACK -> new V1Fallback(
                MovementStyle.SHUFFLE,
                V1_IMMOBILIZED_SPEED,
                true
            );
            case DOG, HUMAN_CHAIR -> new V1Fallback(
                MovementStyle.CRAWL,
                V1_STANDARD_SPEED,
                true
            );
            default ->
                // STANDARD, STRAITJACKET: shuffle at V1 standard speed
                new V1Fallback(MovementStyle.SHUFFLE, V1_STANDARD_SPEED, true);
        };
    }

    /** Internal holder for V1 fallback resolution result. */
    private record V1Fallback(
        MovementStyle style,
        float speed,
        boolean jumpDisabled
    ) {}
}
