package com.tiedup.remake.network.sync;

import com.tiedup.remake.items.clothes.ClothesProperties;
import com.tiedup.remake.items.clothes.GenericClothes;
import com.tiedup.remake.network.base.AbstractClientPacket;
import java.util.EnumSet;
import java.util.UUID;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

/**
 * Packet to synchronize clothes configuration from server to clients.
 * Sent when a player's clothes settings change (URL, fullSkin, smallArms, layers).
 *
 * Direction: Server → Client (S2C)
 *
 * This packet enables other players to see dynamic textures on clothes worn by players.
 */
public class PacketSyncClothesConfig extends AbstractClientPacket {

    private final UUID playerUUID;
    private final boolean hasClothes;
    private final String dynamicUrl; // May be null/empty
    private final boolean fullSkin;
    private final boolean smallArms;
    private final boolean keepHead;
    private final byte layerVisibility; // Bitfield for 6 layers

    /**
     * Create a packet for a player wearing clothes.
     *
     * @param playerUUID The player's UUID
     * @param clothes The clothes ItemStack (may be empty)
     */
    public PacketSyncClothesConfig(UUID playerUUID, ItemStack clothes) {
        this.playerUUID = playerUUID;

        if (
            !clothes.isEmpty() && clothes.getItem() instanceof GenericClothes gc
        ) {
            this.hasClothes = true;
            this.dynamicUrl = gc.getDynamicTextureUrl(clothes);
            this.fullSkin = gc.isFullSkinEnabled(clothes);
            this.smallArms = gc.shouldForceSmallArms(clothes);
            this.keepHead = gc.isKeepHeadEnabled(clothes);
            this.layerVisibility = encodeLayerVisibility(gc, clothes);
        } else {
            this.hasClothes = false;
            this.dynamicUrl = null;
            this.fullSkin = false;
            this.smallArms = false;
            this.keepHead = false;
            this.layerVisibility = 0b111111; // All visible (default)
        }
    }

    /**
     * Create a packet indicating no clothes.
     *
     * @param playerUUID The player's UUID
     */
    public static PacketSyncClothesConfig noClothes(UUID playerUUID) {
        return new PacketSyncClothesConfig(playerUUID, ItemStack.EMPTY);
    }

    /**
     * Private constructor for decoding.
     */
    private PacketSyncClothesConfig(
        UUID playerUUID,
        boolean hasClothes,
        String dynamicUrl,
        boolean fullSkin,
        boolean smallArms,
        boolean keepHead,
        byte layerVisibility
    ) {
        this.playerUUID = playerUUID;
        this.hasClothes = hasClothes;
        this.dynamicUrl = dynamicUrl;
        this.fullSkin = fullSkin;
        this.smallArms = smallArms;
        this.keepHead = keepHead;
        this.layerVisibility = layerVisibility;
    }

    /**
     * Encode layer visibility as a byte bitfield.
     */
    private byte encodeLayerVisibility(GenericClothes gc, ItemStack stack) {
        byte bits = 0;
        if (gc.isLayerEnabled(stack, GenericClothes.LAYER_HEAD)) bits |=
            0b000001;
        if (gc.isLayerEnabled(stack, GenericClothes.LAYER_BODY)) bits |=
            0b000010;
        if (gc.isLayerEnabled(stack, GenericClothes.LAYER_LEFT_ARM)) bits |=
            0b000100;
        if (gc.isLayerEnabled(stack, GenericClothes.LAYER_RIGHT_ARM)) bits |=
            0b001000;
        if (gc.isLayerEnabled(stack, GenericClothes.LAYER_LEFT_LEG)) bits |=
            0b010000;
        if (gc.isLayerEnabled(stack, GenericClothes.LAYER_RIGHT_LEG)) bits |=
            0b100000;
        return bits;
    }

    /**
     * Encode the packet to a byte buffer.
     */
    public void encode(FriendlyByteBuf buf) {
        buf.writeUUID(playerUUID);
        buf.writeBoolean(hasClothes);

        if (hasClothes) {
            // Write URL (empty string if null)
            buf.writeUtf(dynamicUrl != null ? dynamicUrl : "", 2048);
            buf.writeBoolean(fullSkin);
            buf.writeBoolean(smallArms);
            buf.writeBoolean(keepHead);
            buf.writeByte(layerVisibility);
        }
    }

    /**
     * Decode the packet from a byte buffer.
     */
    public static PacketSyncClothesConfig decode(FriendlyByteBuf buf) {
        UUID playerUUID = buf.readUUID();
        boolean hasClothes = buf.readBoolean();

        if (hasClothes) {
            String dynamicUrl = buf.readUtf(2048);
            if (dynamicUrl.isEmpty()) dynamicUrl = null;
            boolean fullSkin = buf.readBoolean();
            boolean smallArms = buf.readBoolean();
            boolean keepHead = buf.readBoolean();
            byte layerVisibility = buf.readByte();

            return new PacketSyncClothesConfig(
                playerUUID,
                true,
                dynamicUrl,
                fullSkin,
                smallArms,
                keepHead,
                layerVisibility
            );
        } else {
            return new PacketSyncClothesConfig(
                playerUUID,
                false,
                null,
                false,
                false,
                false,
                (byte) 0b111111
            );
        }
    }

    /**
     * Client-side handling - update the clothes cache.
     */
    @Override
    @OnlyIn(Dist.CLIENT)
    protected void handleClientImpl() {
        if (hasClothes) {
            EnumSet<ClothesProperties.LayerPart> layers =
                ClothesProperties.decodeLayerVisibility(layerVisibility);
            com.tiedup.remake.client.state.ClothesClientCache.updatePlayerClothes(
                playerUUID,
                dynamicUrl,
                fullSkin,
                smallArms,
                keepHead,
                layers
            );
        } else {
            com.tiedup.remake.client.state.ClothesClientCache.removePlayerClothes(
                playerUUID
            );
        }
    }

    // Getters

    public UUID getPlayerUUID() {
        return playerUUID;
    }

    public boolean hasClothes() {
        return hasClothes;
    }

    public String getDynamicUrl() {
        return dynamicUrl;
    }

    public boolean isFullSkin() {
        return fullSkin;
    }

    public boolean isSmallArms() {
        return smallArms;
    }

    public byte getLayerVisibility() {
        return layerVisibility;
    }
}
