package net.viper.status.modules.chat.bridge;

import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Plugin;
import net.viper.status.modules.chat.AccountLinkManager;
import net.viper.status.modules.chat.ChatConfig;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;

/**
 * Discord-Br\u00fccke f\u00fcr bidirektionale Kommunikation.
 *
 * Fix #12: extractJsonString() behandelt Escape-Sequenzen jetzt korrekt.
 * Statt Zeichenvergleich mit dem Vorg\u00e4nger-Char wird ein expliziter Escape-Flag verwendet.
 */
public class DiscordBridge {

    private final Plugin plugin;
    private final ChatConfig config;
    private final Logger logger;
    private AccountLinkManager linkManager;

    private final java.util.Map<String, AtomicLong> lastMessageIds = new java.util.concurrent.ConcurrentHashMap<>();
    private volatile boolean running = false;

    public DiscordBridge(Plugin plugin, ChatConfig config) {
        this.plugin = plugin;
        this.config = config;
        this.logger = plugin.getLogger();
    }

    public void setLinkManager(AccountLinkManager linkManager) { this.linkManager = linkManager; }

    public void start() {
        if (!config.isDiscordEnabled()
                || config.getDiscordBotToken().isEmpty()
                || config.getDiscordBotToken().equals("YOUR_BOT_TOKEN_HERE")) {
            logger.warning("[ChatModule-Discord] Bot-Token nicht konfiguriert. Discord-Empfang deaktiviert.");
            return;
        }
        running = true;
        int interval = Math.max(2, config.getDiscordPollInterval());
        plugin.getProxy().getScheduler().schedule(plugin, this::pollAllChannels, interval, interval, TimeUnit.SECONDS);
        logger.info("[ChatModule-Discord] Br\u00fccke gestartet (Poll-Intervall: " + interval + "s).");
    }

    public void stop() { running = false; }

    // ===== Minecraft → Discord =====

    public void sendToDiscord(String webhookUrl, String username, String message, String avatarUrl) {
        if (webhookUrl == null || webhookUrl.isEmpty()) return;
        plugin.getProxy().getScheduler().runAsync(plugin, () -> {
            try {
                String payload = "{\"username\":\"" + escapeJson(username) + "\""
                        + (avatarUrl != null && !avatarUrl.isEmpty() ? ",\"avatar_url\":\"" + avatarUrl + "\"" : "")
                        + ",\"content\":\"" + escapeJson(message) + "\"}";
                postJson(webhookUrl, payload, null);
            } catch (Exception e) {
                logger.warning("[ChatModule-Discord] Webhook-Fehler: " + e.getMessage());
            }
        });
    }

    public void sendEmbedToDiscord(String webhookUrl, String title, String description, String colorHex) {
        if (webhookUrl == null || webhookUrl.isEmpty()) return;
        plugin.getProxy().getScheduler().runAsync(plugin, () -> {
            try {
                int color = 0x5865F2;
                try { color = Integer.parseInt(colorHex.replace("#", ""), 16); } catch (Exception ignored) {}
                String payload = "{\"embeds\":[{\"title\":\"" + escapeJson(title) + "\""
                        + ",\"description\":\"" + escapeJson(description) + "\""
                        + ",\"color\":" + color + "}]}";
                postJson(webhookUrl, payload, null);
            } catch (Exception e) {
                logger.warning("[ChatModule-Discord] Embed-Fehler: " + e.getMessage());
            }
        });
    }

    public void sendToChannel(String channelId, String message) {
        if (channelId == null || channelId.isEmpty()) return;
        if (config.getDiscordBotToken().isEmpty()) return;
        plugin.getProxy().getScheduler().runAsync(plugin, () -> {
            try {
                String url = "https://discord.com/api/v10/channels/" + channelId + "/messages";
                postJson(url, "{\"content\":\"" + escapeJson(message) + "\"}", "Bot " + config.getDiscordBotToken());
            } catch (Exception e) {
                logger.warning("[ChatModule-Discord] Send-to-Channel-Fehler: " + e.getMessage());
            }
        });
    }

    // ===== Discord → Minecraft (Polling) =====

    private void pollAllChannels() {
        if (!running) return;
        java.util.Set<String> channelIds = new java.util.LinkedHashSet<>();
        for (net.viper.status.modules.chat.ChatChannel ch : config.getChannels().values()) {
            if (!ch.getDiscordChannelId().isEmpty()) channelIds.add(ch.getDiscordChannelId());
        }
        if (!config.getDiscordAdminChannelId().isEmpty()) channelIds.add(config.getDiscordAdminChannelId());
        for (String channelId : channelIds) pollChannel(channelId);
    }

    private void pollChannel(String channelId) {
        try {
            AtomicLong lastId = lastMessageIds.computeIfAbsent(channelId, k -> new AtomicLong(0L));
            if (lastId.get() == 0L) {
                String initResp = getJson("https://discord.com/api/v10/channels/" + channelId + "/messages?limit=1",
                        "Bot " + config.getDiscordBotToken());
                if (initResp != null && !initResp.equals("[]") && !initResp.isEmpty()) {
                    java.util.List<DiscordMessage> initMsgs = parseMessages(initResp);
                    if (!initMsgs.isEmpty()) lastId.set(initMsgs.get(0).id);
                }
                return;
            }

            String url = "https://discord.com/api/v10/channels/" + channelId + "/messages?after=" + lastId.get() + "&limit=10";
            String response = getJson(url, "Bot " + config.getDiscordBotToken());
            if (response == null || response.equals("[]") || response.isEmpty()) return;

            java.util.List<DiscordMessage> messages = parseMessages(response);
            messages.sort(java.util.Comparator.comparingLong(m -> m.id));

            for (DiscordMessage msg : messages) {
                if (msg.id <= lastId.get()) continue;
                if (msg.isBot) continue;
                if (msg.content.isEmpty()) continue;
                lastId.set(msg.id);

                if (msg.content.startsWith("!link ")) {
                    String token = msg.content.substring(6).trim().toUpperCase();
                    if (linkManager != null) {
                        AccountLinkManager.LinkedAccount acc = linkManager.redeemDiscord(token, msg.authorId, msg.authorName);
                        if (acc != null) sendToChannel(channelId, "✅ Verkn\u00fcpfung erfolgreich! Minecraft-Account: **" + acc.minecraftName + "**");
                        else             sendToChannel(channelId, "❌ Ung\u00fcltiger oder abgelaufener Token. Bitte `/discordlink` im Spiel erneut ausf\u00fchren.");
                    }
                    continue;
                }

                String displayName = (linkManager != null)
                        ? linkManager.resolveDiscordName(msg.authorId, msg.authorName) : msg.authorName;
                String mcFormat = resolveFormat(channelId);
                if (mcFormat == null) continue;

                String formatted = ChatColor.translateAlternateColorCodes('&',
                        mcFormat.replace("{user}", displayName).replace("{message}", msg.content));
                ProxyServer.getInstance().getScheduler().runAsync(plugin,
                        () -> ProxyServer.getInstance().broadcast(new TextComponent(formatted)));
            }
        } catch (Exception e) {
            logger.fine("[ChatModule-Discord] Poll-Fehler f\u00fcr Kanal " + channelId + ": " + e.getMessage());
        }
    }

    private String resolveFormat(String channelId) {
        if (channelId.equals(config.getDiscordAdminChannelId())) return config.getDiscordFromFormat();
        for (net.viper.status.modules.chat.ChatChannel ch : config.getChannels().values()) {
            if (channelId.equals(ch.getDiscordChannelId())) return config.getDiscordFromFormat();
        }
        return null;
    }

    // ===== HTTP =====

    private void postJson(String urlStr, String payload, String authorization) throws Exception {
        HttpURLConnection conn = openConnection(urlStr, "POST", authorization);
        byte[] data = payload.getBytes(StandardCharsets.UTF_8);
        conn.setRequestProperty("Content-Length", String.valueOf(data.length));
        conn.setDoOutput(true);
        try (OutputStream os = conn.getOutputStream()) { os.write(data); }
        int code = conn.getResponseCode();
        if (code >= 400) logger.warning("[ChatModule-Discord] HTTP " + code + ": " + readStream(conn.getErrorStream()));
        conn.disconnect();
    }

    private String getJson(String urlStr, String authorization) throws Exception {
        HttpURLConnection conn = openConnection(urlStr, "GET", authorization);
        int code = conn.getResponseCode();
        if (code != 200) { conn.disconnect(); return null; }
        String result = readStream(conn.getInputStream());
        conn.disconnect();
        return result;
    }

    private HttpURLConnection openConnection(String urlStr, String method, String authorization) throws Exception {
        HttpURLConnection conn = (HttpURLConnection) new URL(urlStr).openConnection();
        conn.setRequestMethod(method);
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(8000);
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("User-Agent", "StatusAPI-ChatModule/1.0");
        if (authorization != null && !authorization.isEmpty()) conn.setRequestProperty("Authorization", authorization);
        return conn;
    }

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

    // ===== JSON Mini-Parser =====

    private static class DiscordMessage {
        long   id;
        String authorId = "", authorName = "", content = "";
        boolean isBot = false;
    }

    private java.util.List<DiscordMessage> parseMessages(String json) {
        java.util.List<DiscordMessage> result = new java.util.ArrayList<>();
        int depth = 0, start = -1;
        for (int i = 0; i < json.length(); i++) {
            char c = json.charAt(i);
            if (c == '{') { if (depth++ == 0) start = i; }
            else if (c == '}') {
                if (--depth == 0 && start != -1) {
                    DiscordMessage msg = parseMessage(json.substring(start, i + 1));
                    if (msg != null) result.add(msg);
                    start = -1;
                }
            }
        }
        return result;
    }

    private DiscordMessage parseMessage(String obj) {
        try {
            DiscordMessage msg = new DiscordMessage();
            msg.id      = Long.parseLong(extractJsonString(obj, "id"));
            msg.content = unescapeJson(extractJsonString(obj, "content"));

            // Webhook-Nachrichten als Bot markieren (Echo-Loop verhindern)
            if (!extractJsonString(obj, "webhook_id").isEmpty()) {
                msg.isBot = true;
                return msg;
            }

            int authStart = obj.indexOf("\"author\"");
            if (authStart >= 0) {
                String authBlock = extractJsonObject(obj, authStart);
                msg.authorId   = extractJsonString(authBlock, "id");
                msg.authorName = unescapeJson(extractJsonString(authBlock, "username"));
                msg.isBot      = "true".equals(extractJsonString(authBlock, "bot"));
            }
            return msg;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * FIX #12: Escape-Sequenzen werden korrekt mit einem Escape-Flag behandelt
     * statt den Vorg\u00e4nger-Char zu vergleichen (der bei '\\' + '"' versagt).
     * Gibt immer einen leeren String zur\u00fcck wenn der Key nicht gefunden wird (nie null).
     */
    private String extractJsonString(String json, String key) {
        if (json == null || key == null) return "";
        String fullKey = "\"" + key + "\"";
        int keyIdx = json.indexOf(fullKey);
        if (keyIdx < 0) return "";
        int colon = json.indexOf(':', keyIdx + fullKey.length());
        if (colon < 0) return "";
        int valStart = colon + 1;
        while (valStart < json.length() && json.charAt(valStart) == ' ') valStart++;
        if (valStart >= json.length()) return "";
        char first = json.charAt(valStart);
        if (first == '"') {
            // FIX: Expliziter Escape-Flag statt Vorg\u00e4nger-Char-Vergleich
            int end = valStart + 1;
            boolean escaped = false;
            while (end < json.length()) {
                char ch = json.charAt(end);
                if (escaped) {
                    escaped = false;
                } else if (ch == '\\') {
                    escaped = true;
                } else if (ch == '"') {
                    break;
                }
                end++;
            }
            return json.substring(valStart + 1, end);
        } else {
            int end = valStart;
            while (end < json.length() && ",}\n".indexOf(json.charAt(end)) < 0) end++;
            return json.substring(valStart, end).trim();
        }
    }

    private String extractJsonObject(String json, int fromIndex) {
        int depth = 0, start = -1;
        for (int i = fromIndex; i < json.length(); i++) {
            char c = json.charAt(i);
            if (c == '{') { if (depth++ == 0) start = i; }
            else if (c == '}') { if (--depth == 0 && start >= 0) return json.substring(start, i + 1); }
        }
        return "";
    }

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

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