package com.tiedup.remake.util;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/**
 * Utility class for common NBT operations on ItemStacks.
 *
 * <p>Centralizes repetitive null-check patterns found throughout the codebase:
 * <ul>
 *   <li>Safe getters with default values</li>
 *   <li>Safe setters that handle empty stacks</li>
 *   <li>UUID list operations for owner/key systems</li>
 * </ul>
 *
 * <p>All methods are null-safe and handle empty ItemStacks gracefully.
 */
public final class ItemNBTHelper {

    private ItemNBTHelper() {
        // Utility class - no instantiation
    }

    // BOOLEAN OPERATIONS

    /**
     * Get a boolean value from an ItemStack's NBT.
     *
     * @param stack The ItemStack to read from
     * @param key The NBT key
     * @param defaultValue Value to return if stack is empty or key doesn't exist
     * @return The boolean value or defaultValue
     */
    public static boolean getBoolean(
        ItemStack stack,
        String key,
        boolean defaultValue
    ) {
        if (stack.isEmpty()) return defaultValue;
        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.contains(key, Tag.TAG_BYTE)) {
            return defaultValue;
        }
        return tag.getBoolean(key);
    }

    /**
     * Get a boolean value from an ItemStack's NBT (defaults to false).
     *
     * @param stack The ItemStack to read from
     * @param key The NBT key
     * @return The boolean value or false
     */
    public static boolean getBoolean(ItemStack stack, String key) {
        return getBoolean(stack, key, false);
    }

    /**
     * Set a boolean value in an ItemStack's NBT.
     *
     * @param stack The ItemStack to modify
     * @param key The NBT key
     * @param value The boolean value to set
     */
    public static void setBoolean(ItemStack stack, String key, boolean value) {
        if (stack.isEmpty()) return;
        stack.getOrCreateTag().putBoolean(key, value);
    }

    // INTEGER OPERATIONS

    /**
     * Get an integer value from an ItemStack's NBT.
     *
     * @param stack The ItemStack to read from
     * @param key The NBT key
     * @param defaultValue Value to return if stack is empty or key doesn't exist
     * @return The integer value or defaultValue
     */
    public static int getInt(ItemStack stack, String key, int defaultValue) {
        if (stack.isEmpty()) return defaultValue;
        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.contains(key, Tag.TAG_INT)) {
            return defaultValue;
        }
        return tag.getInt(key);
    }

    /**
     * Get an integer value from an ItemStack's NBT (defaults to 0).
     *
     * @param stack The ItemStack to read from
     * @param key The NBT key
     * @return The integer value or 0
     */
    public static int getInt(ItemStack stack, String key) {
        return getInt(stack, key, 0);
    }

    /**
     * Set an integer value in an ItemStack's NBT.
     *
     * @param stack The ItemStack to modify
     * @param key The NBT key
     * @param value The integer value to set
     */
    public static void setInt(ItemStack stack, String key, int value) {
        if (stack.isEmpty()) return;
        stack.getOrCreateTag().putInt(key, value);
    }

    // STRING OPERATIONS

    /**
     * Get a string value from an ItemStack's NBT.
     *
     * @param stack The ItemStack to read from
     * @param key The NBT key
     * @param defaultValue Value to return if stack is empty or key doesn't exist
     * @return The string value or defaultValue
     */
    @Nullable
    public static String getString(
        ItemStack stack,
        String key,
        @Nullable String defaultValue
    ) {
        if (stack.isEmpty()) return defaultValue;
        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.contains(key, Tag.TAG_STRING)) {
            return defaultValue;
        }
        return tag.getString(key);
    }

    /**
     * Get a string value from an ItemStack's NBT (defaults to null).
     *
     * @param stack The ItemStack to read from
     * @param key The NBT key
     * @return The string value or null
     */
    @Nullable
    public static String getString(ItemStack stack, String key) {
        return getString(stack, key, null);
    }

    /**
     * Set a string value in an ItemStack's NBT.
     *
     * @param stack The ItemStack to modify
     * @param key The NBT key
     * @param value The string value to set (null removes the key)
     */
    public static void setString(
        ItemStack stack,
        String key,
        @Nullable String value
    ) {
        if (stack.isEmpty()) return;
        if (value == null) {
            remove(stack, key);
        } else {
            stack.getOrCreateTag().putString(key, value);
        }
    }

    // UUID OPERATIONS

    /**
     * Get a UUID value from an ItemStack's NBT.
     *
     * @param stack The ItemStack to read from
     * @param key The NBT key
     * @return The UUID value or null if not present
     */
    @Nullable
    public static UUID getUUID(ItemStack stack, String key) {
        if (stack.isEmpty()) return null;
        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.hasUUID(key)) {
            return null;
        }
        return tag.getUUID(key);
    }

    /**
     * Set a UUID value in an ItemStack's NBT.
     *
     * @param stack The ItemStack to modify
     * @param key The NBT key
     * @param value The UUID value to set (null removes the key)
     */
    public static void setUUID(
        ItemStack stack,
        String key,
        @Nullable UUID value
    ) {
        if (stack.isEmpty()) return;
        if (value == null) {
            remove(stack, key);
        } else {
            stack.getOrCreateTag().putUUID(key, value);
        }
    }

    /**
     * Check if an ItemStack's NBT contains a UUID at the given key.
     *
     * @param stack The ItemStack to check
     * @param key The NBT key
     * @return true if the key exists and contains a UUID
     */
    public static boolean hasUUID(ItemStack stack, String key) {
        if (stack.isEmpty()) return false;
        CompoundTag tag = stack.getTag();
        return tag != null && tag.hasUUID(key);
    }

    // UUID LIST OPERATIONS

    /**
     * Get a list of UUIDs from an ItemStack's NBT.
     * UUIDs are stored as a ListTag of CompoundTags with "UUID" key.
     *
     * @param stack The ItemStack to read from
     * @param key The NBT key for the list
     * @return A list of UUIDs (empty list if not present)
     */
    public static List<UUID> getUUIDList(ItemStack stack, String key) {
        List<UUID> result = new ArrayList<>();
        if (stack.isEmpty()) return result;

        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.contains(key, Tag.TAG_LIST)) {
            return result;
        }

        ListTag listTag = tag.getList(key, Tag.TAG_COMPOUND);
        for (int i = 0; i < listTag.size(); i++) {
            CompoundTag entry = listTag.getCompound(i);
            if (entry.hasUUID("UUID")) {
                result.add(entry.getUUID("UUID"));
            }
        }
        return result;
    }

    /**
     * Set a list of UUIDs in an ItemStack's NBT.
     * UUIDs are stored as a ListTag of CompoundTags with "UUID" key.
     *
     * @param stack The ItemStack to modify
     * @param key The NBT key for the list
     * @param uuids The list of UUIDs to store
     */
    public static void setUUIDList(
        ItemStack stack,
        String key,
        List<UUID> uuids
    ) {
        if (stack.isEmpty()) return;

        if (uuids == null || uuids.isEmpty()) {
            remove(stack, key);
            return;
        }

        ListTag listTag = new ListTag();
        for (UUID uuid : uuids) {
            CompoundTag entry = new CompoundTag();
            entry.putUUID("UUID", uuid);
            listTag.add(entry);
        }
        stack.getOrCreateTag().put(key, listTag);
    }

    /**
     * Add a UUID to a list stored in an ItemStack's NBT.
     *
     * @param stack The ItemStack to modify
     * @param key The NBT key for the list
     * @param uuid The UUID to add
     * @return true if the UUID was added (not already present)
     */
    public static boolean addToUUIDList(
        ItemStack stack,
        String key,
        UUID uuid
    ) {
        if (stack.isEmpty() || uuid == null) return false;

        List<UUID> list = getUUIDList(stack, key);
        if (list.contains(uuid)) {
            return false;
        }
        list.add(uuid);
        setUUIDList(stack, key, list);
        return true;
    }

    /**
     * Remove a UUID from a list stored in an ItemStack's NBT.
     *
     * @param stack The ItemStack to modify
     * @param key The NBT key for the list
     * @param uuid The UUID to remove
     * @return true if the UUID was removed
     */
    public static boolean removeFromUUIDList(
        ItemStack stack,
        String key,
        UUID uuid
    ) {
        if (stack.isEmpty() || uuid == null) return false;

        List<UUID> list = getUUIDList(stack, key);
        if (list.remove(uuid)) {
            setUUIDList(stack, key, list);
            return true;
        }
        return false;
    }

    /**
     * Check if a UUID is present in a list stored in an ItemStack's NBT.
     *
     * @param stack The ItemStack to check
     * @param key The NBT key for the list
     * @param uuid The UUID to find
     * @return true if the UUID is in the list
     */
    public static boolean isInUUIDList(ItemStack stack, String key, UUID uuid) {
        if (stack.isEmpty() || uuid == null) return false;
        return getUUIDList(stack, key).contains(uuid);
    }

    // GENERIC OPERATIONS

    /**
     * Check if an ItemStack's NBT contains a key.
     *
     * @param stack The ItemStack to check
     * @param key The NBT key
     * @return true if the key exists
     */
    public static boolean contains(ItemStack stack, String key) {
        if (stack.isEmpty()) return false;
        CompoundTag tag = stack.getTag();
        return tag != null && tag.contains(key);
    }

    /**
     * Remove a key from an ItemStack's NBT.
     *
     * @param stack The ItemStack to modify
     * @param key The NBT key to remove
     */
    public static void remove(ItemStack stack, String key) {
        if (stack.isEmpty()) return;
        CompoundTag tag = stack.getTag();
        if (tag != null) {
            tag.remove(key);
        }
    }

    /**
     * Get the CompoundTag from an ItemStack (may be null).
     *
     * @param stack The ItemStack to read from
     * @return The CompoundTag or null
     */
    @Nullable
    public static CompoundTag getTag(ItemStack stack) {
        if (stack.isEmpty()) return null;
        return stack.getTag();
    }

    /**
     * Get or create the CompoundTag for an ItemStack.
     *
     * @param stack The ItemStack to modify
     * @return The CompoundTag (never null if stack is not empty)
     */
    @Nullable
    public static CompoundTag getOrCreateTag(ItemStack stack) {
        if (stack.isEmpty()) return null;
        return stack.getOrCreateTag();
    }
}
