package de.viper.survivalplus.Manager;

import de.viper.survivalplus.SurvivalPlus;
import me.clip.placeholderapi.PlaceholderAPI;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.model.user.User;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.ScoreboardManager;
import org.bukkit.scoreboard.Team;

import java.io.File;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TablistManager implements Listener {

    private final SurvivalPlus plugin;
    private final List<String> headerAnim = new ArrayList<>();
    private final List<String> footerAnim = new ArrayList<>();
    private int headerIndex = 0;
    private int footerIndex = 0;
    private boolean enabled;
    private String serverName;
    private String website;
    private String teamspeakAddress;
    private String discordAddress;
    private boolean showTeamspeak;
    private boolean showDiscord;
    private String staffPermission;
    private String separatorLine;
    private LuckPerms luckPerms;
    private boolean hasPlaceholderAPI;
    private FileConfiguration nicknameConfig;

    private final Scoreboard scoreboard;
    private final Map<String, Team> prefixTeams = new HashMap<>();

    private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}");

    public TablistManager(SurvivalPlus plugin) {
        this.plugin = plugin;

        // Scoreboard initialisieren
        ScoreboardManager scoreboardManager = Bukkit.getScoreboardManager();
        this.scoreboard = scoreboardManager != null ? scoreboardManager.getMainScoreboard() : null;

        try {
            File tablistFile = new File(plugin.getDataFolder(), "tablist.yml");
            if (!tablistFile.exists()) {
                plugin.saveResource("tablist.yml", false);
            }
        } catch (Exception ignored) {}

        try {
            plugin.reloadTablistConfig();
        } catch (Exception ignored) {}

        FileConfiguration config = plugin.getTablistConfig();

        this.enabled = config.getBoolean("enabled", true);
        this.serverName = config.getString("server-name", "SurvivalPlus");
        this.website = config.getString("website", "www.example.com");
        this.teamspeakAddress = config.getString("teamspeak-address", "ts.example.com");
        this.discordAddress = config.getString("discord-address", "discord.gg/example");
        this.showTeamspeak = config.getBoolean("show-teamspeak", true);
        this.showDiscord = config.getBoolean("show-discord", true);
        this.staffPermission = config.getString("staff-permission", "survivalplus.staff");
        this.separatorLine = config.getString("separator-line", "&8&l&m================");

        try {
            this.luckPerms = LuckPermsProvider.get();
        } catch (Throwable e) {
            luckPerms = null;
        }

        this.hasPlaceholderAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");

        // Registriere Chat-Listener hier, damit Prefix im Chat gesetzt wird
        Bukkit.getPluginManager().registerEvents(this, plugin);

        if (!enabled) {
            plugin.getLogger().info("Tablist ist deaktiviert (tablist.yml -> enabled: false)");
            return;
        }

        List<String> configHeader = config.getStringList("header-animations");
        List<String> configFooter = config.getStringList("footer-animations");

        if (configHeader != null && !configHeader.isEmpty()) {
            headerAnim.addAll(configHeader);
        } else {
            headerAnim.add("&6&l{server}\n&7Willkommen, &a{player}\n&7Online Player: &e{online}\n&6Online Staff: &e{staff}\n");
        }

        if (configFooter != null && !configFooter.isEmpty()) {
            footerAnim.addAll(configFooter);
        } else {
            footerAnim.add("&7SurvivalPlus &8| &eDein Abenteuer!");
        }

        startAnimation();
    }

    private void startAnimation() {
        if (!enabled) return;

        int interval = Math.max(1, plugin.getTablistConfig().getInt("interval-ticks", 20));

        new BukkitRunnable() {
            @Override
            public void run() {
                reloadNicknameConfig();

                if (headerAnim.isEmpty() || footerAnim.isEmpty()) return;

                int onlinePlayers = Bukkit.getOnlinePlayers().size();
                int onlineStaff = (int) Bukkit.getOnlinePlayers().stream()
                        .filter(p -> p.hasPermission(staffPermission))
                        .count();

                SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
                String currentTime = dateFormat.format(new Date());

                for (Player player : Bukkit.getOnlinePlayers()) {
                    try {
                        // ------ Name / Nick handling exactly like in your old code ------
                        String displayName = getNickname(player);
                        if (displayName == null || displayName.trim().isEmpty()) {
                            displayName = player.getName();
                        }

                        String rankPrefix = getPlayerPrefix(player);
                        // Update nametag (prefix above head) as in old code:
                        updateNametag(player, rankPrefix, getNickname(player)); // pass raw nickname (colored) if present

                        // PlayerList (TAB)
                        int ping = getPlayerPing(player);
                        String playerListName;
                        if (luckPerms == null && !hasPlaceholderAPI) {
                            playerListName = displayName;
                        } else {
                            playerListName = color(rankPrefix + displayName + (ping >= 0 ? "&8 | &e" + ping + "ms" : ""));
                        }

                        try {
                            player.setPlayerListName(playerListName);
                        } catch (Exception ignored) {
                            // safe fallback: set without prefix
                            try {
                                player.setPlayerListName(color(displayName));
                            } catch (Exception ignored2) {}
                        }

                        // Header/Footer build (including TS/Discord/Website and time)
                        String headerRaw = headerAnim.get(headerIndex)
                                .replace("{server}", serverName)
                                .replace("{player}", displayName)
                                .replace("{online}", String.valueOf(onlinePlayers))
                                .replace("{staff}", String.valueOf(onlineStaff));
                        String footerRaw = footerAnim.get(footerIndex);

                        StringBuilder footerBuilder = new StringBuilder();
                        footerBuilder.append("\n");
                        footerBuilder.append(color(footerRaw)).append("\n");
                        footerBuilder.append(color(separatorLine)).append("\n");
                        if (showTeamspeak || showDiscord) {
                            StringBuilder socialLine = new StringBuilder();
                            if (showTeamspeak) {
                                socialLine.append("&7TS: &a").append(teamspeakAddress);
                            }
                            if (showTeamspeak && showDiscord) {
                                socialLine.append(" &8| ");
                            }
                            if (showDiscord) {
                                socialLine.append("&7Discord: &d").append(discordAddress);
                            }
                            footerBuilder.append(color(socialLine.toString())).append("\n");
                        }
                        footerBuilder.append("     &7Webseite: &b").append(website).append("\n");
                        footerBuilder.append(color(separatorLine)).append("\n");
                        footerBuilder.append("&7Datum & Uhrzeit: &e").append(currentTime);

                        String header = color(headerRaw);
                        String footer = color(footerBuilder.toString());

                        // Try Adventure-based header/footer first, fallback to String reflection
                        boolean done = tryAdventureComponent(player, headerRaw, footerBuilder.toString());
                        if (!done) {
                            tryStringMethod(player, header, footer);
                        }

                    } catch (Exception ex) {
                        plugin.getLogger().warning("TablistManager error: " + ex.getMessage());
                    }
                }

                headerIndex = (headerIndex + 1) % headerAnim.size();
                footerIndex = (footerIndex + 1) % footerAnim.size();
            }
        }.runTaskTimer(plugin, 40L, interval);
    }

    /**
     * Lädt nicknames.yml neu.
     */
    private void reloadNicknameConfig() {
        try {
            File nicknameFile = new File(plugin.getDataFolder(), "nicknames.yml");
            if (!nicknameFile.exists()) {
                plugin.saveResource("nicknames.yml", false);
            }
            this.nicknameConfig = YamlConfiguration.loadConfiguration(nicknameFile);
        } catch (Exception ignored) {
            this.nicknameConfig = null;
        }
    }

    /**
     * Nickname abrufen und formatieren (farbig + hex)
     */
    private String getNickname(Player player) {
        if (nicknameConfig == null) return null;
        try {
            String uuid = player.getUniqueId().toString();
            String nickname = nicknameConfig.getString(uuid);
            if (nickname != null && !nickname.trim().isEmpty()) {
                return translateNickColors(nickname);
            }
        } catch (Exception ignored) {}
        return null;
    }

    /**
     * Übersetzt &-Codes und Hex-Farben
     */
    private String translateNickColors(String input) {
        String withLegacy = org.bukkit.ChatColor.translateAlternateColorCodes('&', input);
        Matcher matcher = HEX_PATTERN.matcher(withLegacy);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String hexCode = matcher.group();
            String replacement = ChatColor.of(hexCode).toString();
            matcher.appendReplacement(sb, replacement);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * Aktualisiert den Nametag über dem Kopf genau wie in deinem alten Code:
     * - Wenn Nick vorhanden: Nick als Prefix (zeigt [Nick] Spielername)
     * - Sonst Rang-Prefix (oder [Spieler] wenn kein Rang)
     * Der Entry ist dabei immer der echte Spielername.
     */
    private void updateNametag(Player player, String rankPrefix, String nickname) {
        if (scoreboard == null) return;

        try {
            String teamName = generateTeamName(player, rankPrefix);
            Team team = scoreboard.getTeam(teamName);

            if (team == null) {
                // register new team if not exists
                try {
                    team = scoreboard.registerNewTeam(teamName);
                } catch (IllegalArgumentException ignored) {
                    team = scoreboard.getTeam(teamName);
                }
            }
            if (team == null) return;

            String prefix;
            if (nickname != null && !nickname.trim().isEmpty()) {
                prefix = nickname;
            } else {
                String rp = (rankPrefix != null && !rankPrefix.trim().isEmpty()) ? rankPrefix : "&7[&aSpieler&7]&f ";
                prefix = color(rp);
            }

            try {
                team.setPrefix(prefix);
            } catch (Exception ignored) {}

            // Ensure the genuine player name is used as the team entry
            String entry = player.getName();

            if (!team.hasEntry(entry)) {
                // Remove from other teams to avoid duplicates
                for (Team existingTeam : scoreboard.getTeams()) {
                    if (existingTeam != null && !existingTeam.getName().equals(teamName)) {
                        try {
                            existingTeam.removeEntry(entry);
                        } catch (Exception ignored) {}
                    }
                }
                try {
                    team.addEntry(entry);
                } catch (Exception ignored) {}
            }

            // Synchronize scoreboard to online players (use main scoreboard)
            for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
                try {
                    if (onlinePlayer.getScoreboard() != scoreboard) {
                        onlinePlayer.setScoreboard(scoreboard);
                    }
                } catch (Exception ignored) {}
            }
        } catch (Exception ignored) {}
    }

    /**
     * Eindeutigen Teamnamen generieren
     */
    private String generateTeamName(Player player, String prefix) {
        if (prefix == null || prefix.trim().isEmpty()) {
            return "nametag_" + player.getUniqueId().toString().substring(0, 8);
        }
        String sanitizedPrefix = prefix.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
        return "prefix_" + sanitizedPrefix.substring(0, Math.min(sanitizedPrefix.length(), 12));
    }

    /**
     * Versucht, Adventure Components zu verwenden (falls verfügbar).
     */
    private boolean tryAdventureComponent(Player player, String headerRaw, String footerRaw) {
        try {
            Class<?> compClass = Class.forName("net.kyori.adventure.text.Component");
            Method textMethod = null;
            try {
                textMethod = compClass.getMethod("text", CharSequence.class);
            } catch (NoSuchMethodException ignored) {
                try {
                    textMethod = compClass.getMethod("text", String.class);
                } catch (NoSuchMethodException ignored2) {}
            }

            Object headerComp = null;
            Object footerComp = null;

            if (textMethod != null) {
                headerComp = textMethod.invoke(null, color(headerRaw));
                footerComp = textMethod.invoke(null, color(footerRaw));
            } else {
                try {
                    Class<?> miniMsgClass = Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
                    Method miniMsgSingleton = miniMsgClass.getMethod("miniMessage");
                    Object miniMsg = miniMsgSingleton.invoke(null);
                    Method deserialize = miniMsgClass.getMethod("deserialize", String.class);
                    headerComp = deserialize.invoke(miniMsg, headerRaw);
                    footerComp = deserialize.invoke(miniMsg, footerRaw);
                } catch (Exception ignored) {}
            }

            if (headerComp == null || footerComp == null) return false;

            Method m = findMethod(player.getClass(), "sendPlayerListHeaderAndFooter", compClass, compClass);
            if (m == null) m = findMethod(player.getClass(), "setPlayerListHeaderFooter", compClass, compClass);

            if (m != null) {
                m.invoke(player, headerComp, footerComp);
                return true;
            } else {
                Method mh = findMethod(player.getClass(), "sendPlayerListHeader", compClass);
                Method mf = findMethod(player.getClass(), "sendPlayerListFooter", compClass);
                if (mh != null && mf != null) {
                    mh.invoke(player, headerComp);
                    mf.invoke(player, footerComp);
                    return true;
                }
            }
        } catch (Exception ignored) {
            // fallback to string method
        }
        return false;
    }

    /**
     * Fallback: konventionelle String-Header/Footer mittels Reflection
     */
    private boolean tryStringMethod(Player player, String header, String footer) {
        try {
            Method m = findMethod(player.getClass(), "sendPlayerListHeaderFooter", String.class, String.class);
            if (m == null) {
                m = findMethod(player.getClass(), "setPlayerListHeaderFooter", String.class, String.class);
            }
            if (m != null) {
                m.invoke(player, header, footer);
                return true;
            }

            Method mh = findMethod(player.getClass(), "sendPlayerListHeader", String.class);
            Method mf = findMethod(player.getClass(), "sendPlayerListFooter", String.class);
            if (mh != null && mf != null) {
                mh.invoke(player, header);
                mf.invoke(player, footer);
                return true;
            }
        } catch (Exception ignored) {}
        return false;
    }

    private Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
        Class<?> current = clazz;
        while (current != null) {
            try {
                Method m = current.getMethod(name, paramTypes);
                if (m != null) return m;
            } catch (NoSuchMethodException ignored) {}
            current = current.getSuperclass();
        }
        return null;
    }

    /**
     * Holt den Rang-Prefix (LuckPerms -> PlaceholderAPI -> Default [Spieler])
     */
    private String getPlayerPrefix(Player player) {
        if (luckPerms != null) {
            try {
                User user = luckPerms.getPlayerAdapter(Player.class).getUser(player);
                String prefix = user.getCachedData().getMetaData().getPrefix();
                if (prefix != null && !prefix.trim().isEmpty()) {
                    return prefix + " ";
                }
            } catch (Exception ignored) {}
            if (hasPlaceholderAPI) {
                try {
                    String prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%");
                    if (prefix != null && !prefix.trim().isEmpty()) {
                        return prefix + " ";
                    }
                } catch (Exception ignored) {}
            }
            return "&7[&aSpieler&7]&f ";
        }
        if (hasPlaceholderAPI) {
            try {
                String prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%");
                if (prefix != null && !prefix.trim().isEmpty()) {
                    return prefix + " ";
                }
            } catch (Exception ignored) {}
        }
        return "&7[&aSpieler&7]&f ";
    }

    private int getPlayerPing(Player player) {
        try {
            Method getHandle = player.getClass().getMethod("getHandle");
            Object entityPlayer = getHandle.invoke(player);
            return entityPlayer.getClass().getField("ping").getInt(entityPlayer);
        } catch (Exception ignored) {
            return -1;
        }
    }

    /**
     * Chat-Format (Nickname wird hier angezeigt). Setzt Prefix + Nick/Name im Chat.
     */
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onPlayerChat(AsyncPlayerChatEvent event) {
        try {
            Player player = event.getPlayer();
            String displayName = getNickname(player);
            if (displayName == null || displayName.trim().isEmpty()) {
                displayName = player.getName();
            }

            String prefix = getPlayerPrefix(player);
            // Format: Rang + Nick + ": " + Nachricht
            String format = color(prefix + displayName + "&7: &f") + "%2$s";
            event.setFormat(format);
        } catch (Exception ignored) {}
    }

    private String color(String msg) {
        if (msg == null) return "";
        return msg.replace("&", "§");
    }
}
