package com.tiedup.remake.items.clothes;

import java.util.EnumSet;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/**
 * Immutable snapshot of clothes properties for rendering.
 * Extracted from ItemStack NBT for efficient access during render.
 *
 * <p>This record is created once per render frame to avoid repeated NBT lookups.
 */
public record ClothesProperties(
    @Nullable String dynamicTextureUrl,
    boolean fullSkin,
    boolean smallArms,
    boolean keepHead,
    EnumSet<LayerPart> visibleLayers
) {
    /**
     * Enum representing the six body layer parts that can be hidden.
     */
    public enum LayerPart {
        HEAD,
        BODY,
        LEFT_ARM,
        RIGHT_ARM,
        LEFT_LEG,
        RIGHT_LEG,
    }

    /**
     * Create a ClothesProperties from an ItemStack.
     *
     * @param stack The clothes ItemStack
     * @return ClothesProperties, or null if not a GenericClothes item
     */
    @Nullable
    public static ClothesProperties fromStack(ItemStack stack) {
        if (
            stack.isEmpty() ||
            !(stack.getItem() instanceof GenericClothes clothes)
        ) {
            return null;
        }

        String url = clothes.getDynamicTextureUrl(stack);
        boolean fullSkin = clothes.isFullSkinEnabled(stack);
        boolean smallArms = clothes.shouldForceSmallArms(stack);
        boolean keepHead = clothes.isKeepHeadEnabled(stack);

        EnumSet<LayerPart> visible = EnumSet.noneOf(LayerPart.class);
        if (
            clothes.isLayerEnabled(stack, GenericClothes.LAYER_HEAD)
        ) visible.add(LayerPart.HEAD);
        if (
            clothes.isLayerEnabled(stack, GenericClothes.LAYER_BODY)
        ) visible.add(LayerPart.BODY);
        if (
            clothes.isLayerEnabled(stack, GenericClothes.LAYER_LEFT_ARM)
        ) visible.add(LayerPart.LEFT_ARM);
        if (
            clothes.isLayerEnabled(stack, GenericClothes.LAYER_RIGHT_ARM)
        ) visible.add(LayerPart.RIGHT_ARM);
        if (
            clothes.isLayerEnabled(stack, GenericClothes.LAYER_LEFT_LEG)
        ) visible.add(LayerPart.LEFT_LEG);
        if (
            clothes.isLayerEnabled(stack, GenericClothes.LAYER_RIGHT_LEG)
        ) visible.add(LayerPart.RIGHT_LEG);

        return new ClothesProperties(
            url,
            fullSkin,
            smallArms,
            keepHead,
            visible
        );
    }

    /**
     * Check if a dynamic texture URL is set.
     *
     * @return true if a URL is available
     */
    public boolean hasUrl() {
        return dynamicTextureUrl != null && !dynamicTextureUrl.isEmpty();
    }

    /**
     * Check if all layers are visible (default state).
     *
     * @return true if all 6 layers are visible
     */
    public boolean allLayersVisible() {
        return visibleLayers.size() == 6;
    }

    /**
     * Check if a specific layer is visible.
     *
     * @param part The layer part to check
     * @return true if visible
     */
    public boolean isLayerVisible(LayerPart part) {
        return visibleLayers.contains(part);
    }

    /**
     * Encode layer visibility as a byte bitfield.
     * Used for network packets.
     *
     * <p>Bit positions:
     * <ul>
     *   <li>0: HEAD</li>
     *   <li>1: BODY</li>
     *   <li>2: LEFT_ARM</li>
     *   <li>3: RIGHT_ARM</li>
     *   <li>4: LEFT_LEG</li>
     *   <li>5: RIGHT_LEG</li>
     * </ul>
     *
     * @return Bitfield byte (0b111111 = all visible)
     */
    public byte encodeLayerVisibility() {
        byte bits = 0;
        if (visibleLayers.contains(LayerPart.HEAD)) bits |= 0b000001;
        if (visibleLayers.contains(LayerPart.BODY)) bits |= 0b000010;
        if (visibleLayers.contains(LayerPart.LEFT_ARM)) bits |= 0b000100;
        if (visibleLayers.contains(LayerPart.RIGHT_ARM)) bits |= 0b001000;
        if (visibleLayers.contains(LayerPart.LEFT_LEG)) bits |= 0b010000;
        if (visibleLayers.contains(LayerPart.RIGHT_LEG)) bits |= 0b100000;
        return bits;
    }

    /**
     * Decode layer visibility from a byte bitfield.
     *
     * @param bits The bitfield byte
     * @return EnumSet of visible layers
     */
    public static EnumSet<LayerPart> decodeLayerVisibility(byte bits) {
        EnumSet<LayerPart> visible = EnumSet.noneOf(LayerPart.class);
        if ((bits & 0b000001) != 0) visible.add(LayerPart.HEAD);
        if ((bits & 0b000010) != 0) visible.add(LayerPart.BODY);
        if ((bits & 0b000100) != 0) visible.add(LayerPart.LEFT_ARM);
        if ((bits & 0b001000) != 0) visible.add(LayerPart.RIGHT_ARM);
        if ((bits & 0b010000) != 0) visible.add(LayerPart.LEFT_LEG);
        if ((bits & 0b100000) != 0) visible.add(LayerPart.RIGHT_LEG);
        return visible;
    }
}
