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

import com.google.gson.JsonObject;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.component.ComponentType;
import java.util.Map;
import java.util.Set;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;

/**
 * Immutable definition for a data-driven bondage item.
 *
 * <p>Loaded from JSON files in {@code assets/<namespace>/tiedup_items/}.
 * Each definition describes the properties of a bondage item variant
 * that can be instantiated as an ItemStack with the {@code tiedup_item_id} NBT tag.</p>
 *
 * <p>All rendering and gameplay properties are read from this record at runtime
 * via {@link DataDrivenBondageItem}'s stack-aware method overrides.</p>
 */
public record DataDrivenItemDefinition(
    /** Unique identifier for this item definition (e.g., "tiedup:leather_armbinder"). */
    ResourceLocation id,

    /** Human-readable display name (fallback if no translation key). */
    String displayName,

    /** Optional translation key for localized display name. */
    @Nullable String translationKey,

    /** Resource location of the GLB model file. */
    ResourceLocation modelLocation,

    /** Optional slim (Alex-style) model variant. */
    @Nullable ResourceLocation slimModelLocation,

    /** Optional separate GLB for animations (shared template). */
    @Nullable ResourceLocation animationSource,

    /** Body regions this item occupies. Never empty. */
    Set<BodyRegionV2> occupiedRegions,

    /** Body regions this item blocks. Defaults to occupiedRegions if not specified. */
    Set<BodyRegionV2> blockedRegions,

    /** Optional pose type identifier (e.g., "STANDARD", "STRAITJACKET", "DOG"). */
    @Nullable String poseType,

    /** Pose priority for conflict resolution. Higher wins. */
    int posePriority,

    /** Escape difficulty for the struggle minigame. */
    int escapeDifficulty,

    /** Whether this item can be locked with a padlock. */
    boolean lockable,

    /** Whether a padlock can be attached to this item. False for organic items (slime, vine, web, tape). */
    boolean canAttachPadlock,

    /** Whether this item supports color variants. */
    boolean supportsColor,

    /** Default tint colors per channel (e.g. "tintable_0" -> 0x8B4513). Empty map if none. */
    Map<String, Integer> tintChannels,

    /**
     * Optional inventory icon model location (e.g., "tiedup:item/armbinder").
     *
     * <p>Points to a standard {@code item/generated} model JSON that will be used
     * as the inventory sprite for this data-driven item variant. When null, the
     * default {@code tiedup:item/data_driven_item} model is used.</p>
     */
    @Nullable ResourceLocation icon,

    /**
     * Optional movement style that changes how a bound player physically moves.
     * Determines server-side speed reduction, jump suppression, and client animation.
     */
    @Nullable com.tiedup.remake.v2.bondage.movement.MovementStyle movementStyle,

    /**
     * Optional per-item overrides for the movement style's default values.
     * Requires {@code movementStyle} to be non-null (ignored otherwise).
     */
    @Nullable
    com.tiedup.remake.v2.bondage.movement.MovementModifier movementModifier,

    /** Optional creator/author name displayed in the item tooltip. */
    @Nullable String creator,

    /**
     * Per-animation bone whitelist. Maps animation name (e.g. "idle", "struggle")
     * to the set of PlayerAnimator bone names this item is allowed to animate.
     *
     * <p>Valid bone names: head, body, rightArm, leftArm, rightLeg, leftLeg.</p>
     *
     * <p>At animation time, the effective parts for a given clip are computed as
     * {@code intersection(animationBones[clipName], ownedParts)}. If the clip name
     * is not present in this map (or null), the item falls back to its full
     * {@code ownedParts}.</p>
     *
     * <p>Optional in JSON. When absent, defaults to empty map (= all owned bones
     * enabled for all animations, no filtering).</p>
     */
    Map<String, Set<String>> animationBones,

    /**
     * Optional living-motion animation bindings (P3 rig system).
     *
     * <p>When non-null, the client rig equipment handler rebuilds the player's
     * living animation map with these bindings whenever this item is equipped.
     * When null, no animation bindings are applied (vanilla behavior).</p>
     *
     * <p>Distinct from {@link #animationBones} : {@code animationBones} is a
     * per-clip bone whitelist for the PlayerAnimator pipeline (V1-era),
     * whereas {@code animations} drives the Epic Fight-style rig animator
     * (P3-05).</p>
     */
    @Nullable AnimationBindings animations,

    /** Raw component configs from JSON, keyed by ComponentType. */
    Map<ComponentType, JsonObject> componentConfigs
) {

    /** Compact constructor: default null maps to empty immutable maps. */
    public DataDrivenItemDefinition {
        if (componentConfigs == null) componentConfigs = Map.of();
        if (animationBones == null) animationBones = Map.of();
    }

    /** Check whether this definition declares a given component type. */
    public boolean hasComponent(ComponentType type) {
        return componentConfigs.containsKey(type);
    }
}
