package pers.neige.neigeitems.item.builder;

import com.mojang.serialization.DataResult;
import io.papermc.paper.adventure.AdventureComponent;
import kotlin.text.StringsKt;
import lombok.NonNull;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.RegistryOps;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.item.component.CustomModelData;
import net.minecraft.world.item.component.DyedItemColor;
import net.minecraft.world.item.component.ItemLore;
import net.minecraft.world.item.component.Unbreakable;
import net.minecraft.world.item.enchantment.ItemEnchantments;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import pers.neige.neigeitems.NeigeItems;
import pers.neige.neigeitems.config.ConfigReader;
import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.Nbt;
import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.NbtCompound;
import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.NbtUtils;
import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.internal.annotation.CbVersion;
import pers.neige.neigeitems.manager.HookerManager;
import pers.neige.neigeitems.utils.ItemUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;

public class NewItemBuilder extends ItemBuilder {
    public static final RegistryOps<Tag> registryOps = MinecraftServer.getServer().registryAccess().createSerializationContext(NbtOps.INSTANCE);
    private static final boolean LOWER_THAN_1_21_R4 = !CbVersion.v1_21_R4.isSupport();
    private static final boolean LOWER_THAN_1_21_R3 = !CbVersion.v1_21_R3.isSupport();

    private final @NonNull Set<ItemFlag> hideFlag = new HashSet<>();
    private final @NonNull ItemEnchantments.Mutable mutableEnchantments = new ItemEnchantments.Mutable(ItemEnchantments.EMPTY);
    private @Nullable Component name = null;
    private @Nullable Component itemName = null;
    private @Nullable List<Component> lore = null;
    private DataComponentPatch components = null;

    public NewItemBuilder() {
    }

    public NewItemBuilder(
        @Nullable Material material
    ) {
        this.material = material;
    }

    public NewItemBuilder(
        @Nullable ItemStack itemStack
    ) {
        this.itemStack = itemStack == null ? null : NbtUtils.asCraftCopy(itemStack);
    }

    public NewItemBuilder(
        @Nullable ConfigReader config
    ) {
        load(config);
    }

    public static <T> void loadComponent(
        @NonNull DataComponentPatch.Builder builder,
        @NonNull DataComponentType<T> type,
        @NonNull Object value
    ) throws IllegalStateException {
        Tag tag = (Tag) Nbt.Unsafe.getDelegate(ItemUtils.toNbt(value));
        DataResult<T> result = type.codecOrThrow().parse(registryOps, tag);
        builder.set(type, result.getOrThrow());
    }

    @Override
    public void load(
        @Nullable ConfigReader config
    ) {
        if (config == null) return;
        for (String key : config.keySet()) {
            switch (key.toLowerCase()) {
                case "type":
                case "material": {
                    String materialString = config.getString(key);
                    if (materialString != null) {
                        this.material = HookerManager.getMaterial(materialString);
                    }
                    break;
                }
                case "damage": {
                    this.damage = (short) config.getInt(key);
                    break;
                }
                case "enchantments": {
                    ConfigReader enchantSection = config.getConfig(key);
                    if (enchantSection != null) {
                        for (String enchantId : enchantSection.keySet()) {
                            String uppercaseEnchantId = enchantId.toUpperCase();
                            short level = (short) enchantSection.getInt(enchantId);
                            Enchantment enchant = Enchantment.getByName(uppercaseEnchantId);
                            if (enchant == null) {
                                enchant = byFieldName.get(uppercaseEnchantId);
                            }
                            if (level > 0 && enchant != null) {
                                this.enchantments.put(enchant, level);
                                this.mutableEnchantments.set(CraftEnchantment.bukkitToMinecraftHolder(enchant), level);
                            }
                        }
                    }
                    break;
                }
                case "custommodeldata":
                case "custom-model-data": {
                    this.customModelData = config.getInt(key);
                    break;
                }
                case "name": {
                    String rawName = config.getString(key);
                    if (rawName != null) {
                        Component component = CraftChatMessage.fromStringOrNull(ChatColor.translateAlternateColorCodes('&', rawName));
                        this.name = component == null ? CommonComponents.EMPTY : component;
                    }
                    break;
                }
                case "item-name": {
                    String rawName = config.getString(key);
                    if (rawName != null) {
                        Component component = CraftChatMessage.fromStringOrNull(ChatColor.translateAlternateColorCodes('&', rawName));
                        this.itemName = component == null ? CommonComponents.EMPTY : component;
                    }
                    break;
                }
                case "lore": {
                    List<String> originLore = config.getStringList(key);
                    List<Component> finalLore = new ArrayList<>();
                    for (String rawLore : originLore) {
                        for (String loreText : ChatColor.translateAlternateColorCodes('&', rawLore).split("\n")) {
                            Component component = CraftChatMessage.fromStringOrNull(loreText);
                            finalLore.add(component == null ? CommonComponents.EMPTY : component);
                        }
                    }
                    if (!finalLore.isEmpty()) {
                        this.lore = finalLore;
                    }
                    break;
                }
                case "mini-name": {
                    String rawName = config.getString(key);
                    if (rawName != null) {
                        this.name = new AdventureComponent(MiniMessage.miniMessage().deserialize(rawName)).deepConverted();
                    }
                    break;
                }
                case "mini-item-name": {
                    String rawName = config.getString(key);
                    if (rawName != null) {
                        this.itemName = new AdventureComponent(MiniMessage.miniMessage().deserialize(rawName)).deepConverted();
                    }
                    break;
                }
                case "mini-lore": {
                    List<String> originLore = config.getStringList(key);
                    List<Component> finalLore = new ArrayList<>();
                    for (String rawLore : originLore) {
                        for (String loreText : rawLore.split("\n")) {
                            finalLore.add(new AdventureComponent(MiniMessage.miniMessage().deserialize(loreText)).deepConverted());
                        }
                    }
                    if (!finalLore.isEmpty()) {
                        this.lore = finalLore;
                    }
                    break;
                }
                case "color": {
                    Object value = config.get(key);
                    if (value instanceof String) {
                        color = StringsKt.toIntOrNull((String) value, 16);
                    } else if (value instanceof Integer) {
                        color = Math.min(0xFFFFFF, Math.max(0, (int) value));
                    }
                    break;
                }
                case "unbreakable": {
                    this.unbreakable = config.getBoolean(key);
                    break;
                }
                case "item-flags":
                case "itemflags":
                case "hide-flags":
                case "hideflags": {
                    List<String> flags = config.getStringList(key);
                    if (!flags.isEmpty()) {
                        for (String flagText : flags) {
                            ItemFlag flag = ItemFlag.valueOf(flagText);
                            this.hideFlag.add(flag);
                        }
                    }
                    break;
                }
                case "nbt": {
                    ConfigReader nbtConfig = config.getConfig(key);
                    if (nbtConfig != null) {
                        this.coverNbt = ItemUtils.toNbtCompound(nbtConfig);
                    }
                    break;
                }
                case "components": {
                    ConfigReader componentsConfig = config.getConfig(key);
                    if (componentsConfig != null) {
                        DataComponentPatch.Builder builder = DataComponentPatch.builder();
                        for (String componentKey : componentsConfig.keySet()) {
                            Object componentValue = componentsConfig.get(componentKey);
                            if (componentValue == null) continue;
                            DataComponentType<?> type = (DataComponentType<?>) HookerManager.INSTANCE.getNmsHooker().getDataComponentType(componentKey);
                            if (type == null) {
                                NeigeItems.getInstance().getLogger().warning("Unknown component type: " + componentKey);
                                continue;
                            }
                            try {
                                loadComponent(builder, type, componentValue);
                            } catch (IllegalStateException e) {
                                NeigeItems.getInstance().getLogger().log(Level.WARNING, "Invalid component value: " + componentValue, e);
                            }
                        }
                        components = builder.build();
                    }
                    break;
                }
            }
        }
    }

    /**
     * 根据已有内容构建并返回物品.
     * 返回前会将当前ItemBuilder实例的itemStack字段设置为返回值.
     * 构建过程中可能会对已有内容进行修改, 比如清空所有Enchantment.
     * 构建结果永远不为null, 构建失败将返回AIR.
     * 禁止多次调用build方法, 为防止出现问题, 此时将抛出异常.
     *
     * @return 构建产物.
     */
    @Override
    public @NonNull ItemStack build() {
        if (hasBuild) {
            throw new UnsupportedOperationException("Already build");
        }
        this.hasBuild = true;
        if (this.material == Material.AIR) {
            this.itemStack = air;
            if (postItemInit != null) {
                postItemInit.accept(air.clone());
            }
            return air;
        }
        CraftItemStack result;
        if (this.itemStack != null) {
            result = this.itemStack instanceof CraftItemStack ? (CraftItemStack) this.itemStack : CraftItemStack.asCraftCopy(this.itemStack);
            if (this.material != null) {
                result.setType(this.material);
            }
            if (postItemInit != null) {
                postItemInit.accept(result);
            }
        } else {
            if (this.material != null) {
                result = CraftItemStack.asCraftCopy(new ItemStack(this.material));
                if (postItemInit != null) {
                    postItemInit.accept(result);
                }
            } else {
                this.itemStack = air;
                if (postItemInit != null) {
                    postItemInit.accept(air.clone());
                }
                return air;
            }
        }
        if (result.getType() == Material.AIR) {
            this.itemStack = air;
            return air;
        }
        result.lore();
        net.minecraft.world.item.ItemStack handle = result.handle;
        if (this.damage != null) {
            handle.setDamageValue(damage);
        }
        if (!this.enchantments.isEmpty()) {
            if (LOWER_THAN_1_21_R4) {
                mutableEnchantments.showInTooltip = !this.hideFlag.contains(ItemFlag.HIDE_ENCHANTS);
            }
            handle.set(DataComponents.ENCHANTMENTS, mutableEnchantments.toImmutable());
        }
        if (customModelData != null) {
            if (LOWER_THAN_1_21_R3) {
                handle.set(DataComponents.CUSTOM_MODEL_DATA, new CustomModelData(customModelData));
            } else {
                NeigeItems.getInstance().getLogger().warning("1.21.4+的版本请使用components配置项通过组件配置CustomModelData");
            }
        }
        if (name != null) {
            handle.set(DataComponents.CUSTOM_NAME, name);
        }
        if (itemName != null) {
            handle.set(DataComponents.ITEM_NAME, itemName);
        }
        if (lore != null) {
            handle.set(DataComponents.LORE, new ItemLore(lore));
        }
        if (color != null) {
            if (LOWER_THAN_1_21_R4) {
                handle.set(DataComponents.DYED_COLOR, new DyedItemColor(color, !this.hideFlag.contains(ItemFlag.HIDE_DYE)));
            } else {
                NeigeItems.getInstance().getLogger().warning("1.21.4+的版本请使用components配置项通过组件配置皮革颜色");
            }
        }
        if (Boolean.TRUE.equals(unbreakable)) {
            if (LOWER_THAN_1_21_R4) {
                handle.set(DataComponents.UNBREAKABLE, new Unbreakable(!this.hideFlag.contains(ItemFlag.HIDE_UNBREAKABLE)));
            } else {
                NeigeItems.getInstance().getLogger().warning("1.21.4+的版本请使用components配置项通过组件配置无法破坏");
            }
        }
        if (components != null) {
            handle.applyComponents(components);
        }
        NbtCompound nbt = ItemUtils.getNbt(result);
        if (preCoverNbt != null) {
            preCoverNbt.accept(result, nbt);
        }
        if (coverNbt != null) {
            nbt.coverWith(coverNbt);
        }
        this.itemStack = result;
        return result;
    }
}
