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

import de.steamwar.Reflection;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.commands.Commands;
import de.steamwar.fightsystem.commands.GUI;
import de.steamwar.fightsystem.listener.PersonalKitCreator;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.fightsystem.utils.ReflectionWrapper;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.sql.PersonalKit;
import de.steamwar.sql.SteamwarUser;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffect;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;

public class Kit {
    private static final File kits = new File(FightSystem.getPlugin().getDataFolder(), Config.KitFile);
    private static final ArrayList<Kit> loadedKits = new ArrayList<>();

    protected static final Map<Player, Kit> activeKits = new HashMap<>();

    public static Kit getActiveKit(Player player) {
        return activeKits.get(player);
    }

    static {
        if(!kits.exists()) {
            Bukkit.getLogger().log(Level.SEVERE, "Kitconfig fehlend!" + kits.getAbsolutePath());
        }

        FileConfiguration kitData = YamlConfiguration.loadConfiguration(kits);
        ConfigurationSection kitSection = kitData.getConfigurationSection("Kits");

        for(String key : Objects.requireNonNull(kitSection).getKeys(false)) {
            loadedKits.add(new Kit(Objects.requireNonNull(kitSection.getConfigurationSection(key))));
        }
    }

    @Getter
    private final String name;
    @Getter
    private final ItemStack[] inventory;
    @Getter
    private final ItemStack[] armor;
    @Getter
    private final Collection<PotionEffect> effects;
    /* In which stage is entern allowed? */
    @Getter
    private final int enterStage;
    /* Is this kit allowed to set/handle tnt? */
    @Getter
    private final boolean tnt;
    private final boolean leaderAllowed;
    private final boolean memberAllowed;

    protected Kit(String name, ItemStack[] inventory, ItemStack[] armor, Collection<PotionEffect> effects) {
        this.name = name;
        this.inventory = inventory;
        this.armor = armor;
        this.effects = effects;
        this.leaderAllowed = true;
        this.memberAllowed = true;
        this.enterStage = 0;
        this.tnt = true;
    }

    protected Kit(Kit kit) {
        this(kit.name, kit.inventory.clone(), kit.armor, kit.effects);
    }

    public Kit(String name, Player player) {
        this(name, player.getInventory().getContents(), player.getInventory().getArmorContents(), player.getActivePotionEffects());
    }

    public Kit(PersonalKit kit){
        this(kit.getName(), kit.getInventory(), kit.getArmor(), Collections.emptyList());
    }

    public Kit(ConfigurationSection kit){
        name = kit.getName();
        inventory = Objects.requireNonNull(kit.getList("Items")).toArray(new ItemStack[0]);
        if(kit.isList("Armor"))
            armor = Objects.requireNonNull(kit.getList("Armor")).toArray(new ItemStack[0]);
        else
            armor = null;
        leaderAllowed = kit.getBoolean("LeaderAllowed");
        memberAllowed = kit.getBoolean("MemberAllowed");
        if(kit.isList("Effects"))
            effects = (List<PotionEffect>) kit.getList("Effects");
        else
            effects = null;
        enterStage = kit.getInt("EnterStage", 0);
        tnt = kit.getBoolean("TNT", true);
    }

    protected void setItem(int id, ItemStack stack) {
        inventory[id] = stack;
    }

    public static Kit getKitByName(String kitName) {
        for(Kit kit : loadedKits) {
            if(kit.getName().equalsIgnoreCase(kitName))
                return kit;
        }
        return null;
    }

    public static List<Kit> getAvailableKits(boolean leader){
        List<Kit> kits = new ArrayList<>();
        for (Kit k : loadedKits) {
            if (k.canUseKit(leader)){
                kits.add(k);
            }
        }
        return kits;
    }

    public boolean canUseKit(boolean leader){
        if (leader) {
            return leaderAllowed;
        } else {
            return memberAllowed;
        }
    }

    public boolean leaderExclusive() {
        return !memberAllowed;
    }

    public boolean contains(ItemStack stack) {
        for(ItemStack i : inventory) {
            if(similar(i, stack))
                return true;
        }
        for(ItemStack i : armor) {
            if(similar(i, stack))
                return true;
        }
        return false;
    }

    public void toPersonalKit(PersonalKit kit) {
        kit.setContainer(inventory, armor);
    }

    public void removeBadItems(){
        Kit normal = Kit.getKitByName(Config.MemberDefault);
        assert normal != null;

        for(int i = 0; i < inventory.length; i++){
            if(isBadItem(inventory[i]))
                inventory[i] = null;
        }

    }

    public static boolean isBadItem(ItemStack stack){
        if(stack == null)
            return false;

        //Check for forbidden item
        if(Config.ForbiddenItems.contains(stack.getType()))
            return true;

        //Check for attribute modifiers
        if(FlatteningWrapper.impl.hasAttributeModifier(stack)){
            return true;
        }

        if(stack.hasItemMeta()){
            ItemMeta meta = stack.getItemMeta();
            if(FlatteningWrapper.impl.containsBlockMeta(meta))
                return true; //Blocks always upwards slabs etc.

            if(hasItems(stack))
                return true; //Blocks prefilled inventories
        }

        Kit normal = Kit.getKitByName(Config.MemberDefault);
        assert normal != null;
        return !normal.isEnchantmentInKit(stack) && !stack.getEnchantments().isEmpty();
    }
    public static boolean hasItems(ItemStack stack) {
        return ReflectionWrapper.impl.hasItems(stack);
    }

    private boolean isEnchantmentInKit(ItemStack stack){
        for(ItemStack is : inventory){
            if(similar(stack, is))
                return true;
        }
        if(armor != null){
            for(ItemStack is : armor){
                if(similar(stack, is))
                    return true;
            }
        }
        return false;
    }

    private boolean similar(ItemStack stack, ItemStack stack2){
        if(stack == null || stack2 == null)
            return false;
        if(stack.getType() != stack2.getType())
            return false;
        if(stack.hasItemMeta() != stack2.hasItemMeta())
            return false;
        if(stack.getItemMeta() == null || stack2.getItemMeta() == null)
            return true;

        //Enchantment Map comparison used for default similarity check does not work
        Map<Enchantment, Integer> en = stack.getItemMeta().getEnchants();
        Map<Enchantment, Integer> en2 = new HashMap<>(stack.getItemMeta().getEnchants());

        for(Map.Entry<Enchantment, Integer> e : en.entrySet()){
            if(!en2.remove(e.getKey(), e.getValue()))
                return false;
        }
        return en2.isEmpty();
    }

    public void loadToPlayer(Player player) {
        activeKits.put(player, this);

        player.getInventory().clear();
        player.getInventory().setContents(inventory);
        if(armor != null)
            player.getInventory().setArmorContents(armor);
        player.updateInventory();
        if(effects != null)
            player.addPotionEffects(effects);
    }

    /**
     * Opens a kit preview with the options to go back to kit selection or to select the kit.
     */
    public void preview(Player player){
        SWInventory inv = new SWInventory(player, 54, name);

        //36 = Inventargröße
        for(int i = 0; i < 36; i++){
            if(inventory[i] == null)
                continue;
            SWItem item = new SWItem();
            item.setItemStack(inventory[i]);
            inv.setItem(i, item);
        }

        if(armor != null){
            for(int i = 0; i < 4; i++){
                if(armor[i] == null)
                    continue;
                SWItem item = new SWItem();
                item.setItemStack(armor[i]);
                inv.setItem(36 + i, item);
            }
        }

        if(effects != null){
            Iterator<PotionEffect> it = effects.iterator();
            int pos = 44;
            while(it.hasNext()){
                PotionEffect effect = it.next();
                SWItem item = new SWItem(SWItem.getMaterial("POTION"), effect.getType().getName());
                inv.setItem(pos, item);
                pos--;
            }
        }

        inv.setCallback(-999, click -> player.closeInventory());
        if(Config.PersonalKits){
            inv.setItem(49, SWItem.getMaterial("WOOD_AXE"), FightSystem.getMessage().parse("KIT_PREVIEW_EDIT", player), clickType -> PersonalKitCreator.openKitCreator(player, PersonalKit.get(SteamwarUser.get(player.getUniqueId()).getId(), Config.SchematicType.toDB(), name)));
            inv.setItem(53, Material.BARRIER, FightSystem.getMessage().parse("KIT_PREVIEW_DELETE", player), clickType -> {
                player.closeInventory();
                SWInventory conf = new SWInventory(player, 9, FightSystem.getMessage().parse("KIT_DELETION_CONFIRMATION", player));
                conf.setItem(8, SWItem.getDye(1), FightSystem.getMessage().parse("KIT_DELETION_ABORT", player), click -> player.closeInventory());
                conf.setItem(0, SWItem.getDye(10), FightSystem.getMessage().parse("KIT_DELETION_DELETE", player), click -> {
                    player.closeInventory();
                    SteamwarUser user = SteamwarUser.get(player.getUniqueId());
                    PersonalKit kit = PersonalKit.get(user.getId(), Config.SchematicType.toDB(), name);
                    if(kit.isInUse()) {
                        List<PersonalKit> kits = PersonalKit.get(user.getId(), Config.SchematicType.toDB());
                        if(!kits.isEmpty()){
                            PersonalKit kit1 = kits.get(0);
                            kit1.setInUse();
                            FightPlayer fightPlayer = Fight.getFightPlayer(player);
                            assert fightPlayer != null;
                            fightPlayer.setKit(new Kit(kit1));
                        }
                    }
                    kit.delete();
                });
                conf.open();
            });
        }
        inv.setItem(45, SWItem.getDye(10), (byte)10, FightSystem.getMessage().parse("KIT_PREVIEW_CHOOSE", player), click -> {
            Commands.kit(player, name);
            player.closeInventory();
        });
        inv.setItem(53, SWItem.getDye(1), (byte)1, FightSystem.getMessage().parse("KIT_PREVIEW_BACK", player), click -> GUI.kitSelection(player, ""));
        inv.open();
    }

    public static void createKit(String kitName, Player player){
        loadedKits.add(new Kit(kitName, player));
        YamlConfiguration yamlConfiguration = new YamlConfiguration();
        for(Kit k : loadedKits){
            ConfigurationSection section = yamlConfiguration.createSection("Kits." + k.getName());
            section.set("Items", k.inventory);
            if(k.armor != null)
                section.set("Armor", k.armor);
            section.set("LeaderAllowed", k.leaderAllowed);
            section.set("MemberAllowed", k.memberAllowed);
            section.set("Effects", k.effects);
            section.set("EnterStage", k.enterStage);
            section.set("TNT", k.tnt);
        }

        try {
            yamlConfiguration.save(kits);
        }catch(IOException e){
            throw new SecurityException("Failed to save kits.data", e);
        }
    }
}
