package dev.okenote.items;

import dev.okenote.JediCraft;
import dev.okenote.entities.player.Faction;
import dev.okenote.modules.CaseModule;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.component.DataComponent;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.item.component.Unbreakable;
import net.minestom.server.tag.Tag;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.function.Function;

public abstract class ItemBuilder<T extends ItemBuilder<?>> {

    JediItem jediItem = JediCraft.getJediCraft().getJediItem();

    public static MaterialItemBuilder fromMaterial(Material material) {
        return new MaterialItemBuilder(material);
    }

    public static ItemStackItemBuilder fromItem(ItemStack itemStack) {
        return new ItemStackItemBuilder(itemStack);
    }

    private final List<Function<ItemStack, ItemStack>> itemChangers = new ArrayList<>();

    public T name(@NotNull Component name) {
        itemChangers.add(itemStack -> itemStack.with(ItemComponent.CUSTOM_NAME, name));
        return getThis();
    }

    public T lore(Collection<Component> lore) {
        itemChangers.add(itemStack -> itemStack.with(ItemComponent.LORE, new ArrayList<>(lore)));
        return getThis();
    }

    public T lore(Component... lore) {
        return lore(Arrays.asList(lore));
    }

    public T appendLore(Component... lines) {
        itemChangers.add(itemStack -> {
            List<Component> lore = lore(itemStack);
            lore.addAll(Arrays.asList(lines));
            return itemStack.with(ItemComponent.LORE, lore);
        });
        return getThis();
    }

    public T formatLore(Function<List<Component>, List<Component>> formatter) {
        itemChangers.add(itemStack -> {
            List<Component> lore = lore(itemStack);
            return itemStack.with(ItemComponent.LORE, formatter.apply(lore));
        });
        return getThis();
    }

    public T formatLoreLines(Function<Component, Component> formatter) {
        itemChangers.add(itemStack -> {
            List<Component> lore = new ArrayList<>();
            lore(itemStack).forEach(line -> lore.add(formatter.apply(line)));
            return itemStack.with(ItemComponent.LORE, lore);
        });
        return getThis();
    }

    private List<Component> lore(@NotNull ItemStack itemStack) {
        List<Component> lore = itemStack.get(ItemComponent.LORE);
        return lore == null ? new ArrayList<>() : lore;
    }

    public T amount(int amount) {
        itemChangers.add(itemStack -> itemStack.withAmount(amount));
        return getThis();
    }

    public T material(@NotNull Material material) {
        itemChangers.add(itemStack -> itemStack.withMaterial(material));
        return getThis();
    }

    public T maxStackSize(int maxStackSize) {
        itemChangers.add(itemStack -> itemStack.with(ItemComponent.MAX_STACK_SIZE, maxStackSize));
        return getThis();
    }

    public T hideExtraTooltip() {
        itemChangers.add(ItemStack::withoutExtraTooltip);
        return getThis();
    }

    public T textures(int textures) {
        itemChangers.add(itemStack -> itemStack.with(ItemComponent.CUSTOM_MODEL_DATA, textures));
        return getThis();
    }

    public T glowing() {
        itemChangers.add(itemStack -> itemStack.withGlowing(true));
        return getThis();
    }

    public T set(@NotNull DataComponent<T> component, T value) {
        itemChangers.add(itemStack -> itemStack.with(component, value));
        return getThis();
    }

    public T setTag(@NotNull Tag<T> tag, @NotNull T value) {
        itemChangers.add(itemStack -> itemStack.withTag(tag, value));
        return getThis();
    }

    public T remove(@NotNull DataComponent<T> component) {
        itemChangers.add(itemStack -> itemStack.without(component));
        return getThis();
    }

    public T unbreakable() {
        itemChangers.add(itemStack -> itemStack.with(ItemComponent.UNBREAKABLE, Unbreakable.DEFAULT));
        return getThis();
    }

    public T maxDurability(int maxDurability) {
        itemChangers.add(itemStack -> itemStack.with(ItemComponent.MAX_DAMAGE, maxDurability));
        return getThis();
    }

    public T durability(int durability) {
        itemChangers.add(itemStack -> itemStack.with(ItemComponent.DAMAGE, durability));
        return getThis();
    }

    public T addToJediItem(String techName, JediItem.Category category, JediItem.Rare rare) {
        itemChangers.add(itemStack -> itemStack.with(builder -> {
            builder.set(jediItem.getTECH_NAME(), techName);
            builder.set(jediItem.getCATEGORY(), category.getNamespace());
            builder.set(jediItem.getRARE(), rare.getNamespace());
        }));
        return getThis();
    }

    public T level(int level) {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getLEVEL(), level));
        return getThis();
    }

    public T faction(@NotNull Faction faction) {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getFACTION(), faction.name()));
        return getThis();
    }

    public T faction() {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getFACTION(), "null"));
        return getThis();
    }

    public T uid() {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getUID(), 0));
        return getThis();
    }

    public T owner() {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getOWNER(), "null"));
        return getThis();
    }

    public T nodrop() {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getNODROP(), true));
        return getThis();
    }

    public T artefact() {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getARTEFACT(), true));
        return getThis();
    }

    public T cap() {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getCAP(), true));
        return getThis();
    }

    public T power(int power) {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getPOWER(), power));
        return getThis();
    }

    public T vampirism(double vampirism) {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getVAMPIRISM(), vampirism));
        return getThis();
    }

    public T starTrack() {
        itemChangers.add(itemStack -> itemStack.with(builder -> {
            builder.set(jediItem.getMOB_KILLS(), 0);
            builder.set(jediItem.getPLAYER_KILLS(), 0);
        }));
        return getThis();
    }

    public T damage(double damage, double criticalDamage, double criticalChance) {
        itemChangers.add(itemStack -> itemStack.with(builder -> {
            builder.set(jediItem.getDAMAGE(), damage);
            builder.set(jediItem.getCRITICAL_DAMAGE(), criticalDamage);
            builder.set(jediItem.getCRITICAL_CHANCE(), criticalChance);
        }));
        return getThis();
    }

    public T cooldown(int cooldown) {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getCOOLDOWN(), cooldown));
        return getThis();
    }

    public T keyName(@NotNull CaseModule.CaseType caseType) {
        itemChangers.add(itemStack -> itemStack.withTag(jediItem.getKEY(), caseType.name()));
        return getThis();
    }

    public T itemLevel(int level) {
        itemChangers.add(itemStack -> {
            Component customName = itemStack.get(ItemComponent.CUSTOM_NAME);
            itemStack = itemStack.without(ItemComponent.CUSTOM_NAME);
            assert customName != null;
            return itemStack.with(ItemComponent.CUSTOM_NAME,
                    customName.append(Component.text(" " + level + " Lvl", NamedTextColor.GOLD)));
        });
        return getThis();
    }

    @SuppressWarnings("unchecked")
    T getThis() {
        return (T) this;
    }

    protected abstract ItemStack getItemStack();

    public ItemStack build() {
        ItemStack temporaryItem = getItemStack();
        for (Function<ItemStack, ItemStack> changer : itemChangers) {
            temporaryItem = changer.apply(temporaryItem);
        }

        Double damage = temporaryItem.getTag(jediItem.getDAMAGE());
        Double criticalChance = temporaryItem.getTag(jediItem.getCRITICAL_CHANCE());
        Double criticalDamage = temporaryItem.getTag(jediItem.getCRITICAL_DAMAGE());
        if (damage != null) {
            appendLore(Component.text(" "),
                    Component.text("⚔", NamedTextColor.RED)
                            .append(Component.text(" Урон: ", NamedTextColor.GRAY))
                            .append(Component.text(damage, NamedTextColor.AQUA, TextDecoration.BOLD)),
                    Component.text("⚡", NamedTextColor.YELLOW)
                            .append(Component.text(" Крит. урон: ", NamedTextColor.GRAY))
                            .append(Component.text(criticalDamage + " (" + criticalChance + "%)", NamedTextColor.LIGHT_PURPLE, TextDecoration.BOLD)));
        }

        Integer mobKills = temporaryItem.getTag(jediItem.getMOB_KILLS());
        Integer playerKills = temporaryItem.getTag(jediItem.getPLAYER_KILLS());
        if (mobKills != null) {
            appendLore(Component.text(" "),
                    Component.text("Убито мобов: " + mobKills, NamedTextColor.DARK_RED),
                    Component.text("Убито игроков: " + playerKills, NamedTextColor.DARK_RED));
        }

        Integer level = temporaryItem.getTag(jediItem.getLEVEL());
        if (level != null) {
            appendLore(Component.text(" "),
                    Component.text("✓", NamedTextColor.GREEN).append(Component.text(" Требуемый уровень: ", NamedTextColor.GRAY))
                            .append(Component.text(level, NamedTextColor.GREEN, TextDecoration.BOLD)));
        }

        String faction = temporaryItem.getTag(jediItem.getFACTION());
        if (faction != null) {
            appendLore(Component.text(" "),
                    Component.text("☬", NamedTextColor.GREEN)
                            .append(Component.text(" Доступно для " + ("null".equals(faction) ? "Всех" : faction.toUpperCase()), NamedTextColor.GRAY)));
        }

        String owner = temporaryItem.getTag(jediItem.getOWNER());
        if (owner != null) {
            appendLore(Component.text(" "),
                    Component.text("Владелец: " + owner, NamedTextColor.LIGHT_PURPLE));
        }

        Integer uid = temporaryItem.getTag(jediItem.getUID());
        if (uid != null) {
            appendLore(Component.text(" "),
                    Component.text("UID: " + uid, NamedTextColor.BLUE));
        }

        String category = temporaryItem.getTag(jediItem.getCATEGORY());
        String rare = temporaryItem.getTag(jediItem.getRARE());
        if (category != null) {
            JediItem.Category catEnum = JediItem.Category.getByNamespace(category);
            appendLore(Component.text("Категория: ").append(catEnum.getName()));
        }

        if (rare != null) {
            JediItem.Rare rareEnum = JediItem.Rare.getByRarity(rare);
            appendLore(Component.text("Редкость: ").append(rareEnum.getName()));
        }

        Boolean nodrop = temporaryItem.getTag(jediItem.getNODROP());
        if (Boolean.TRUE.equals(nodrop)) {
            appendLore(Component.text(" "),
                    Component.text("nodrop", NamedTextColor.LIGHT_PURPLE, TextDecoration.ITALIC));
        }

        Boolean artefact = temporaryItem.getTag(jediItem.getARTEFACT());
        if (Boolean.TRUE.equals(artefact)) {
            appendLore(Component.text(" "),
                    Component.text("artefact", NamedTextColor.LIGHT_PURPLE, TextDecoration.ITALIC));
        }

        String techName = temporaryItem.getTag(jediItem.getTECH_NAME());
        jediItem.getServerItems().put(techName, temporaryItem);

        return temporaryItem;
    }

    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    public static class MaterialItemBuilder extends ItemBuilder<MaterialItemBuilder> {

        private Material material;

        public MaterialItemBuilder setMaterial(Material material) {
            this.material = material;
            return getThis();
        }

        @Override
        protected ItemStack getItemStack() {
            return ItemStack.of(material);
        }
    }

    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    public static class ItemStackItemBuilder extends ItemBuilder<ItemStackItemBuilder> {

        @Getter(AccessLevel.PROTECTED)
        private ItemStack itemStack;

        public ItemStackItemBuilder setItemStack(ItemStack itemStack) {
            this.itemStack = itemStack;
            return getThis();
        }
    }
}

В методе build не срабатывают методы appendLore, исправь.