package net.viper.status.modules.forum;

import net.viper.status.StatusAPI;
import net.md_5.bungee.api.ChatColor;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.CommandSender;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.ProxyServer;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.chat.ClickEvent;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.chat.HoverEvent;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.chat.TextComponent;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.plugin.Command;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.plugin.Listener;
import net.viper.status.StatusAPI;
import net.md_5.bungee.api.plugin.Plugin;
import net.viper.status.StatusAPI;
import net.md_5.bungee.event.EventHandler;
import net.viper.status.module.Module;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * ForumBridgeModule: Verbindet das WP Business Forum mit dem Minecraft-Server.
 *
 * Fix #13: extractJsonString() gibt jetzt immer einen leeren String statt null zur\u00fcck.
 * Alle Aufrufer m\u00fcssen nicht mehr auf null pr\u00fcfen, was NullPointerExceptions verhindert.
 */
public class ForumBridgeModule implements Module, Listener {

    private Plugin plugin;
    private ForumNotifStorage storage;

    private boolean enabled = true;
    private String wpBaseUrl = "";
    private String apiSecret = "";
    private int loginDelaySeconds = 3;

    @Override
    public String getName() { return "ForumBridgeModule"; }

    @Override
    public void onEnable(Plugin plugin) {
        this.plugin = plugin;
        loadConfig(plugin);
        if (!enabled) { StatusAPI.debugLog(plugin, "ForumBridgeModule ist deaktiviert."); return; }

        storage = new ForumNotifStorage(plugin.getDataFolder(), plugin.getLogger());
        storage.load();

        plugin.getProxy().getPluginManager().registerListener(plugin, this);
        ProxyServer.getInstance().getPluginManager().registerCommand(plugin, new ForumLinkCommand());
        ProxyServer.getInstance().getPluginManager().registerCommand(plugin, new ForumCommand());

        plugin.getProxy().getScheduler().schedule(plugin, () -> {
            try { storage.save(); } catch (Exception e) { plugin.getLogger().warning("ForumBridge Auto-Save Fehler: " + e.getMessage()); }
        }, 10, 10, TimeUnit.MINUTES);

        plugin.getProxy().getScheduler().schedule(plugin, () -> storage.purgeOld(30), 1, 24, TimeUnit.HOURS);
        plugin.getLogger().fine("ForumBridgeModule aktiviert.");
    }

    @Override
    public void onDisable(Plugin plugin) {
        if (storage != null) { storage.save(); StatusAPI.debugLog(plugin, "Forum-Benachrichtigungen gespeichert."); }
    }

    private void loadConfig(Plugin plugin) {
        try {
            Properties props = new Properties();
            File configFile = new File(plugin.getDataFolder(), "verify.properties");
            if (configFile.exists()) {
                try (FileInputStream fis = new FileInputStream(configFile)) { props.load(fis); }
            }
            this.enabled           = !"false".equalsIgnoreCase(props.getProperty("forum.enabled", "true"));
            this.wpBaseUrl         = props.getProperty("forum.wp_url", props.getProperty("wp_verify_url", ""));
            this.apiSecret         = props.getProperty("forum.api_secret", "");
            this.loginDelaySeconds = parseInt(props.getProperty("forum.login_delay_seconds", "3"), 3);
        } catch (Exception e) {
            plugin.getLogger().warning("Fehler beim Laden der ForumBridge-Config: " + e.getMessage());
        }
    }

    private int parseInt(String s, int def) { try { return Integer.parseInt(s); } catch (Exception e) { return def; } }

    // ===== HTTP HANDLER =====

    public String handleNotify(String body, String apiKeyHeader) {
        if (!apiSecret.isEmpty() && !apiSecret.equals(apiKeyHeader)) {
            return "{\"success\":false,\"error\":\"unauthorized\"}";
        }

        // FIX #13: extractJsonString gibt "" statt null → kein NullPointerException m\u00f6glich
        String playerUuid = extractJsonString(body, "player_uuid");
        String type       = extractJsonString(body, "type");
        String title      = extractJsonString(body, "title");
        String author     = extractJsonString(body, "author");
        String url        = extractJsonString(body, "url");

        if (playerUuid.isEmpty()) return "{\"success\":false,\"error\":\"missing_player_uuid\"}";

        java.util.UUID uuid;
        try { uuid = java.util.UUID.fromString(playerUuid); }
        catch (Exception e) { return "{\"success\":false,\"error\":\"invalid_uuid\"}"; }

        if ("thread".equalsIgnoreCase(type) && title.toLowerCase().contains("umfrage")) type = "poll";
        if (type.isEmpty()) type = "reply";

        // Alle Werte sind garantiert nicht null (extractJsonString gibt "" zur\u00fcck)
        ForumNotification notification = new ForumNotification(uuid, type, title, author, url);

        ProxiedPlayer online = ProxyServer.getInstance().getPlayer(uuid);
        if (online != null && online.isConnected()) {
            deliverNotification(online, notification);
            notification.setDelivered(true);
            return "{\"success\":true,\"delivered\":true}";
        }

        storage.add(notification);
        return "{\"success\":true,\"delivered\":false}";
    }

    public String handleUnlink(String body, String apiKeyHeader) {
        if (!apiSecret.isEmpty() && !apiSecret.equals(apiKeyHeader)) return "{\"success\":false,\"error\":\"unauthorized\"}";
        return "{\"success\":true}";
    }

    public String handleStatus() {
        String version = "unknown";
        try { if (plugin.getDescription() != null) version = plugin.getDescription().getVersion(); } catch (Exception ignored) {}
        return "{\"success\":true,\"module\":\"ForumBridgeModule\",\"version\":\"" + version + "\"}";
    }

    // ===== NOTIFICATION =====

    private void deliverNotification(ProxiedPlayer player, ForumNotification notif) {
        String color = notif.getTypeColor();
        String label = notif.getTypeLabel();
        player.sendMessage(new TextComponent("§8§m                                                        "));
        player.sendMessage(new TextComponent("§6§l✉ Forum §8» " + color + label));
        if (!notif.getTitle().isEmpty()) player.sendMessage(new TextComponent("§7  " + notif.getTitle()));
        if (!notif.getAuthor().isEmpty()) player.sendMessage(new TextComponent("§7  von §f" + notif.getAuthor()));
        if (!notif.getUrl().isEmpty()) {
            TextComponent link = new TextComponent("§a  ➜ Im Forum ansehen");
            link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, notif.getUrl()));
            link.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
                    new ComponentBuilder("§7Klicke um den Beitrag im Forum zu \u00f6ffnen").create()));
            player.sendMessage(link);
        }
        player.sendMessage(new TextComponent("§8§m                                                        "));
    }

    private void deliverPending(ProxiedPlayer player) {
        List<ForumNotification> pending = storage.getPending(player.getUniqueId());
        if (pending.isEmpty()) return;
        int count = pending.size();
        if (count > 3) {
            player.sendMessage(new TextComponent("§8§m                                                        "));
            player.sendMessage(new TextComponent("§6§l✉ Forum §8» §fDu hast §e" + count + " §fneue Benachrichtigungen!"));
            player.sendMessage(new TextComponent("§7  Tippe §e/forum §7um sie anzuzeigen."));
            player.sendMessage(new TextComponent("§8§m                                                        "));
        } else {
            for (ForumNotification n : pending) deliverNotification(player, n);
        }
        storage.markAllDelivered(player.getUniqueId());
        storage.clearDelivered(player.getUniqueId());
    }

    @EventHandler
    public void onJoin(PostLoginEvent e) {
        ProxiedPlayer player = e.getPlayer();
        plugin.getProxy().getScheduler().schedule(plugin, () -> {
            if (player.isConnected()) deliverPending(player);
        }, loginDelaySeconds, TimeUnit.SECONDS);
    }

    // ===== COMMANDS =====

    private class ForumLinkCommand extends Command {
        public ForumLinkCommand() { super("forumlink", null, "fl"); }

        @Override
        public void execute(CommandSender sender, String[] args) {
            if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(new TextComponent("§cNur Spieler k\u00f6nnen diesen Befehl benutzen.")); return; }
            ProxiedPlayer p = (ProxiedPlayer) sender;
            if (args.length != 1) {
                p.sendMessage(new TextComponent("§eBenutzung: §f/forumlink <token>"));
                p.sendMessage(new TextComponent("§7Den Token erh\u00e4ltst du in deinem Forum-Profil unter §fMinecraft-Verkn\u00fcpfung§7."));
                return;
            }
            String token = args[0].trim().toUpperCase();
            if (wpBaseUrl.isEmpty()) { p.sendMessage(new TextComponent("§cForum-Verkn\u00fcpfung ist nicht konfiguriert.")); return; }
            p.sendMessage(new TextComponent("§7\u00dcberpr\u00fcfe Token..."));

            plugin.getProxy().getScheduler().runAsync(plugin, () -> {
                try {
                    String endpoint = wpBaseUrl + "/wp-json/mc-bridge/v1/verify-link";
                    String payload  = "{\"token\":\"" + escapeJson(token) + "\","
                            + "\"mc_uuid\":\"" + p.getUniqueId() + "\","
                            + "\"mc_name\":\"" + escapeJson(p.getName()) + "\"}";

                    HttpURLConnection conn = (HttpURLConnection) new URL(endpoint).openConnection();
                    conn.setRequestMethod("POST");
                    conn.setDoOutput(true);
                    conn.setConnectTimeout(5000);
                    conn.setReadTimeout(7000);
                    conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
                    if (!apiSecret.isEmpty()) conn.setRequestProperty("X-Api-Key", apiSecret);

                    Charset utf8 = Charset.forName("UTF-8");
                    try (OutputStream os = conn.getOutputStream()) { os.write(payload.getBytes(utf8)); }

                    int code = conn.getResponseCode();
                    String resp = code >= 200 && code < 300
                            ? streamToString(conn.getInputStream(), utf8)
                            : streamToString(conn.getErrorStream(), utf8);

                    if (resp != null && resp.contains("\"success\":true")) {
                        String displayName = extractJsonString(resp, "display_name");
                        String username    = extractJsonString(resp, "username");
                        String show = !displayName.isEmpty() ? displayName : username;
                        p.sendMessage(new TextComponent("§8§m                                                        "));
                        p.sendMessage(new TextComponent("§a§l✓ §fForum-Account erfolgreich verkn\u00fcpft!"));
                        if (!show.isEmpty()) p.sendMessage(new TextComponent("§7  Forum-User: §f" + show));
                        p.sendMessage(new TextComponent("§7  Du erh\u00e4ltst jetzt Ingame-Benachrichtigungen."));
                        p.sendMessage(new TextComponent("§8§m                                                        "));
                    } else {
                        String error   = extractJsonString(resp, "error");
                        String message = extractJsonString(resp, "message");
                        if ("token_expired".equals(error))         p.sendMessage(new TextComponent("§c✗ Der Token ist abgelaufen."));
                        else if ("uuid_already_linked".equals(error)) p.sendMessage(new TextComponent("§c✗ " + (!message.isEmpty() ? message : "Diese UUID ist bereits verkn\u00fcpft.")));
                        else if ("invalid_token".equals(error))    p.sendMessage(new TextComponent("§c✗ Ung\u00fcltiger Token."));
                        else p.sendMessage(new TextComponent("§c✗ Verkn\u00fcpfung fehlgeschlagen: " + (!error.isEmpty() ? error : "Unbekannter Fehler")));
                    }
                } catch (Exception ex) {
                    p.sendMessage(new TextComponent("§c✗ Fehler bei der Verbindung zum Forum."));
                    plugin.getLogger().warning("ForumLink Fehler: " + ex.getMessage());
                }
            });
        }
    }

    private class ForumCommand extends Command {
        public ForumCommand() { super("forum"); }

        @Override
        public void execute(CommandSender sender, String[] args) {
            if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(new TextComponent("§cNur Spieler k\u00f6nnen diesen Befehl benutzen.")); return; }
            ProxiedPlayer p = (ProxiedPlayer) sender;
            List<ForumNotification> pending = storage.getPending(p.getUniqueId());

            if (pending.isEmpty()) {
                p.sendMessage(new TextComponent("§7Keine neuen Forum-Benachrichtigungen."));
                if (!wpBaseUrl.isEmpty()) {
                    TextComponent link = new TextComponent("§a➜ Forum \u00f6ffnen");
                    link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, wpBaseUrl));
                    link.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§7Klicke um das Forum zu \u00f6ffnen").create()));
                    p.sendMessage(link);
                }
                return;
            }

            p.sendMessage(new TextComponent("§8§m                                                        "));
            p.sendMessage(new TextComponent("§6§l✉ Forum-Benachrichtigungen §8(§f" + pending.size() + "§8)"));
            p.sendMessage(new TextComponent(""));
            int shown = 0;
            for (ForumNotification n : pending) {
                if (shown >= 10) { p.sendMessage(new TextComponent("§7  ... und " + (pending.size() - 10) + " weitere")); break; }
                String color = n.getTypeColor();
                TextComponent line = new TextComponent(color + " • " + n.getTypeLabel() + "§7: ");
                TextComponent detail = new TextComponent(!n.getTitle().isEmpty() ? "§f" + n.getTitle() : "§fvon " + n.getAuthor());
                if (!n.getUrl().isEmpty()) {
                    detail.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, n.getUrl()));
                    detail.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§7Klicke zum \u00d6ffnen").create()));
                }
                line.addExtra(detail);
                p.sendMessage(line);
                shown++;
            }
            p.sendMessage(new TextComponent(""));
            p.sendMessage(new TextComponent("§8§m                                                        "));
            storage.markAllDelivered(p.getUniqueId());
            storage.clearDelivered(p.getUniqueId());
        }
    }

    // ===== HELPER =====

    public ForumNotifStorage getStorage() { return storage; }

    /**
     * FIX #13: Gibt immer einen leeren String zur\u00fcck, niemals null.
     * Verhindert NullPointerExceptions in allen Aufrufern.
     */
    private static String extractJsonString(String json, String key) {
        if (json == null || key == null) return "";
        String search = "\"" + key + "\"";
        int idx = json.indexOf(search);
        if (idx < 0) return "";
        int colon = json.indexOf(':', idx + search.length());
        if (colon < 0) return "";
        int i = colon + 1;
        while (i < json.length() && Character.isWhitespace(json.charAt(i))) i++;
        if (i >= json.length()) return "";
        char c = json.charAt(i);
        if (c == '"') {
            i++;
            StringBuilder sb = new StringBuilder();
            boolean escape = false;
            while (i < json.length()) {
                char ch = json.charAt(i++);
                if (escape) { sb.append(ch); escape = false; }
                else { if (ch == '\\') escape = true; else if (ch == '"') break; else sb.append(ch); }
            }
            return sb.toString();
        }
        return "";
    }

    private static String escapeJson(String s) {
        if (s == null) return "";
        return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r");
    }

    private static String streamToString(InputStream in, Charset charset) throws IOException {
        if (in == null) return "";
        try (BufferedReader br = new BufferedReader(new InputStreamReader(in, charset))) {
            StringBuilder sb = new StringBuilder(); String line;
            while ((line = br.readLine()) != null) sb.append(line);
            return sb.toString();
        }
    }
}
