package de.viper.survivalplus.Manager;

import de.viper.survivalplus.SurvivalPlus;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.*;
import org.bukkit.block.Chest;
import org.bukkit.command.*;
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.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.scheduler.BukkitRunnable;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

public class LootChestManager implements Listener, CommandExecutor {

    private final SurvivalPlus plugin;
    private final List<Location> activeChests = new ArrayList<>();
    private final Map<Location, Long> chestSpawnTimes = new HashMap<>();
    private final List<LootItem> lootItems = new ArrayList<>();
    private final Map<UUID, Integer> playerLootCount = new HashMap<>();

    private final long despawnMillis;
    private final int maxActiveChests;
    private final int maxLootPerPlayer;
    private final long lootLimitResetMillis;

    // Persistence
    private final File lootChestFile;
    private FileConfiguration lootChestConfig;

    public LootChestManager(SurvivalPlus plugin) {
        this.plugin = plugin;
        this.lootChestFile = new File(plugin.getDataFolder(), "lootchests.yml");
        this.lootChestConfig = YamlConfiguration.loadConfiguration(lootChestFile);
        
        loadLootFromConfig();
        loadActiveChests(); // Kisten beim Start laden

        FileConfiguration cfg = plugin.getConfig();
        this.despawnMillis = cfg.getLong("lootchest.despawn-minutes", 10) * 60 * 1000;
        this.maxActiveChests = cfg.getInt("lootchest.max-active-chests", 10);
        this.maxLootPerPlayer = cfg.getInt("lootchest.max-loot-per-player", 3);
        this.lootLimitResetMillis = cfg.getLong("lootchest.loot-limit-reset-minutes", 60) * 60 * 1000;

        startChestSpawnTask();
        startChestDespawnTask();
        startLootLimitResetTask();
    }

    /**
     * Lädt gespeicherte Kisten aus der Datei und prüft, ob sie abgelaufen sind
     */
    private void loadActiveChests() {
        if (!lootChestFile.exists()) return;
        
        long now = System.currentTimeMillis();
        List<Location> toRemoveFile = new ArrayList<>();

        for (String key : lootChestConfig.getKeys(false)) {
            try {
                long spawnTime = lootChestConfig.getLong(key);

                // Prüfen ob die Zeit abgelaufen ist (inklusive Offline-Zeit)
                if (now - spawnTime >= despawnMillis) {
                    String[] parts = key.split("_");
                    if (parts.length == 4) {
                        World w = Bukkit.getWorld(parts[0]);
                        if (w != null) {
                            int x = Integer.parseInt(parts[1]);
                            int y = Integer.parseInt(parts[2]);
                            int z = Integer.parseInt(parts[3]);
                            Location loc = new Location(w, x, y, z);
                            
                            // Kiste physikalisch löschen falls noch vorhanden
                            if (loc.getBlock().getType() == Material.CHEST) {
                                loc.getBlock().setType(Material.AIR);
                            }
                            toRemoveFile.add(loc);
                        }
                    }
                } else {
                    // Kiste ist noch gültig -> in den Speicher laden
                    String[] parts = key.split("_");
                    if (parts.length == 4) {
                        World w = Bukkit.getWorld(parts[0]);
                        if (w != null) {
                            int x = Integer.parseInt(parts[1]);
                            int y = Integer.parseInt(parts[2]);
                            int z = Integer.parseInt(parts[3]);
                            Location loc = new Location(w, x, y, z);
                            
                            // Integritätsprüfung: Ist da auch wirklich eine Kiste?
                            if (loc.getBlock().getType() == Material.CHEST) {
                                activeChests.add(loc);
                                chestSpawnTimes.put(loc, spawnTime);
                            } else {
                                // Datei sagt ja, Welt sagt nein -> Cleanup in Datei
                                toRemoveFile.add(loc);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                plugin.getLogger().warning("Fehler beim Laden einer Lootkiste: " + key);
            }
        }
        
        // Alte Einträge aus der Datei entfernen
        for (Location loc : toRemoveFile) {
            String key = loc.getWorld().getName() + "_" + loc.getBlockX() + "_" + loc.getBlockY() + "_" + loc.getBlockZ();
            lootChestConfig.set(key, null);
        }
        saveActiveChests();
    }

    /**
     * Speichert die aktuellen aktiven Kisten in die Datei
     */
    private void saveActiveChests() {
        lootChestConfig = new YamlConfiguration();
        for (Map.Entry<Location, Long> entry : chestSpawnTimes.entrySet()) {
            Location loc = entry.getKey();
            String key = loc.getWorld().getName() + "_" + loc.getBlockX() + "_" + loc.getBlockY() + "_" + loc.getBlockZ();
            lootChestConfig.set(key, entry.getValue());
        }
        try {
            lootChestConfig.save(lootChestFile);
        } catch (IOException e) {
            plugin.getLogger().severe("Konnte lootchests.yml nicht speichern!");
            e.printStackTrace();
        }
    }

    /**
     * Entfernt eine Kiste komplett aus System und Datei (z.B. beim Looten)
     */
    private void removeChestData(Location loc) {
        activeChests.remove(loc);
        chestSpawnTimes.remove(loc);
        
        String key = loc.getWorld().getName() + "_" + loc.getBlockX() + "_" + loc.getBlockY() + "_" + loc.getBlockZ();
        lootChestConfig.set(key, null);
        saveActiveChests();
    }

    private void loadLootFromConfig() {
        FileConfiguration cfg = plugin.getConfig();
        lootItems.clear();
        if (cfg.isConfigurationSection("lootchest.items")) {
            for (String key : cfg.getConfigurationSection("lootchest.items").getKeys(false)) {
                lootItems.add(new LootItem(cfg, "lootchest.items." + key));
            }
        }
    }

    private void startLootLimitResetTask() {
        new BukkitRunnable() {
            @Override
            public void run() {
                playerLootCount.clear();
            }
        }.runTaskTimer(plugin, lootLimitResetMillis / 50, lootLimitResetMillis / 50);
    }

    private void startChestSpawnTask() {
        long intervalTicks = plugin.getConfig().getLong("lootchest.interval-minutes", 30) * 60 * 20;
        int spawnCount = plugin.getConfig().getInt("lootchest.spawn-count", 3);

        new BukkitRunnable() {
            @Override
            public void run() {
                // Falls deaktiviert, nichts tun
                // Hinweis: Die Logik hier war "!enabled ... return; clearAll". 
                // Das bedeutet: Wenn enabled=true -> !enabled=false -> kein return -> clearAll.
                if (!plugin.getConfig().getBoolean("lootchest.enabled", true)) return;
                
                clearAllActiveChests();
                for (int i = 0; i < spawnCount; i++) {
                    spawnRandomLootChest();
                }
                Bukkit.broadcastMessage(color(
                        plugin.getLangConfig().getString("lootchest.spawn-simple", "&aNeue Loot-Kisten sind gespawnt!")
                ));
            }
        }.runTaskTimer(plugin, 0L, intervalTicks);
    }

    private void startChestDespawnTask() {
        // Prüft jede Minute (20 Ticks * 60)
        new BukkitRunnable() {
            @Override
            public void run() {
                long now = System.currentTimeMillis();
                
                // Sicheres Iterieren um ConcurrentModificationException zu vermeiden
                Iterator<Location> it = activeChests.iterator();
                while (it.hasNext()) {
                    Location loc = it.next();
                    Long spawnTime = chestSpawnTimes.get(loc);
                    
                    if (spawnTime != null && now - spawnTime >= despawnMillis) {
                        if (loc.getBlock().getType() == Material.CHEST) {
                            loc.getBlock().setType(Material.AIR);
                        }
                        
                        // Manuell aus den Datenstrukturen entfernen
                        chestSpawnTimes.remove(loc);
                        it.remove(); // Aus der Liste entfernen (Thread-Safe)
                        
                        Bukkit.broadcastMessage(color(
                                plugin.getLangConfig().getString("lootchest.despawn-msg",
                                        "&eEine Lootkiste ist von selbst verschwunden.")
                        ));
                    }
                }
                
                // Config einmal speichern (wichtig!)
                saveActiveChests();
            }
        }.runTaskTimer(plugin, 20L * 60, 20L * 60);
    }

    private void clearAllActiveChests() {
        for (Location loc : new ArrayList<>(activeChests)) {
            if (loc.getBlock().getType() == Material.CHEST) {
                loc.getBlock().setType(Material.AIR);
            }
            removeChestData(loc);
        }
        activeChests.clear();
        chestSpawnTimes.clear();
    }

    private void spawnRandomLootChest() {
        if (activeChests.size() >= maxActiveChests) {
            return;
        }

        World world = Bukkit.getWorld(plugin.getConfig().getString("lootchest.world", "world"));
        if (world == null) return;

        int radius = plugin.getConfig().getInt("lootchest.spawn-radius", 500);
        ThreadLocalRandom r = ThreadLocalRandom.current();

        int x = r.nextInt(-radius, radius);
        int z = r.nextInt(-radius, radius);
        int y = world.getHighestBlockYAt(x, z);

        Location loc = new Location(world, x, y, z);

        // Kein Spawn auf Wasser, Lava, Kelp, Seegras
        Material below = world.getBlockAt(x, y - 1, z).getType();
        if (below == Material.WATER || below == Material.LAVA
                || below == Material.KELP || below == Material.SEAGRASS
                || below == Material.TALL_SEAGRASS) {
            return;
        }

        if (loc.getBlock().getType() != Material.AIR) {
            loc.add(0, 1, 0);
        }

        loc.getBlock().setType(Material.CHEST);
        Chest chest = (Chest) loc.getBlock().getState();
        fillChestWithRandomLoot(chest.getInventory());

        activeChests.add(loc);
        chestSpawnTimes.put(loc, System.currentTimeMillis());
        
        saveActiveChests();

        world.spawnParticle(Particle.FIREWORK, loc.clone().add(0.5, 1.0, 0.5),
                20, 0.3, 0.5, 0.3);
        world.playSound(loc, Sound.BLOCK_CHEST_OPEN, 1.0f, 1.0f);
    }

    private void fillChestWithRandomLoot(Inventory inv) {
        inv.clear();
        Random random = new Random();
        for (LootItem lootItem : lootItems) {
            if (lootItem.shouldSpawn(random)) {
                inv.addItem(lootItem.createStack(random));
            }
        }
    }

    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
        if (!(sender instanceof Player p)) {
            sender.sendMessage(color(plugin.getLangConfig().getString("lootchest.player-only", "")));
            return true;
        }
        if (!p.hasPermission("survivalplus.lootchests")) {
            p.sendMessage(color(plugin.getLangConfig().getString("lootchest.no-permission", "")));
            return true;
        }

        if (cmd.getName().equalsIgnoreCase("lootchests")) {
            if (activeChests.isEmpty()) {
                p.sendMessage(color(plugin.getLangConfig().getString("lootchest.no-chests", "")));
                return true;
            }
            p.sendMessage(color(plugin.getLangConfig().getString("lootchest.list-header", "")));
            for (Location loc : activeChests) {
                String coords = loc.getBlockX() + " " + loc.getBlockY() + " " + loc.getBlockZ();
                net.md_5.bungee.api.chat.TextComponent msg =
                        new net.md_5.bungee.api.chat.TextComponent(ChatColor.AQUA + " - " + coords + " " + ChatColor.GREEN + "[TP]");
                msg.setClickEvent(new net.md_5.bungee.api.chat.ClickEvent(
                        net.md_5.bungee.api.chat.ClickEvent.Action.RUN_COMMAND,
                        "/tploot " + loc.getWorld().getName() + " " + loc.getBlockX()
                                + " " + loc.getBlockY() + " " + loc.getBlockZ()
                ));
                msg.setHoverEvent(new net.md_5.bungee.api.chat.HoverEvent(
                        net.md_5.bungee.api.chat.HoverEvent.Action.SHOW_TEXT,
                        new net.md_5.bungee.api.chat.ComponentBuilder(color(
                                plugin.getLangConfig().getString("lootchest.list-tp-hover", "")
                        )).create()
                ));
                p.spigot().sendMessage(msg);
            }
            return true;
        }

        if (cmd.getName().equalsIgnoreCase("tploot")) {
            if (!p.isOp() && !p.hasPermission("survivalplus.lootchests")) return true;
            if (args.length != 4) {
                p.sendMessage(color(plugin.getLangConfig().getString("lootchest.tp-usage", "")));
                return true;
            }
            World w = Bukkit.getWorld(args[0]);
            if (w == null) {
                p.sendMessage(color(plugin.getLangConfig().getString("lootchest.tp-world-not-found", "")));
                return true;
            }
            try {
                double x = Double.parseDouble(args[1]);
                double y = Double.parseDouble(args[2]);
                double z = Double.parseDouble(args[3]);
                p.teleport(new Location(w, x + 0.5, y, z + 0.5));
                p.sendMessage(color(plugin.getLangConfig().getString("lootchest.tp-success", "")));
            } catch (NumberFormatException e) {
                p.sendMessage(color(plugin.getLangConfig().getString("lootchest.tp-invalid-coords", "")));
            }
            return true;
        }

        return false;
    }

    @EventHandler
    public void onInventoryOpen(InventoryOpenEvent event) {
        if (!(event.getPlayer() instanceof Player player)) return;
        InventoryHolder holder = event.getInventory().getHolder();
        if (!(holder instanceof Chest chest)) return;

        if (activeChests.contains(chest.getLocation())) {
            UUID uuid = player.getUniqueId();
            int looted = playerLootCount.getOrDefault(uuid, 0);
            if (looted >= maxLootPerPlayer) {
                player.sendMessage(color(plugin.getLangConfig().getString("lootchest.limit-reached", "&cDu hast dein Loot-Limit erreicht!")));
                event.setCancelled(true);
            }
        }
    }

    @EventHandler
    public void onInventoryClose(InventoryCloseEvent event) {
        if (!(event.getPlayer() instanceof Player player)) return;
        InventoryHolder holder = event.getInventory().getHolder();
        if (!(holder instanceof Chest chest)) return;

        Location chestLoc = chest.getLocation();
        if (activeChests.contains(chestLoc) && isInventoryEmpty(event.getInventory())) {
            UUID uuid = player.getUniqueId();
            int looted = playerLootCount.getOrDefault(uuid, 0);
            if (looted >= maxLootPerPlayer) return;
            playerLootCount.put(uuid, looted + 1);
            chestLoc.getBlock().setType(Material.AIR);
            
            removeChestData(chestLoc);
            
            Bukkit.broadcastMessage(color(
                    plugin.getLangConfig().getString("lootchest.removed-msg", "&aEine Lootkiste wurde geleert und entfernt.")
            ));
        }
    }

    private boolean isInventoryEmpty(Inventory inventory) {
        for (ItemStack item : inventory.getContents()) {
            if (item != null && item.getType() != Material.AIR) return false;
        }
        return true;
    }

    private String color(String msg) {
        return ChatColor.translateAlternateColorCodes('&', msg == null ? "" : msg);
    }

    private static class LootItem {
        private final Material material;
        private final String name;
        private final Map<Enchantment, Integer> enchantments = new HashMap<>();
        private final double chance;
        private final int min;
        private final int max;

        LootItem(FileConfiguration cfg, String path) {
            material = Material.valueOf(cfg.getString(path + ".material", "STONE"));
            name = cfg.getString(path + ".name", null);
            chance = cfg.getDouble(path + ".chance", 1.0);
            min = cfg.getInt(path + ".min", 1);
            max = cfg.getInt(path + ".max", 1);
            if (cfg.isConfigurationSection(path + ".enchantments")) {
                for (String enchKey : cfg.getConfigurationSection(path + ".enchantments").getKeys(false)) {
                    Enchantment ench = Enchantment.getByName(enchKey.toUpperCase());
                    if (ench != null) {
                        enchantments.put(ench, cfg.getInt(path + ".enchantments." + enchKey));
                    }
                }
            }
        }
        boolean shouldSpawn(Random r) { return r.nextDouble() <= chance; }
        ItemStack createStack(Random r) {
            int amt = min + r.nextInt(max - min + 1);
            ItemStack stack = new ItemStack(material, amt);
            ItemMeta meta = stack.getItemMeta();
            if (name != null) meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name));
            stack.setItemMeta(meta);
            for (Map.Entry<Enchantment, Integer> e : enchantments.entrySet()) {
                stack.addUnsafeEnchantment(e.getKey(), e.getValue());
            }
            return stack;
        }
    }
}