package dev.okenote.services.menu;

import lombok.AccessLevel;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerChatEvent;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.InventoryType;
import net.minestom.server.item.ItemStack;

import java.util.*;
import java.util.function.Consumer;
import java.util.stream.IntStream;
import java.util.stream.Stream;

@FieldDefaults(level = AccessLevel.PRIVATE)
public class Menu {

    final Component title;
    InventoryType inventoryType;

    final Map<Integer, Map<Integer, Button>> menuButton = new HashMap<>();

    final Map<Integer, Inventory> pageMap = new HashMap<>();
    int currentPage;
    Integer[] slots;

    final List<Consumer<Player>> onOpen = new ArrayList<>();
    final Map<Integer, Map<Integer, Consumer<Player>>> onClick = new HashMap<>();
    //final Map<Integer, Consumer<Player>> onClick = new HashMap<>();
    final List<Consumer<Player>> onClose = new ArrayList<>();

    @Setter
    boolean toolbar = false, fullPage = false;

    public Menu(Component title, InventoryType inventoryType) {
        this.title = title;
        this.inventoryType = inventoryType;
    }

    public void addSlots(int[][] ranges) {
        ranges = ranges == null ? new int[][]{{1, 5}} : ranges;
        Integer[] newSlots = Arrays.stream(ranges)
                .flatMapToInt(range -> IntStream.rangeClosed(range[0], range[1]))
                .boxed()
                .toArray(Integer[]::new);
        this.slots = slots == null ? newSlots : Stream.concat(Arrays.stream(slots), Arrays.stream(newSlots)).toArray(Integer[]::new);
    }

    public void setButton(int page, int slot, Button button) {
        menuButton.computeIfAbsent(page, k -> new HashMap<>()).put(slot, button);
        if (button.getOnClick() != null)
            onClick.computeIfAbsent(page, k -> new HashMap<>()).put(slot, button.getOnClick());
        //onClick.put(page, button.getOnClick());
    }

    public void setButton(int slot, Button button) {
        setButton(1, slot, button);
    }

    public void setButtonInAllPages(int slot, Button button) {
        for (int i = 1; i <= menuButton.size(); i++) {
            setButton(i, slot, button);
        }
    }

    public void addButton(Button button) {
        for (int page = 1; ; page++) {
            for (int slot : slots) {
                if (!menuButton.computeIfAbsent(page, k -> new HashMap<>()).containsKey(slot)) {
                    setButton(page, slot, button);
                    return;
                }
            }
        }
    }

    public void onOpen(Consumer<Player> consumer) {
        onOpen.add(consumer);
    }

    public void onClose(Consumer<Player> consumer) {
        onClose.add(consumer);
    }

    private void createMenu() {
        int totalPages = menuButton.size();
        int totalButton = Collections.max(menuButton.get(totalPages).keySet());

        if (totalPages > 1) toolbar = true;

        if (inventoryType == null) {
            for (int i = 1; i <= totalPages; i++) {

                if (fullPage) inventoryType = InventoryType.CHEST_6_ROW;
                else {
                    if (i != totalPages) {
                        inventoryType = InventoryType.CHEST_6_ROW;
                    } else {
                        if (toolbar) inventoryType = addRow(totalButton + 1);
                        else inventoryType = getRowForSlot(totalButton);
                    }
                }

                Inventory inventory = new Inventory(inventoryType, title);

                pageMap.put(i, inventory);

                if (toolbar) setupToolBar(inventory, i, totalPages);
            }
        } else {
            for (int i = 1; i <= totalPages; i++) {
                Inventory inventory = new Inventory(inventoryType, title);

                pageMap.put(i, inventory);

                if (toolbar) setupToolBar(inventory, i, totalPages);
            }
        }
    }

    private void fillItems() {
        menuButton.entrySet().stream()
                .filter(entry -> !entry.getValue().isEmpty())
                .forEach(menuEntry -> {
                    int menuPage = menuEntry.getKey();
                    Inventory menu = pageMap.get(menuPage);

                    Map<Integer, Button> buttons = menuEntry.getValue();

                    List<Integer> keysToRemove = new ArrayList<>();

                    buttons.forEach((slotIndex, button) -> {
                        menu.setItemStack(slotIndex, button.getIcon());

                        Consumer<Player> clickAction = null;
                        Map<Integer, Consumer<Player>> clickMap = onClick.get(menuPage);
                        if (clickMap != null) {
                            clickAction = clickMap.get(slotIndex);
                        }

                        Consumer<Player> finalClickAction = clickAction;

                        menu.addInventoryCondition((player1, slot1, clickType, inventoryConditionResult) -> {
                            if (slot1 == slotIndex) {
                                if (finalClickAction != null) {
                                    finalClickAction.accept(player1);
                                }
                                inventoryConditionResult.setCancel(true);
                            }
                        });

                        keysToRemove.add(slotIndex);
                    });

                    keysToRemove.forEach(buttons::remove);
                });
    }

    public void open(Player player) {
        open(player, 1);
    }

    public void open(Player player, int page) {
        createMenu();
        fillItems();

        onOpen.stream()
                .findFirst()
                .ifPresent(openAction -> openAction.accept(player));

        currentPage = page;

        player.openInventory(pageMap.get(currentPage));
    }

    private void setupToolBar(Inventory inventory, int currentPage, int totalPages) {
        if (currentPage > 1)
            setToolBarItem(inventory, Button.BACK_BUTTON_ITEM, 7, player -> forwardPage(player, false));

        if (currentPage < totalPages)
            setToolBarItem(inventory, Button.NEXT_BUTTON_ITEM, 3, player -> forwardPage(player, true));

        setToolBarItem(inventory, Button.UPDATE_BUTTON_ITEM, 5, inventory::update);

        setToolBarItem(inventory,
                Button.INFO_BUTTON_ITEM.builder()
                        .lore(Component.text("")).build(), 2, player -> {

                });

        setToolBarItem(inventory, Button.CLOSE_BUTTON_ITEM, 1, player -> {
            player.closeInventory();
            if (onClose.getFirst() != null) {
                Consumer<Player> closeAction = onClose.getFirst();

                closeAction.accept(player);
            }
        });

        setToolBarItem(inventory, Button.SORT_BUTTON_ITEM, 8, player -> {

        });

        setToolBarItem(inventory, Button.SEARCH_BUTTON_ITEM, 9, player -> {
            player.closeInventory();

            player.sendMessage(Component.text("Введите запрос в чат.", NamedTextColor.GREEN));

            Map<Player, Component> searchMap = new HashMap<>();

            player.eventNode().addListener(PlayerChatEvent.class, event -> {
                searchMap.put(player, Component.text(event.getMessage()));
                event.setCancelled(true);


            });
        });
    }

    private void setToolBarItem(Inventory inventory, ItemStack button, int slotSpecial, Consumer<Player> consumer) {
        int specialSlot = inventory.getSize() - slotSpecial;
        inventory.setItemStack(specialSlot, button);
        inventory.addInventoryCondition((player, slot, clickType, inventoryConditionResult) -> {
            if (slot == specialSlot) {
                inventoryConditionResult.setCancel(true);
                consumer.accept(player);
            }
        });
    }

    private void forwardPage(Player player, boolean forward) {
        int newPage = forward ? currentPage + 1 : currentPage - 1;
        if (pageMap.containsKey(newPage)) {
            currentPage = newPage;
            player.openInventory(pageMap.get(currentPage));
        }
    }

    private final InventoryType[] rows = {
            InventoryType.CHEST_1_ROW, InventoryType.CHEST_2_ROW, InventoryType.CHEST_3_ROW,
            InventoryType.CHEST_4_ROW, InventoryType.CHEST_5_ROW, InventoryType.CHEST_6_ROW};

    private InventoryType addRow(int size) {
        int rowIndex = ((size - 1) % 54) / 9;
        return rows[(rowIndex + (rows[rowIndex] == InventoryType.CHEST_6_ROW ? 0 : 1)) % rows.length];
    }

    private InventoryType getRowForSlot(int slot) {
        return rows[(slot / 9) % rows.length];
    }
}

Оптимизируй код