/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.schematicsystem.commands.schematiccommand;

import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.network.NetworkSender;
import de.steamwar.network.packets.client.PrepareSchemPacket;
import de.steamwar.providers.BauServerInfo;
import de.steamwar.schematicsystem.CheckSchemType;
import de.steamwar.schematicsystem.SchematicSystem;
import de.steamwar.schematicsystem.autocheck.AutoChecker;
import de.steamwar.schematicsystem.autocheck.AutoCheckerResult;
import de.steamwar.sql.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.With;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

import java.io.IOException;
import java.util.*;
import java.util.function.IntFunction;
import java.util.function.UnaryOperator;
import java.util.logging.Level;
import java.util.stream.Collectors;

public class SchematicCommandUtils {

    public static final int CHUNK_SIZE = 15;
    private static final List<Player> PUBLIC_TOGGLED = new ArrayList<>();
    private static final List<String> FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public"));
    private static final Map<Player, CachedSchematicList> CACHED_LISTS = new HashMap<>();


    private SchematicCommandUtils() {
    }

    public static boolean invalidSchemName(Player player, String[] layers) {
        for (String layer : layers) {
            if (layer.isEmpty()) {
                SchematicSystem.MESSAGE.send("UTIL_NAME_REQUIRED", player);
                return true;
            }
            if(layer.length() > 64) {
                SchematicSystem.MESSAGE.send("UTIL_NAME_TOO_LONG", player);
                return true;
            }
            if (layer.contains("/") ||
                    layer.contains("\\") ||
                    layer.contains("<") ||
                    layer.contains(">") ||
                    layer.contains("^") ||
                    layer.contains("°") ||
                    layer.contains("'") ||
                    layer.contains("\"") ||
                    layer.contains(" ")) {
                SchematicSystem.MESSAGE.send("UTIL_NAME_INVALID_CHAR", player);
                return true;
            }
            if (FORBIDDEN_NAMES.contains(layer.toLowerCase())) {
                SchematicSystem.MESSAGE.send("UTIL_NAME_FORBIDDEN", player, layer);
                return true;
            }
        }
        return false;
    }

    public static void createCachedSchemList(Player player, List<SchematicNode> nodes, int chunk, SchematicNode parent, SchematicListBehavior behavior) {
        CachedSchematicList cachedSchematicList = new CachedSchematicList(nodes, chunk, (int) Math.ceil(nodes.size() / (double) CHUNK_SIZE), parent, behavior.withPageCommandGen((page) -> "/schematic page " + (page - 1)));
        CACHED_LISTS.put(player, cachedSchematicList);
        renderSchemlist(player, cachedSchematicList.nodes, chunk, parent, cachedSchematicList.behavior);
    }

    public static void cachedSchemList(Player player, int chunk) {
        CACHED_LISTS.computeIfPresent(player, (p, cachedSchematicList) -> {
            renderSchemlist(player, cachedSchematicList.nodes, chunk, null, cachedSchematicList.behavior);
            return cachedSchematicList.withPage(chunk);
        });
    }

    public static void renderSchemlist(Player player,
                                       List<SchematicNode> nodes,
                                       int chunk,
                                       SchematicNode parent,
                                       SchematicListBehavior behavior) {
        SteamwarUser user = getUser(player);
        int pageCount = (int) Math.ceil(nodes.size() / (double) CHUNK_SIZE);

        SchematicSystem.MESSAGE.sendPrefixless("UTIL_LIST_HEAD", player, nodes.size());
        String breadcrumbs = parent == null ? "" : parent.generateBreadcrumbs();
        SchematicSystem.MESSAGE.sendPrefixless("UTIL_LIST_PATH", player, (breadcrumbs.isEmpty() ? "/" : breadcrumbs));
        if (!breadcrumbs.isEmpty()) {
            String str = breadcrumbs.substring(0, Math.max(0, breadcrumbs.substring(0, breadcrumbs.length() - 1).lastIndexOf("/")));
            SchematicSystem.MESSAGE.sendPrefixless("UTIL_LIST_BACK", player, SchematicSystem.MESSAGE.parse("UTIL_LIST_BACK_HOVER", player, str), new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/schem list " + (behavior.isPublics() ? "public" : "") + " " + str));
        }
        for (int i = chunk * CHUNK_SIZE; i < nodes.size() && i < (chunk + 1) * CHUNK_SIZE; i++) {
            SchematicNode node = nodes.get(i);

            StringBuilder nodeString = new StringBuilder();

            if (node.isDir()) {
                nodeString.append(SchematicSystem.MESSAGE.parse("UTIL_LIST_DIR", player)).append(" ");
            } else {
                SchematicType type = node.getSchemtype();
                if (type != SchematicType.Normal) {
                    nodeString.append(SchematicSystem.MESSAGE.parse("UTIL_LIST_TYPE", player, type.getKuerzel())).append(" ");
                }
            }
            nodeString.append(SchematicSystem.MESSAGE.parse("UTIL_LIST_BASE", player));

            nodeString.append(behavior.getRenderHook().apply(node.getName()));

            if (node.isDir()) {
                nodeString.append("/");
            }

            if (node.getOwner() != user.getId()) {
                nodeString.append(" ").append(SchematicSystem.MESSAGE.parse("UTIL_LIST_FROM", player, SteamwarUser.get(node.getOwner()).getUserName()));
            }
            TextComponent schematics = new TextComponent(nodeString.toString());

            if (node.isDir()) {
                schematics.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(SchematicSystem.MESSAGE.parse("UTIL_LIST_OPEN_DIR", player)).create()));
                schematics.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/schem list " + (behavior.isPublics() ? "public " : "") + (behavior.nonCachedBreadcrumbs ? node.generateBreadcrumbs() : breadcrumbs + node.getName() + "/")));
            } else {
                schematics.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(SchematicSystem.MESSAGE.parse("UTIL_LIST_OPEN_SCHEM", player)).create()));
                schematics.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/schem info " + (behavior.isPublics() ? "public " : "") + (behavior.nonCachedBreadcrumbs ? node.generateBreadcrumbs() : breadcrumbs + node.getName())));
            }

            player.spigot().sendMessage(schematics);
        }

        if (parent != null && parent.getOwner() != user.getId() && NodeMember.getNodeMember(parent.getId(), user.getId()) != null) {
            TextComponent deadd = SchematicSystem.MESSAGE.parseToComponent("UTIL_LIST_REMOVE", false, player);
            deadd.setColor(ChatColor.RED);
            deadd.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,  new TextComponent[] {SchematicSystem.MESSAGE.parseToComponent("UTIL_LIST_REMOVE_HOVER", false, player)}));
            deadd.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/schem delete " + parent.generateBreadcrumbs()));
            player.spigot().sendMessage(deadd);
        }

        TextComponent beforePage = SchematicSystem.MESSAGE.parseToComponent("UTIL_LIST_BACK_ARROW", false, player);
        beforePage.addExtra(" ");
        if (chunk > 0) {
            beforePage.setColor(ChatColor.YELLOW);
            beforePage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[] {SchematicSystem.MESSAGE.parseToComponent("UTIL_LIST_BACK_ARROW_HOVER", false, player)}));
            beforePage.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, behavior.getPageCommandGen().apply(chunk)));
        } else {
            beforePage.setColor(ChatColor.RED);
        }

        TextComponent nextPage = SchematicSystem.MESSAGE.parseToComponent("UTIL_LIST_NEXT", false, player, chunk + 1, Math.max(pageCount, 1));
        if (chunk < pageCount - 1) {
            nextPage.setColor(ChatColor.YELLOW);
            nextPage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[] {SchematicSystem.MESSAGE.parseToComponent("UTIL_LIST_NEXT_HOVER", false, player)}));
            nextPage.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, behavior.getPageCommandGen().apply(chunk + 2)));
        } else {
            nextPage.setColor(ChatColor.RED);
        }

        beforePage.addExtra(nextPage);
        player.spigot().sendMessage(beforePage);
    }

    @Getter
    @Builder(setterPrefix = "set")
    @With
    public static class SchematicListBehavior {
        public static final SchematicListBehavior DEFAULT = SchematicListBehavior.builder().build();

        @Builder.Default private boolean showPath = true;
        @Builder.Default private boolean publics = false;
        @Builder.Default private boolean nonCachedBreadcrumbs = false;
        @Builder.Default private UnaryOperator<String> renderHook = s -> s;
        @Builder.Default private IntFunction<String> pageCommandGen = value -> "/schem list " + value;
    }

    public static void printSchemInfo(Player player, SchematicNode node) {
        SteamwarUser user = getUser(player);
        SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_SCHEM", player, node.generateBreadcrumbs());
        if(node.getOwner() == user.getId()) {
            player.spigot().sendMessage(
                    new ComponentBuilder(SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_NAME", false, player, node.getName()))
                            .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[] {SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_RENAME_HOVER", false, player)}))
                            .event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/schem rename " + node.generateBreadcrumbs() + " "))
                            .create()
            );
        } else {
            SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_NAME", player, node.getName());
        }
        SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_OWNER", player, node.getOwner() == user.getId() ? user.getUserName() : SteamwarUser.get(node.getOwner()).getUserName());
        if(node.getOwner() == user.getId()) {
            player.spigot().sendMessage(
                    new ComponentBuilder(SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_PARENT", false, player, node.getParent() == null ? "/" : node.getParentNode().generateBreadcrumbs()))
                            .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[] {SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_MOVE_HOVER", false, player)}))
                            .event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/schem move " + node.generateBreadcrumbs() + " " + node.generateBreadcrumbs().replace("/" + node.getName(), "")))
                            .create()
            );
        } else {
            SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_PARENT", player, node.getParent() == null ? "/" : node.getParentNode().generateBreadcrumbs());
        }
        player.spigot().sendMessage(
                new ComponentBuilder(SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_REVISIONS", false, player, NodeData.getRevisions(node)))
                        .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[] {SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_REVISIONS_HOVER", false, player)}))
                        .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/schem revisions " + node.generateBreadcrumbs()))
                        .create());
        SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_UPDATED", player, node.getLastUpdate());
        if (!node.isDir()) {
            if(node.getOwner() == user.getId()) {
                player.spigot().sendMessage(
                        new ComponentBuilder(SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_TYPE", false, player, node.getSchemtype().name()))
                                .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[] {SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_TYPE_HOVER", false, player)}))
                                .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/schem changetype " + node.generateBreadcrumbs()))
                                .create()
                );
            } else {
                SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_TYPE", player, node.getSchemtype().name());
            }
            if (node.getRank() > 0) {
                SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_RANK", player, node.getRank());
            }

            if (node.getSchemtype().fightType()) {
                SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_COLOR", player, SchematicSystem.MESSAGE.parse(node.replaceColor() ? "ON" : "OFF", player));
                SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_REPLAY", player, SchematicSystem.MESSAGE.parse(node.allowReplay() ? "ON" : "OFF", player));
                SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_ELO", player, node.getElo(Season.getSeason()));
            }

            SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_FORMAT", player, node.getFileEnding());
            CheckedSchematic.getLastDeclinedOfNode(node.getId()).stream().findFirst().ifPresent(checkedSchematic -> SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_STATUS", player, checkedSchematic.getEndTime(), checkedSchematic.getDeclineReason()));
        } else {
            SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_TYPE", player, SchematicSystem.MESSAGE.parse("UTIL_INFO_TYPE_DIR", player));
        }


        if(node.getOwner() == user.getId()) {
            ComponentBuilder memberBuilder = new ComponentBuilder(SchematicSystem.MESSAGE.parse("UTIL_INFO_MEMBER_STRING", player) + " ").color(ChatColor.GRAY);
            NodeMember.getNodeMembers(node.getId()).forEach(nodeMember -> {
                SteamwarUser member = SteamwarUser.get(nodeMember.getMember());
                memberBuilder.append(member.getUserName())
                        .color(ChatColor.YELLOW)
                        .event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/schem delmember " + node.generateBreadcrumbs() + " " + member.getUserName()))
                        .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[]{SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_REMOVE_HOVER", false, player, member.getUserName())}))
                        .append(" ", ComponentBuilder.FormatRetention.NONE);
            });
            memberBuilder.append("(+)", ComponentBuilder.FormatRetention.NONE)
                    .color(ChatColor.GREEN)
                    .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[]{SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_ADD_HOVER", false, player)}))
                    .event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/schem addmember " + node.generateBreadcrumbs() + " "));
            player.spigot().sendMessage(memberBuilder.create());
        } else {
            List<String> schematicMembers = new ArrayList<>();
            NodeMember.getNodeMembers(node.getId()).forEach(nodeMember -> schematicMembers.add(SteamwarUser.get(nodeMember.getMember()).getUserName()));
            if(!schematicMembers.isEmpty()) {
                SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_MEMBER", player, schematicMembers.stream().reduce((s, s2) -> s + ", " + s2).orElse(""));
            }
        }

        TextComponent base = new TextComponent();

        if (!node.isDir()) {
            TextComponent load = SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_LOAD", false, player);
            load.setColor(ChatColor.GREEN);
            load.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[]{SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_LOAD_HOVER", false, player)}));
            load.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/schem load " + (node.getOwner() == 0 ? "public " : "") + node.generateBreadcrumbs()));
            base.addExtra(load);

            if (node.getOwner() == user.getId()) {
                TextComponent download = SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_DOWNLOAD", false, player);
                download.setColor(ChatColor.GOLD);
                download.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[]{SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_DOWNLOAD_HOVER", false, player)}));
                download.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/schem download " + node.generateBreadcrumbs()));
                base.addExtra(download);
            }
        }

        if (node.getOwner() != 0) {
            TextComponent delete = SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_DELETE", false, player);
            delete.setColor(ChatColor.DARK_RED);
            delete.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[]{SchematicSystem.MESSAGE.parseToComponent("UTIL_INFO_ACTION_DELETE_HOVER", false, player)}));
            delete.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/schem delete " + node.generateBreadcrumbs()));
            base.addExtra(delete);
        }

        if (!base.getExtra().isEmpty()) {
            player.spigot().sendMessage(base);
        }
    }

    public static void check(Player player, Clipboard clipboard, CheckSchemType type, String schemName, boolean gui) {
        AutoCheckerResult result = AutoChecker.check(clipboard, type);
        if(!result.isOk()) {
            result.sendErrorMessage(player, schemName);
        } else {
            SchematicSystem.MESSAGE.send("UTIL_CHECK_SUCCESS", player, schemName);
        }
    }

    public static SchematicNode mkdirs(String[] layers, SteamwarUser user, int minus) {
        Optional<SchematicNode> currentNode = Optional.empty();
        for (int i = 0; i < layers.length - minus; i++) {
            int finalI = i;
            List<SchematicNode> nodes = SchematicNode.list(user, currentNode.map(SchematicNode::getId).orElse(null)).stream()
                    .filter(node -> node.getName().equalsIgnoreCase(layers[finalI]))
                    .collect(Collectors.toList());
            if (nodes.isEmpty()) {
                currentNode = Optional.ofNullable(SchematicNode.byIdAndUser(user, SchematicNode.createSchematicDirectory(user.getId(), layers[i], currentNode.map(SchematicNode::getId).orElse(null)).getId()));
            } else {
                if(nodes.stream().anyMatch(SchematicNode::isDir)) {
                    currentNode = nodes.stream().filter(SchematicNode::isDir).findFirst();
                } else {
                    currentNode = Optional.ofNullable(SchematicNode.byIdAndUser(user, SchematicNode.createSchematicDirectory(user.getId(), layers[i] + "-dir-" + System.currentTimeMillis() % 100, currentNode.map(SchematicNode::getId).orElse(null)).getId()));
                }
            }
        }
        return currentNode.orElse(null);
    }

    public static SteamwarUser getUser(Player player) {
        if (PUBLIC_TOGGLED.contains(player)) {
            return SteamwarUser.get(0);
        } else {
            return SteamwarUser.get(player.getUniqueId());
        }
    }

    public static boolean togglePublic(Player player) {
        if (PUBLIC_TOGGLED.contains(player)) {
            PUBLIC_TOGGLED.remove(player);
            return false;
        } else {
            PUBLIC_TOGGLED.add(player);
            return true;
        }
    }

    public static void turnOffPublic(Player player) {
        PUBLIC_TOGGLED.remove(player);
    }

    public static void loadSchem(Player player, SchematicNode node, int revision) {
        SteamwarUser user = getUser(player);
        if(BauServerInfo.isBauServer() && BauServerInfo.getOwnerId() != user.getId() &&
                (Punishment.isPunished(user, Punishment.PunishmentType.NoSchemSharing, punishment ->
                        SchematicSystem.MESSAGE.send("UTIL_LOAD_NOT_HERE", player)) ||
                Punishment.isPunished(SteamwarUser.get(BauServerInfo.getOwnerId()), Punishment.PunishmentType.NoSchemReceiving, punishment ->
                        SchematicSystem.MESSAGE.send("UTIL_LOAD_NOT_HERE_ALL", player)))) {
            return;
        }
        if (node.isDir()) {
            SchematicSystem.MESSAGE.send("UTIL_LOAD_DIR", player);
            return;
        }

        try {
            new SchematicData(node, revision).loadToPlayer(player);
            SchematicSystem.MESSAGE.send("UTIL_LOAD_DONE", player, node.getName());
            Bukkit.getLogger().log(Level.INFO, "{0} has loaded Schematic {1} {2}", new Object[]{player.getName(), node.getId(), node.getName()});
        } catch (NoClipboardException e) {
            SchematicSystem.MESSAGE.send("UTIL_LOAD_NO_DATA", player);
        } catch (IllegalArgumentException e) {
            SchematicSystem.MESSAGE.send("UTIL_LOAD_ILLEGAL_REVISION", player, revision);
        } catch (Exception e) {
            SchematicSystem.MESSAGE.send("UTIL_LOAD_ERROR", player);
            Bukkit.getLogger().log(Level.INFO, e.getMessage(), e);
        }
    }

    public static void download(Player player, SchematicNode node) {
        SteamwarUser user = getUser(player);
        if (Punishment.isPunished(user, Punishment.PunishmentType.NoSchemSharing, punishment -> {
            SchematicSystem.MESSAGE.send("UTIL_DOWNLOAD_PUNISHED", player, punishment.getReason());
        })) {
            return;
        }
        if (node.getOwner() != user.getId()) {
            SchematicSystem.MESSAGE.send("UTIL_DOWNLOAD_NOT_OWN", player);
            return;
        }

        SchematicSystem.MESSAGE.send("UTIL_DOWNLOAD_LINK", player);
        player.sendMessage(NodeDownload.getLink(node));
    }

    public static void changeType(Player player, SchematicNode node, SchematicType type, SchematicCommand.Extend extend) {
        SteamwarUser user = getUser(player);
        if (node.getOwner() != user.getId()) {
            SchematicSystem.MESSAGE.send("UTIL_TYPE_NOT_OWN", player);
            return;
        }

        if (node.isDir()) {
            SchematicSystem.MESSAGE.send("UTIL_TYPE_DIR", player);
            return;
        }

        if (!type.isAssignable()) {
            SchematicSystem.MESSAGE.send("UTIL_TYPE_NOT_ASSIGNABLE", player);
            return;
        }

        if (node.getSchemtype().equals(type)) {
            SchematicSystem.MESSAGE.send("UTIL_TYPE_ALREADY", player);
            return;
        }

        node.setPrepared(false);

        if (type.writeable()) {
            node.setSchemtype(type);
            SchematicSystem.MESSAGE.send("UTIL_TYPE_DONE", player);
        } else if (type.fightType()) {
            if (Punishment.isPunished(user,
                    Punishment.PunishmentType.NoSchemSubmitting,
                    punishment -> SchematicSystem.MESSAGE.send(punishment.isPerma() ? "UTIL_TYPE_PUNISHED_PERMA" : "UTIL_TYPE_PUNISHED", player, punishment.getReason(), punishment.getEndTime()))) {
                return;
            }

            if (node.getSchemtype().check()) {
                SchematicSystem.MESSAGE.send("UTIL_TYPE_FIGHT_ALREADY", player);
                return;
            }

            if (SchematicType.values().stream().filter(SchematicType::check)
                    .map(schematicType -> SchematicNode.getAllSchematicsOfType(user.getId(), schematicType.toDB()))
                    .map(List::size)
                    .reduce(Integer::sum)
                    .orElse(0) >= 3) {
                SchematicSystem.MESSAGE.send("UTIL_TYPE_TOO_MANY", player);
                return;
            }

            CheckSchemType checkSchemType = CheckSchemType.get(type);
            if (checkSchemType.isAfterDeadline()) {
                SchematicSystem.MESSAGE.send("UTIL_TYPE_AFTER_DEADLINE", player, checkSchemType.getDeadline());
                return;
            }

            AutoCheckerResult result = null;
            try {
                result = AutoChecker.check(new SchematicData(node).load(), checkSchemType);
            } catch (IOException e) {
                SchematicSystem.MESSAGE.send("UTIL_LOAD_ERROR", player);
                SchematicSystem.getInstance().getLogger().throwing(SchematicCommandUtils.class.getName(), "changeType", e);
                return;
            }
            if (!result.isOk()) {
                SchematicSystem.MESSAGE.send("UTIL_TYPE_ERROR", player);
                result.sendErrorMessage(player, node.getName());
                return;
            }

            if (extend == null) {
                submitSchemGUI(player, node, type);
            } else if (extend == SchematicCommand.Extend.AUSFAHREN) {
                NetworkSender.send(new PrepareSchemPacket(SteamwarUser.get(player.getUniqueId()).getId(), node.getId(), type.toDB()));
                SchematicSystem.MESSAGE.send("UTIL_TYPE_EXTEND", player);
            } else {
                node.setSchemtype(type.checkType());
                SchematicSystem.MESSAGE.send("UTIL_SUBMIT_DIRECT_DONE", player);
            }
        }
    }

    private static void submitSchemGUI(Player player, SchematicNode node, SchematicType type) {
        SWInventory inv = new SWInventory(player, 9, SchematicSystem.MESSAGE.parse("UTIL_SUBMIT_TITLE", player));
        inv.setItem(0, SWItem.getMaterial("SIGN"), SchematicSystem.MESSAGE.parse(node.allowReplay()?"UTIL_SUBMIT_REPLAY_ON":"UTIL_SUBMIT_REPLAY_OFF", player), click -> {
            node.setAllowReplay(!node.allowReplay());
            submitSchemGUI(player, node, type);
        });
        inv.setItem(1, SWItem.getMaterial(node.replaceColor() ? "PINK_WOOL" : "LIGHT_GRAY_WOOL"), SchematicSystem.MESSAGE.parse(node.replaceColor()?"UTIL_SUBMIT_COLOR_ON":"UTIL_SUBMIT_COLOR_OFF", player), click -> {
            node.setReplaceColor(!node.replaceColor());
            submitSchemGUI(player, node, type);
        });
        inv.setItem(7, SWItem.getDye(7), (byte) 7, SchematicSystem.MESSAGE.parse("UTIL_SUBMIT_DIRECT", player), click -> {
            node.setSchemtype(type.checkType());
            SchematicSystem.MESSAGE.send("UTIL_SUBMIT_DIRECT_DONE", player);
            player.closeInventory();
        });
        inv.setItem(8, SWItem.getDye(10), (byte) 10, SchematicSystem.MESSAGE.parse("UTIL_SUBMIT_EXTEND", player), click -> {
            NetworkSender.send(new PrepareSchemPacket(SteamwarUser.get(player.getUniqueId()).getId(), node.getId(), type.toDB()));
            SchematicSystem.MESSAGE.send("UTIL_SUBMIT_EXTEND_DONE", player);
            player.closeInventory();
        });
        inv.setCallback(-999, click -> player.closeInventory());
        inv.open();
    }

    public static void quitPlayer(Player player) {
        CACHED_LISTS.remove(player);
        PUBLIC_TOGGLED.remove(player);
    }

    @AllArgsConstructor
    @With
    static class CachedSchematicList {
        private List<SchematicNode> nodes;
        private int page;
        private int maxPage;
        private SchematicNode parent;
        private SchematicListBehavior behavior;
    }
}
