/*
 *   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.fightsystem.commands;

import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.ai.AIManager;
import de.steamwar.fightsystem.fight.*;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightPlayer;
import de.steamwar.fightsystem.listener.PersonalKitCreator;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.utils.ColorConverter;
import de.steamwar.inventory.*;
import de.steamwar.message.Message;
import de.steamwar.sql.*;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

public class GUI {
    private GUI() {
    }

    private static final Message msg = FightSystem.getMessage();

    @SuppressWarnings("deprecation")
    private static void addTeamRequest(Player p, SWInventory inv, int pos, FightTeam team) {
        String name = team.getLeader() != null ? team.getLeader().getEntity().getName() : team.getName();
        inv.setItem(pos, ColorConverter.chat2dye(team.getColor()), msg.parse("JOIN_REQUEST_TEAM", p, team.getColor() + name), click -> {
            p.closeInventory();

            if (ArenaMode.ManualTeams.contains(Config.mode) && team.canbeLeader(p)) {
                team.addMember(p);
            } else {
                JoinRequest.forPlayer(p, team);
            }
        });
    }

    public static void joinRequest(Player p) {
        if (JoinRequest.get(p) != null) {
            msg.sendPrefixless("JOIN_REQUEST_ALREADY", p, ChatMessageType.ACTION_BAR);
            return;
        }

        SWInventory inv = new SWInventory(p, 9, msg.parse("JOIN_REQUEST_TITLE", p));
        FightTeam team = Fight.getPlayerTeam(p);
        if (team != Fight.getRedTeam()) {
            addTeamRequest(p, inv, team == null ? 0 : 4, Fight.getBlueTeam());
        }
        if (team != Fight.getBlueTeam()) {
            addTeamRequest(p, inv, team == null ? 8 : 4, Fight.getRedTeam());
        }
        inv.open();
    }

    public static void state(Player p) {
        SWInventory inv = new SWInventory(p, 9, msg.parse("STATE_TITLE", p));
        int i = 0;
        for (FightState state : FightState.values()) {
            if (state == FightState.SPECTATE) continue;

            inv.setItem(i++, Material.GLASS, msg.parse("STATE_" + state.name(), p), click -> FightState.setFightState(state));
        }
        for (FightTeam team : Fight.teams()) {
            inv.setItem(i++, Material.GLASS, msg.parse("STATE_SPECTATE_WIN", p, team.getColoredName()), click -> FightSystem.setSpectateState(team, "operator", "WIN_FIGHTLEADER"));
        }
        inv.setItem(i, Material.GLASS, msg.parse("STATE_SPECTATE_TIE", p), click -> FightSystem.setSpectateState(null, "operator", "WIN_FIGHTLEADER"));
        inv.setCallback(-999, (ClickType click) -> p.closeInventory());
        inv.open();
    }

    public static void addAI(Player p) {
        SWListInv<AIManager> inv = new SWListInv<>(
                p, msg.parse("ADD_AI_TITLE", p),
                AIManager.availableAIs().stream().map(manager -> new SWListInv.SWListEntry<>(new SWItem(manager.getIcon(), manager.name()), manager)).collect(Collectors.toList()),
                (click, manager) -> {
                    FightTeam team = Fight.getPlayerTeam(p);
                    if (FightState.PreLeaderSetup.contains(FightState.getFightState())) {
                        manager.join(Fight.getOpposite(team));
                    } else {
                        JoinRequest.forAI(manager, team);
                    }
                    p.closeInventory();
                }
        );
        inv.setCallback(-999, (ClickType click) -> p.closeInventory());
        inv.open();
    }

    public static void chooseJoinRequests(Player p) {
        List<SWListInv.SWListEntry<JoinRequest>> players = JoinRequest.openRequests(p, Fight.getPlayerTeam(p));
        SWListInv<JoinRequest> inv = new SWListInv<>(p, msg.parse("REQUESTS_TITLE", p), players, (ClickType click, JoinRequest request) -> {
            p.closeInventory();
            RequestsCommand.onJoinRequest(p, request, click.isLeftClick() ? JoinRequest::accept : JoinRequest::decline);
        });
        inv.setCallback(-999, (ClickType click) -> p.closeInventory());
        inv.open();
    }

    public static void managePlayers(Player p) {
        List<SWListInv.SWListEntry<UUID>> players = SWListInv.createPlayerList(p.getUniqueId());
        FightTeam team = Fight.getPlayerTeam(p);
        if (team == null) return;
        players.removeIf(listEntry -> !team.equals(Fight.getPlayerTeam(Bukkit.getPlayer(listEntry.getObject()))));
        players.forEach(listEntry -> listEntry.getItem().setLore(msg.parse("MANAGE_LORE1", p), msg.parse("MANAGE_LORE2", p)));
        SWListInv<UUID> inv = new SWListInv<>(p, msg.parse("MANAGE_TITLE", p), players, (ClickType click, UUID player) -> {
            if (click.isLeftClick()) {
                Commands.kick(p, SteamwarUser.get(player).getUserName());
            } else if (click.isRightClick()) {
                LivingEntity target = (LivingEntity) Bukkit.getEntity(player);
                if (target != null) {
                    team.setLeader(team.getFightPlayer(target), false);
                }
            }
            p.closeInventory();
        });
        inv.setCallback(-999, (ClickType click) -> p.closeInventory());
        inv.open();
    }

    public static void kitSelection(Player p, String query) {
        FightPlayer fightPlayer = Fight.getFightPlayer(p);
        if (fightPlayer == null) return;

        List<SWListInv.SWListEntry<Kit>> entries = new ArrayList<>();

        if (Config.GameModeConfig.Kits.PersonalKits) {
            List<PersonalKit> kits = PersonalKit.get(SteamwarUser.get(p.getUniqueId()).getId(), Config.GameModeConfig.Schematic.Type.toDB());
            kits.forEach(kit -> entries.add(new SWListInv.SWListEntry<>(new SWItem(Material.LEATHER_CHESTPLATE, "§e" + kit.getName(), new ArrayList<>(), kit.isInUse(), clickType -> {
            }), new Kit(kit))));
        } else {
            List<Kit> kitList = Kit.getAvailableKits(fightPlayer.isLeader());
            for (Kit k : kitList) {
                entries.add(new SWListInv.SWListEntry<>(new SWItem(Material.LEATHER_CHESTPLATE, k.getName(), null, k.leaderExclusive(), null), k));
            }
        }

        entries.removeIf(entry -> !entry.getObject().getName().toLowerCase().contains(query.toLowerCase()));

        SWListInv<Kit> inv = new SWListInv<>(p, msg.parse("KIT_SELECTION_TITLE", p), false, entries, (clickType, kit) -> kit.preview(p));
        inv.setCallback(-999, (ClickType click) -> p.closeInventory());
        if (entries.isEmpty()) {
            inv.setItem(22, new SWItem(Material.BARRIER, msg.parse("KIT_NO_KITS", p)));
        }
        if (Config.GameModeConfig.Kits.PersonalKits) {
            inv.setItem(48, Material.NETHER_STAR, msg.parse("KIT_CREATE", p), clickType -> {
                SWAnvilInv anvilInv = new SWAnvilInv(p, msg.parse("KITNAME_TITLE", p));
                anvilInv.setItem(Material.LEATHER_CHESTPLATE);
                anvilInv.setCallback(s -> {
                    SteamwarUser user = SteamwarUser.get(p.getUniqueId());
                    if (PersonalKit.get(user.getId(), Config.GameModeConfig.Schematic.Type.toDB(), s) != null) {
                        msg.sendPrefixless("KITNAME_IN_USE", p, ChatMessageType.ACTION_BAR);
                        p.closeInventory();
                        return;
                    }
                    Kit prototype = Kit.getAvailableKits(Fight.getFightPlayer(p).isLeader()).get(0);
                    PersonalKit kit = PersonalKit.create(user.getId(), Config.GameModeConfig.Schematic.Type.toDB(), s, prototype.getInventory(), prototype.getArmor());
                    Bukkit.getScheduler().runTask(FightSystem.getPlugin(), () -> PersonalKitCreator.openKitCreator(p, kit));
                });
                anvilInv.open();
            });
        }
        inv.setItem(50, Material.NAME_TAG, msg.parse("KIT_SEARCH", p), clickType -> {
            SWAnvilInv anvilInv = new SWAnvilInv(p, msg.parse("KITSEARCH_TITLE", p));
            anvilInv.setItem(Material.PAPER);
            anvilInv.setCallback(s -> kitSelection(p, s));
            anvilInv.open();
        });
        inv.open();
    }

    public static void preSchemDialog(Player p) {
        if (!Config.test() && FightState.getFightState() != FightState.PRE_SCHEM_SETUP) {
            msg.sendPrefixless("SCHEM_NO_ENEMY", p, ChatMessageType.ACTION_BAR);
            return;
        }

        int invSize = (Config.GameModeConfig.Schematic.SubTypes.size() + 1) * 9;
        SWInventory inv = new SWInventory(p, invSize, msg.parse("SCHEM_TITLE", p, Config.GameModeConfig.GameName));
        setupSchemTypeRow(p, inv, Config.GameModeConfig.Schematic.Type, 0);
        for (int i = 0; i < Config.GameModeConfig.Schematic.SubTypes.size(); i++) {
            setupSchemTypeRow(p, inv, Config.GameModeConfig.Schematic.SubTypes.get(i), i + 1);
        }
        if (!Config.test() && SteamwarUser.get(p.getUniqueId()).hasPerm(UserPerm.TEAM)) {
            SchematicNode node = SchematicNode.getSchematicNode(-1, Config.GameModeConfig.GameName, (Integer) null);
            if (node != null) {
                inv.setItem(2, new SWItem(SWItem.getMaterial(node.getItem()), msg.parse("SCHEM_DIRT", p), click -> {
                    schemSelect(p, node);
                }));
            }
        }
        inv.setCallback(-999, (ClickType click) -> p.closeInventory());
        inv.open();
    }

    private static void setupSchemTypeRow(Player p, SWInventory inv, SchematicType type, int row) {
        inv.setItem(row * 9 + 8, Material.REDSTONE, msg.parse("SCHEM_PUBLIC", p, type.name()), (ClickType click) -> {
            p.closeInventory();
            schemDialog(p, type, true, false);
        });

        if (Fight.publicOnly()) {
            inv.setItem(row * 9, Material.GRAY_DYE, (byte) 8, msg.parse("SCHEM_PRIVATE_FORBIDDEN", p, type.name()), (ClickType click) -> {
            });
            return;
        }

        if (type.checkType() != null && type.checkType() != type && ArenaMode.AntiEvent.contains(Config.mode) && !SchematicNode.getAllAccessibleSchematicsOfType(SteamwarUser.get(p.getUniqueId()).getId(), type.checkType().toDB()).isEmpty()) {
            inv.setItem(row * 9 + 4, Material.ANVIL, msg.parse("SCHEM_UNCHECKED", p, type.name()), (ClickType click) -> {
                p.closeInventory();
                schemDialog(p, type, false, true);
            });
        }

        if (SchematicNode.getAllAccessibleSchematicsOfType(SteamwarUser.get(p.getUniqueId()).getId(), type.toDB()).isEmpty() && !Config.test()) {
            inv.setItem(row * 9, Material.GRAY_DYE, (byte) 8, msg.parse("SCHEM_NO_PRIVATE", p, type.name()), (ClickType click) -> {
            });
            return;
        }

        inv.setItem(row * 9, Material.CAULDRON, msg.parse("SCHEM_PRIVATE", p, type.name()), (ClickType click) -> {
            p.closeInventory();
            schemDialog(p, type, false, false);
        });
    }

    private static void schemDialog(Player p, SchematicType type, boolean publicSchems, boolean unchecked) {
        SchematicSelector selector = new SchematicSelector(p, Config.test() ? SchematicSelector.selectSchematic() : SchematicSelector.selectSchematicType(unchecked ? type.checkType() : type), node -> {
            schemSelect(p, node);
        });
        selector.setPublicMode(publicSchems ? SchematicSelector.PublicMode.PUBLIC_ONLY : SchematicSelector.PublicMode.PRIVATE_ONLY);
        selector.open();
    }

    private static void schemSelect(Player p, SchematicNode node) {
        FightTeam fightTeam = Fight.getPlayerTeam(p);
        if (fightTeam == null) return;
        if (Config.test() || FightState.getFightState() != FightState.POST_SCHEM_SETUP) {
            fightTeam.pasteSchem(node);
        }
        p.closeInventory();
    }
}
