content: package pers.neige.neigeitems.item.builder;
  
  import kotlin.text.StringsKt;
  import lombok.NonNull;
  import lombok.val;
  import net.kyori.adventure.text.minimessage.MiniMessage;
  import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
  import org.bukkit.ChatColor;
  import org.bukkit.Material;
  import org.bukkit.configuration.ConfigurationSection;
  import org.bukkit.enchantments.Enchantment;
  import org.bukkit.inventory.ItemFlag;
  import org.bukkit.inventory.ItemStack;
  import org.jetbrains.annotations.Nullable;
  import pers.neige.neigeitems.config.ConfigReader;
  import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.*;
  import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.internal.annotation.CbVersion;
  import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.neigeitems.utils.EnchantmentUtils;
  import pers.neige.neigeitems.libs.bot.inker.bukkit.nbt.neigeitems.utils.TranslationUtils;
  import pers.neige.neigeitems.manager.HookerManager;
  import pers.neige.neigeitems.utils.ItemUtils;
  
  import java.lang.reflect.Modifier;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.function.BiConsumer;
  import java.util.function.Consumer;
  
  public class ItemBuilder {
      public static final ItemStack air = new ItemStack(Material.AIR);
      public static final Map<String, Enchantment> byFieldName = new HashMap<>();
  
      static {
          val fields = Enchantment.class.getDeclaredFields();
          for (val field : fields) {
              if (!Modifier.isStatic(field.getModifiers())) continue;
              if (!Enchantment.class.isAssignableFrom(field.getType())) continue;
              try {
                  byFieldName.put(field.getName().toUpperCase(), (Enchantment) field.get(null));
              } catch (IllegalAccessException exception) {
                  exception.printStackTrace();
              }
          }
      }
  
      protected @Nullable Material material = null;
      protected @Nullable ItemStack itemStack = null;
      protected @Nullable Short damage = null;
      protected @NonNull Map<Enchantment, Short> enchantments = new HashMap<>();
      protected @NonNull Map<Short, Short> enchantmentIdToLevel = new HashMap<>();
      protected @NonNull Map<String, Short> enchantmentKeyToLevel = new HashMap<>();
      protected @Nullable Integer customModelData = null;
      protected @Nullable NbtString name = null;
      protected @Nullable NbtList lore = null;
      protected @Nullable Integer color = null;
      protected @Nullable Integer potionColor = null;
      protected @Nullable Boolean unbreakable = null;
      protected @Nullable Integer hideFlag = null;
      protected @Nullable NbtCompound coverNbt = null;
      protected boolean hasBuild = false;
      protected @Nullable Consumer<ItemStack> postItemInit = null;
      protected @Nullable BiConsumer<ItemStack, NbtCompound> preCoverNbt = null;
  
      public ItemBuilder() {
      }
  
      public ItemBuilder(
              @Nullable Material material
      ) {
          this.material = material;
      }
  
      public ItemBuilder(
              @Nullable ItemStack itemStack
      ) {
          this.itemStack = itemStack == null ? null : NbtUtils.asCraftCopy(itemStack);
      }
  
      public ItemBuilder(
              @Nullable ConfigReader config
      ) {
          load(config);
      }
  
      public void load(
              @Nullable ConfigurationSection config
      ) {
          if (config == null) return;
          load(ConfigReader.parse(config));
      }
  
      public void load(
              @Nullable ConfigReader config
      ) {
          if (config == null) return;
          for (val key : config.keySet()) {
              switch (key.toLowerCase()) {
                  case "type":
                  case "material": {
                      val materialString = config.getString(key);
                      if (materialString != null) {
                          this.material = HookerManager.getMaterial(materialString);
                      }
                      break;
                  }
                  case "damage": {
                      this.damage = (short) config.getInt(key);
                      break;
                  }
                  case "enchantments": {
                      val enchantSection = config.getConfig(key);
                      if (enchantSection != null) {
                          for (val enchantId : enchantSection.keySet()) {
                              val uppercaseEnchantId = enchantId.toUpperCase();
                              val 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);
                                  if (CbVersion.v1_13_R1.isSupport()) {
                                      this.enchantmentKeyToLevel.put(enchant.getKey().toString(), level);
                                  } else {
                                      this.enchantmentIdToLevel.put((short) EnchantmentUtils.getId(enchant), level);
                                  }
                              }
                          }
                      }
                      break;
                  }
                  case "custommodeldata":
                  case "custom-model-data": {
                      this.customModelData = config.getInt(key);
                      break;
                  }
                  case "name": {
                      val rawName = config.getString(key);
                      if (rawName != null) {
                          this.name = NbtString.valueOf(TranslationUtils.toJsonText(ChatColor.translateAlternateColorCodes('&', rawName)));
                      }
                      break;
                  }
                  case "lore": {
                      val originLore = config.getStringList(key);
                      val finalLore = new NbtList();
                      for (val rawLore : originLore) {
                          for (val loreText : ChatColor.translateAlternateColorCodes('&', rawLore).split("\n")) {
                              finalLore.add(NbtString.valueOf(TranslationUtils.toJsonText(loreText)));
                          }
                      }
                      if (!finalLore.isEmpty()) {
                          this.lore = finalLore;
                      }
                      break;
                  }
                  case "mini-name": {
                      val rawName = config.getString(key);
                      if (rawName != null) {
                          this.name = NbtString.valueOf(GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(rawName)));
                      }
                      break;
                  }
                  case "mini-lore": {
                      val originLore = config.getStringList(key);
                      val finalLore = new NbtList();
                      for (val rawLore : originLore) {
                          for (val loreText : rawLore.split("\n")) {
                              finalLore.add(NbtString.valueOf(GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(loreText))));
                          }
                      }
                      if (!finalLore.isEmpty()) {
                          this.lore = finalLore;
                      }
                      break;
                  }
                  case "color": {
                      val 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 "potion-color": {
                      val value = config.get(key);
                      if (value instanceof String) {
                          potionColor = StringsKt.toIntOrNull((String) value, 16);
                      } else if (value instanceof Integer) {
                          potionColor = 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": {
                      val flags = config.getStringList(key);
                      if (!flags.isEmpty()) {
                          this.hideFlag = 0;
                          for (val flagText : flags) {
                              try {
                                  val flag = ItemFlag.valueOf(flagText);
                                  this.hideFlag |= (1 << flag.ordinal());
                              } catch (IllegalArgumentException exception) {
                                  exception.printStackTrace();
                              }
                          }
                      }
                      break;
                  }
                  case "nbt": {
                      val nbtConfig = config.getConfig(key);
                      if (nbtConfig != null) {
                          this.coverNbt = ItemUtils.toNbtCompound(nbtConfig);
                      }
                      break;
                  }
              }
          }
      }
  
      /**
       * 根据已有内容构建并返回物品.
       * 返回前会将当前ItemBuilder实例的itemStack字段设置为返回值.
       * 构建过程中可能会对已有内容进行修改, 比如清空所有Enchantment.
       * 构建结果永远不为null, 构建失败将返回AIR.
       * 禁止多次调用build方法, 为防止出现问题, 此时将抛出异常.
       *
       * @return 构建产物.
       */
      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;
          }
          ItemStack result;
          if (this.itemStack != null) {
              result = this.itemStack;
              if (this.material != null) {
                  result.setType(this.material);
              }
              if (postItemInit != null) {
                  postItemInit.accept(result);
              }
          } else {
              if (this.material != null) {
                  result = NbtUtils.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;
          }
          if (CbVersion.v1_20_R3.isUpTo()) {
              val nbtItemStack = new NbtItemStack(result);
              val nbt = nbtItemStack.getOrCreateTag();
              if (this.damage != null) {
                  if (CbVersion.v1_13_R1.isSupport()) {
                      nbt.putInt(NbtUtils.getDamageNbtKeyOrThrow(), damage);
                  } else {
                      result.setDurability(damage);
                  }
              }
              if (!this.enchantments.isEmpty()) {
                  if (CbVersion.v1_13_R1.isSupport()) {
                      val list = nbt.getOrCreateList(NbtUtils.getEnchantmentsNbtKey());
  
                      for (val tag : list) {
                          if (!(tag instanceof NbtCompound)) continue;
                          val compound = (NbtCompound) tag;
                          val id = compound.getString(NbtUtils.getEnchantmentIdNbtKey());
                          val lvl = enchantmentKeyToLevel.get(id);
                          if (lvl != null) {
                              compound.putShort(NbtUtils.getEnchantmentLvlNbtKey(), lvl);
                              enchantmentKeyToLevel.remove(id);
                          }
                      }
  
                      enchantmentKeyToLevel.forEach((id, lvl) -> {
                          val tag = new NbtCompound();
                          tag.putString(NbtUtils.getEnchantmentIdNbtKey(), id);
                          tag.putShort(NbtUtils.getEnchantmentLvlNbtKey(), lvl);
                          list.add(tag);
                      });
                  } else {
                      val list = nbt.getOrCreateList(NbtUtils.getEnchantmentsNbtKey());
  
                      for (val tag : list) {
                          if (!(tag instanceof NbtCompound)) continue;
                          val compound = (NbtCompound) tag;
                          val id = compound.getShort(NbtUtils.getEnchantmentIdNbtKey());
                          val lvl = enchantmentIdToLevel.get(id);
                          if (lvl != null) {
                              compound.putShort(NbtUtils.getEnchantmentLvlNbtKey(), lvl);
                              enchantmentIdToLevel.remove(id);
                          }
                      }
                      enchantmentIdToLevel.forEach((id, lvl) -> {
                          val tag = new NbtCompound();
                          tag.putShort(NbtUtils.getEnchantmentIdNbtKey(), id);
                          tag.putShort(NbtUtils.getEnchantmentLvlNbtKey(), lvl);
                          list.add(tag);
                      });
                  }
              }
              if (customModelData != null) {
                  val nbtKey = NbtUtils.getCustomModelDataNbtKeyOrNull();
                  if (nbtKey != null) {
                      nbt.putInt(nbtKey, customModelData);
                  }
              }
              if (name != null || lore != null || color != null) {
                  val display = nbt.getOrCreateCompound(NbtUtils.getDisplayNbtKey());
                  if (name != null) {
                      display.put(NbtUtils.getNameNbtKey(), name);
                  }
                  if (lore != null) {
                      display.put(NbtUtils.getLoreNbtKey(), lore);
                  }
                  if (color != null) {
                      display.putInt("color", color);
                  }
              }
              if (potionColor != null) {
                  nbt.putInt("CustomPotionColor", potionColor);
              }
              if (unbreakable != null) {
                  nbt.putBoolean(NbtUtils.getUnbreakableNbtKey(), unbreakable);
              }
              if (hideFlag != null) {
                  nbt.putInt(NbtUtils.getHideFlagsNbtKey(), hideFlag);
              }
              if (preCoverNbt != null) {
                  preCoverNbt.accept(result, nbt);
              }
              if (coverNbt != null) {
                  nbt.coverWith(coverNbt);
              }
              this.itemStack = result;
          }
          return result;
      }
  
      public @Nullable Material getType() {
          return material;
      }
  
      public void setType(@Nullable Material material) {
          this.material = material;
      }
  
      public @Nullable ItemStack getItemStack() {
          return itemStack;
      }
  
      public void setItemStack(@Nullable ItemStack itemStack) {
          this.itemStack = itemStack;
      }
  
      public @Nullable Short getDamage() {
          return damage;
      }
  
      public void setDamage(short damage) {
          this.damage = damage;
      }
  
      public @NonNull Map<Enchantment, Short> getEnchantments() {
          return enchantments;
      }
  
      public void setEnchantments(@NonNull Map<Enchantment, Short> enchantments) {
          this.enchantments = enchantments;
      }
  
      public @Nullable Integer getCustomModelData() {
          return customModelData;
      }
  
      public void setCustomModelData(int customModelData) {
          this.customModelData = customModelData;
      }
  
      public boolean getUnbreakable() {
          return unbreakable != null && unbreakable;
      }
  
      public void setUnbreakable(boolean unbreakable) {
          this.unbreakable = unbreakable;
      }
  
      public @Nullable Integer getHideFlag() {
          return hideFlag;
      }
  
      public void setHideFlag(@Nullable Integer hideFlag) {
          this.hideFlag = hideFlag;
      }
  
      public @Nullable NbtCompound getCoverNbt() {
          return coverNbt;
      }
  
      public void setCoverNbt(@Nullable NbtCompound coverNbt) {
          this.coverNbt = coverNbt;
      }
  
      /**
       * build方法内, 根据material生成ItemStack实例或执行ItemStack#setType后执行的动作.
       *
       * @param postItemInit 执行的动作
       */
      public void runPostItemInit(@Nullable Consumer<ItemStack> postItemInit) {
          this.postItemInit = postItemInit;
      }
  
      /**
       * build方法内, 物品各个属性设置完毕, 将额外NBT覆盖至物品当前NBT前执行的动作.
       *
       * @param preCoverNbt 执行的动作
       */
      public void runPreCoverNbt(@Nullable BiConsumer<ItemStack, NbtCompound> preCoverNbt) {
          this.preCoverNbt = preCoverNbt;
      }
  }
  