/*
   This file is a part of the SteamWar software.

   Copyright (C) 2023  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.core.Core;
import de.steamwar.inventory.*;
import de.steamwar.schematicsystem.CheckSchemType;
import de.steamwar.schematicsystem.SafeSchematicNode;
import de.steamwar.schematicsystem.SchematicSystem;
import de.steamwar.schematicsystem.autocheck.AutoChecker;
import de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommandUtils;
import de.steamwar.sql.*;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommandUtils.*;

public class GUI {
    private GUI() {}

    public static void list(Player player) {
        SchematicSelector selector = new SchematicSelector(player, SchematicSelector.selectSchematic(), new SchematicSelectorInjectable() {
            @Override
            public String createTitle(Player player) {
                return SchematicSystem.MESSAGE.parse("GUI_TITLE", player);
            }

            @Override
            public void onListRender(SchematicSelector selector, SWListInv<SchematicNode> inv, SchematicNode parent) {
                inv.setCallback(-999, (ClickType click) -> {
                    if(parent == null) {
                        player.closeInventory();
                    } else {
                        selector.reOpenDirUp();
                    }
                });
                if(parent == null) {
                    inv.setItem(49, Material.AIR, "", clickType -> {});
                } else {
                    inv.setItem(49, Material.ANVIL, SchematicSystem.MESSAGE.parse("GUI_FOLDER_PROPERTIES", player), clickType -> {
                        info(selector.getPlayer(), parent, selector);
                    });
                }
            }

            @Override
            public AnvilInvCloseAction onAnvilInvCloseAction(SchematicSelector selector) {
                return AnvilInvCloseAction.REOPEN;
            }
        }, node -> {});
        selector.setCallback(node -> info(player, node, selector));
        selector.setSingleDirOpen(false);
        selector.open();
    }

    private static void setCloseLeftCallback(SWAnvilInv inv, Runnable action) {
        AtomicBoolean left = new AtomicBoolean(false);
        inv.addCloseCallback(() -> {
            if(!left.get()) {
                Bukkit.getScheduler().runTaskLater(SchematicSystem.getInstance(), action, 1);
            }
        });
        inv.addLeftCallback(() -> {
            left.set(true);
            action.run();
        });
    }

    private static void info(Player player, SchematicNode node, SchematicSelector back) {
        SteamwarUser user = getUser(player);
        SWInventory inv = new SWInventory(player, 9 * 2, node.generateBreadcrumbs());
        if(!node.isDir()) {
            inv.setItem(0, SWItem.getMaterial("WOOD_AXE"), SchematicSystem.MESSAGE.parse("GUI_INFO_LOAD", player), click -> {
                player.closeInventory();
                SchematicCommandUtils.loadSchem(player, node);
            });
        }

        inv.setItem(9, SWItem.getMaterial("LEASH"), SchematicSystem.MESSAGE.parse("GUI_INFO_BACK", player), clickType -> {
            back.reOpen();
        });

        if(node.getOwner() == user.getId()){
            if(!node.isDir() && node.getSchemtype().writeable()){
                CheckedSchematic.getLastDeclinedOfNode(node.getId()).stream().findFirst().ifPresent(checkedSchematic ->
                        inv.setItem(1, SWItem.getDye(10), (byte) 10, SchematicSystem.MESSAGE.parse("GUI_INFO_STATUS", player, node.getSchemtype().name()), Collections.singletonList(SchematicSystem.MESSAGE.parse("GUI_INFO_STATUS_LORE", player, checkedSchematic.getDeclineReason().replaceAll("&", "§"))), false, click -> {}));
            }
            Material mat = SWItem.getMaterial(node.getItem());

            inv.setItemEvent(node.isDir()?7:5, mat, SchematicSystem.MESSAGE.parse("GUI_INFO_MAT", player, mat.name()), Arrays.asList(SchematicSystem.MESSAGE.parse("CHANGE", player), SchematicSystem.MESSAGE.parse("CLICK_DRAG_ITEM", player)), false, event -> {
                if(event.getCursor() != null && event.getCursor().getType() != Material.AIR) {
                    node.setItem(event.getCursor().getType().name());
                    info(player, node, back);
                } else {
                    changeItem(player, node, back);
                }
            });
            if(!node.isDir()) {
                inv.setItem(6, SWItem.getMaterial(node.getSchemtype().getMaterial()), SchematicSystem.MESSAGE.parse("GUI_INFO_TYPE", player, node.getSchemtype().name()), Arrays.asList(SchematicSystem.MESSAGE.parse("CHANGE", player), SchematicSystem.MESSAGE.parse("CLICK", player)), node.getSchemtype().fightType(), click -> {
                    changeType(player, node);
                });
                inv.setItem(7, SWItem.getMaterial("MAGENTA_GLAZED_TERRACOTTA"), SchematicSystem.MESSAGE.parse("GUI_INFO_DOWNLOAD", player), click -> {
                    player.closeInventory();
                    SchematicCommandUtils.download(player, node);
                });
                if(node.getSchemtype().fightType()) {
                    inv.setItem(14, SWItem.getMaterial(node.replaceColor() ? "PINK_WOOL" : "LIGHT_GRAY_WOOL"), SchematicSystem.MESSAGE.parse("GUI_INFO_COLOR", player), Arrays.asList(SchematicSystem.MESSAGE.parse("CURRENT", player, SchematicSystem.MESSAGE.parse(node.replaceColor()?"ON":"OFF", player)), SchematicSystem.MESSAGE.parse("CHANGE", player), SchematicSystem.MESSAGE.parse("CLICK", player)), false, clickType -> {
                        node.setReplaceColor(!node.replaceColor());
                        info(player, node, back);
                    });
                    inv.setItem(13, SWItem.getMaterial(node.allowReplay() ? "EYE_OF_ENDER" : "ENDER_PEARL"), SchematicSystem.MESSAGE.parse("GUI_INFO_REPLAY", player), Arrays.asList(SchematicSystem.MESSAGE.parse("CURRENT", player, SchematicSystem.MESSAGE.parse(node.allowReplay()?"ON":"OFF", player)), SchematicSystem.MESSAGE.parse("GUI_INFO_REPLAY_OFF", player), SchematicSystem.MESSAGE.parse("CLICK", player)), false, clickType -> {
                        if(node.allowReplay()) {
                            SWInventory confInv = new SWInventory(player, 9, SchematicSystem.MESSAGE.parse("GUI_INFO_REPLAY_TITLE", player));
                            confInv.setItem(0, SWItem.getDye(10), (byte) 10, SchematicSystem.MESSAGE.parse("CONFIRM", player), type -> {
                                node.setAllowReplay(false);
                                info(player, node, back);
                            });
                            confInv.setItem(8, SWItem.getDye(1), (byte) 1, SchematicSystem.MESSAGE.parse("CANCEL", player), type -> {
                                info(player, node, back);
                            });
                            confInv.open();
                        }
                    });
                }
            }
            SWItem skull = SWItem.getPlayerSkull("MHF_STEVE");
            skull.setCallback(click -> {
                player.closeInventory();
                delmembers(player, node);
            });
            skull.setName(SchematicSystem.MESSAGE.parse("GUI_INFO_MEMBER", player));
            inv.setItem(8, skull);
            inv.setItem(16, Material.NAME_TAG, SchematicSystem.MESSAGE.parse("GUI_INFO_RENAME", player), clickType -> {
                SWAnvilInv anvilInv = new SWAnvilInv(player, SchematicSystem.MESSAGE.parse("GUI_INFO_RENAME_TITLE", player, node.getName()), node.getName());
                anvilInv.setItem(mat, Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_CLICK_BACK", player)), false);
                anvilInv.setCallback(s -> {
                    SafeSchematicNode.Result result = SafeSchematicNode.setName(user, node, s);
                    if(result.isSuccessful()) {
                        info(player, SchematicNode.byIdAndUser(user, node.getId()), back);
                    } else {
                        result.sendError(player);
                    }
                });
                setCloseLeftCallback(anvilInv, () -> info(player, node, back));
                anvilInv.open();
            });
            if(node.getOwner() != 0) {
                inv.setItem(17, SWItem.getDye(1), (byte) 1, SchematicSystem.MESSAGE.parse("GUI_INFO_DELETE", player), click -> {
                    delete(player, node, back);
                });
            }
        }else{
            if(!node.isDir()) {
                inv.setItem(4, SWItem.getMaterial("CAULDRON_ITEM"), SchematicSystem.MESSAGE.parse("GUI_INFO_TYPE", player, node.getSchemtype().name()), Collections.emptyList(), node.getSchemtype().fightType(), click -> {});
            }

            SteamwarUser owneruser = SteamwarUser.get(node.getOwner());
            SWItem skull = SWItem.getPlayerSkull(owneruser.getUserName());
            skull.setName(SchematicSystem.MESSAGE.parse("GUI_INFO_MEMBER_FROM", player, owneruser.getUserName()));
            inv.setItem(8, skull.getItemStack(), clickType -> {});

            if(NodeMember.getNodeMember(node.getId(), user.getId()) != null) {
                inv.setItem(17, SWItem.getDye(1), (byte) 1, SchematicSystem.MESSAGE.parse("GUI_INFO_MEMBER_REMOVE", player), click -> {
                    delete(player, node, back);
                });
            }
        }

        if(node.getOwner() == user.getId() || NodeMember.getNodeMember(node.getId(), user.getId()) != null) {
            inv.setItem(15, Material.ARROW, SchematicSystem.MESSAGE.parse("GUI_INFO_MOVE", player), clickType -> {
                SchematicSelector selector = new SchematicSelector(player, SchematicSelector.selectDirectory(), npar -> {
                    if(npar != null && SchematicNode.parentsOfNode(user, npar.getId()).stream().anyMatch(n -> n.getId() == node.getId())) {
                        SchematicSystem.MESSAGE.send("COMMAND_MOVE_RECURSIVE", player);
                        return;
                    }
                    if(node.getOwner() == user.getId()) {
                        SafeSchematicNode.Result result = SafeSchematicNode.setParent(user, node, npar);
                        if(result.isSuccessful()) {
                            info(player, SchematicNode.getSchematicNode(node.getId()), back);
                        } else {
                            result.sendError(player);
                        }
                    } else {
                        NodeMember.getNodeMember(node.getId(), user.getId()).setParentId(Optional.ofNullable(npar).map(SchematicNode::getId).orElse(null));
                    }
                });
                selector.open();
            });
        }

        inv.setCallback(-999, click -> back.reOpen());
        inv.open();
    }

    static void changeType(Player p, SchematicNode schem){
        Clipboard clipboard = null;
        try {
            clipboard = new SchematicData(schem).load();
        } catch (IOException ignored) { }

        Clipboard finalClipboard = clipboard;
        List<SchematicType> types = SchematicType.values().parallelStream()
                .filter(SchematicType::isAssignable)
                .filter(type -> finalClipboard == null || CheckSchemType.get(type) == null || AutoChecker.sizeCheck(finalClipboard, CheckSchemType.get(type)).fastOk())
                .collect(Collectors.toList());

        List<SWListInv.SWListEntry<SchematicType>> items = types.stream()
                .map(type -> new SWListInv.SWListEntry<>(new SWItem(SWItem.getMaterial(type.getMaterial()), type.name(), Collections.emptyList(), type.fightType(), null), type))
                .collect(Collectors.toList());

        items.addAll(SchematicType.values().stream()
                .filter(SchematicType::isAssignable)
                .filter(type -> !types.contains(type))
                .map(type -> new SWListInv.SWListEntry<>(new SWItem(SWItem.getMaterial(type.getMaterial()), SchematicSystem.MESSAGE.parse("GUI_CHANGE_TYPE_NOT_POSSIBLE_COLOR", p, type.name()), Collections.singletonList(SchematicSystem.MESSAGE.parse("GUI_CHANGE_TYPE_NOT_POSSIBLE", p)), false, null), (SchematicType) null))
                .collect(Collectors.toList()));

        SWListInv<SchematicType> inv = new SWListInv<>(p, SchematicSystem.MESSAGE.parse("GUI_CHANGE_TYPE", p), items, (clickType, schematicType) -> {
            if(schematicType == null) return;
            p.closeInventory();
            SchematicCommandUtils.changeType(p, schem, schematicType, null);
        });
        inv.setCallback(-999, (ClickType click) -> p.closeInventory());
        inv.open();
    }

    private static void deleteOwn(Player p, SchematicNode schem, SchematicSelector back){
        SteamwarUser user = getUser(p);
        SWInventory inv = new SWInventory(p, 9, SchematicSystem.MESSAGE.parse("GUI_DELETE_OWN_TITLE", p, schem.generateBreadcrumbs()));
        inv.setItem(0, SWItem.getDye(1), (byte) 1, SchematicSystem.MESSAGE.parse("CONFIRM", p), click -> {
            p.performCommand("schematic delete " + schem.generateBreadcrumbs());

            if(back != null) {
                if(schem.isDir()) {
                    back.reOpenDirUp();
                } else {
                    back.reOpen();
                }
            }
        });
        inv.setItem(8, SWItem.getDye(14), (byte) 14, SchematicSystem.MESSAGE.parse("CANCEL", p), click -> p.closeInventory());
        inv.setCallback(-999, click -> p.closeInventory());
        inv.open();
    }

    private static void deleteMembership(Player p, SchematicNode schem, SchematicSelector back){
        SteamwarUser user = getUser(p);
        SWInventory inv = new SWInventory(p, 9, SchematicSystem.MESSAGE.parse("GUI_DELETE_MEMBER_TITLE", p, schem.generateBreadcrumbs()));
        inv.setItem(0, SWItem.getDye(1), (byte) 1, SchematicSystem.MESSAGE.parse("CONFIRM", p), click -> {
            NodeMember member = NodeMember.getNodeMember(schem.getId(), user.getId());
            if(member != null)
                member.delete();
            SchematicSystem.MESSAGE.send("GUI_DELETE_MEMBER_DONE", p, schem.generateBreadcrumbs());
            if(back != null) {
                if(schem.isDir()) {
                    back.reOpenDirUp();
                } else {
                    back.reOpen();
                }
            }
        });
        inv.setItem(8, SWItem.getDye(14), (byte) 14, SchematicSystem.MESSAGE.parse("CANCEL", p), click -> p.closeInventory());
        inv.setCallback(-999, click -> p.closeInventory());
        inv.open();
    }

    static void delete(Player p, SchematicNode schem, SchematicSelector back){
        if(SteamwarUser.get(p.getUniqueId()).getId() == schem.getOwner())
            deleteOwn(p, schem, back);
        else
            deleteMembership(p, schem, back);
    }

    static void delmembers(Player p, SchematicNode schem){
        List<SWListInv.SWListEntry<NodeMember>> members = new LinkedList<>();
        for(NodeMember member : schem.getMembers()){
            SteamwarUser user = SteamwarUser.get(member.getMember());
            members.add(new SWListInv.SWListEntry<>(SWItem.getPlayerSkull(user.getUserName()), member));
        }

        SWListInv<NodeMember> inv = new SWListInv<>(p, SchematicSystem.MESSAGE.parse("GUI_DELETE_MEMBERS_TITLE", p), members, (clickType, member) -> {
            member.delete();
            p.closeInventory();
            delmembers(p, schem);
        });
        inv.setCallback(-999, (ClickType click) -> p.closeInventory());
        inv.open();
    }

    private static void changeItem(Player p, SchematicNode schem, SchematicSelector back){
        UtilGui.openMaterialSelector(p, SchematicSystem.MESSAGE.parse("GUI_CHANGE_ITEM", p), material -> {
            schem.setItem(material.name());
            p.closeInventory();
            info(p, schem, back);
        });
    }
}
