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.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;

/**
 * Telegram-Br\u00fccke f\u00fcr bidirektionale Kommunikation.
 *
 * Minecraft → Telegram: Via Bot API (sendMessage)
 * Telegram → Minecraft: Via Long-Polling (getUpdates)
 *
 * Voraussetzungen:
 *   - Telegram Bot via @BotFather erstellen
 *   - Bot-Token in chat.yml eintragen
 *   - Bot in die gew\u00fcnschten Gruppen/Kan\u00e4le einladen
 *   - Bot zu Admin machen (f\u00fcr Gruppen-Nachrichten empfangen)
 */
public class TelegramBridge {

    private static final String API_BASE = "https://api.telegram.org/bot";

    private final Plugin plugin;
    private final ChatConfig config;
    private final Logger logger;
    private AccountLinkManager linkManager; // wird nach dem Start gesetzt

    // Letztes verarbeitetes Update-ID (f\u00fcr getUpdates Offset)
    private final AtomicLong lastUpdateId = new AtomicLong(0L);

    private volatile boolean running = false;

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

    /** Setzt den AccountLinkManager – muss vor start() aufgerufen werden. */
    public void setLinkManager(AccountLinkManager linkManager) {
        this.linkManager = linkManager;
    }

    public void start() {
        if (!config.isTelegramEnabled()
                || config.getTelegramBotToken().isEmpty()
                || config.getTelegramBotToken().equals("YOUR_TELEGRAM_BOT_TOKEN")) {
            logger.warning("[ChatModule-Telegram] Bot-Token nicht konfiguriert. Telegram-Empfang deaktiviert.");
            return;
        }

        running = true;
        int interval = Math.max(2, config.getTelegramPollInterval());

        plugin.getProxy().getScheduler().schedule(plugin, this::pollUpdates,
                interval, interval, TimeUnit.SECONDS);

        logger.info("[ChatModule-Telegram] Br\u00fccke gestartet (Poll-Intervall: " + interval + "s).");
    }

    public void stop() {
        running = false;
    }

    // ===== Minecraft → Telegram =====

    /**
     * Sendet eine Nachricht an eine Telegram-Chat-ID.
     * Unterst\u00fctzt Themen-Gruppen via message_thread_id.
     */
    public void sendToTelegram(String chatId, String message) {
        sendToTelegram(chatId, 0, message);
    }

    public void sendToTelegram(String chatId, int threadId, String message) {
        if (chatId == null || chatId.isEmpty()) return;

        plugin.getProxy().getScheduler().runAsync(plugin, () -> {
            try {
                String cleanMessage = ChatColor.stripColor(
                        ChatColor.translateAlternateColorCodes('&', message));

                String url = API_BASE + config.getTelegramBotToken()
                        + "/sendMessage?chat_id=" + URLEncoder.encode(chatId, "UTF-8")
                        + "&text=" + URLEncoder.encode(cleanMessage, "UTF-8")
                        + "&parse_mode=HTML"
                        + (threadId > 0 ? "&message_thread_id=" + threadId : "");

                getJson(url);
            } catch (Exception e) {
                logger.warning("[ChatModule-Telegram] Sende-Fehler: " + e.getMessage());
            }
        });
    }

    /**
     * Sendet eine formatierte HelpOp/Broadcast-Nachricht an Telegram.
     * Unterst\u00fctzt Themen-Gruppen via message_thread_id.
     */
    public void sendFormattedToTelegram(String chatId, String header, String content) {
        sendFormattedToTelegram(chatId, 0, header, content);
    }

    public void sendFormattedToTelegram(String chatId, int threadId, String header, String content) {
        if (chatId == null || chatId.isEmpty()) return;
        String text = "<b>" + escapeHtml(header) + "</b>\n" + escapeHtml(content);

        plugin.getProxy().getScheduler().runAsync(plugin, () -> {
            try {
                String url = API_BASE + config.getTelegramBotToken()
                        + "/sendMessage?chat_id=" + URLEncoder.encode(chatId, "UTF-8")
                        + "&text=" + URLEncoder.encode(text, "UTF-8")
                        + "&parse_mode=HTML"
                        + (threadId > 0 ? "&message_thread_id=" + threadId : "");
                getJson(url);
            } catch (Exception e) {
                logger.warning("[ChatModule-Telegram] Format-Sende-Fehler: " + e.getMessage());
            }
        });
    }

    // ===== Telegram → Minecraft (Polling) =====

    private void pollUpdates() {
        if (!running) return;
        try {
            // Beim ersten Poll: nur den aktuellen Offset holen, keine alten Updates verarbeiten
            if (lastUpdateId.get() == 0L) {
                String initUrl = API_BASE + config.getTelegramBotToken()
                        + "/getUpdates?limit=1&offset=-1";
                String initResp = getJson(initUrl);
                if (initResp != null && initResp.contains("\"ok\":true")) {
                    java.util.List<TelegramUpdate> initUpdates = parseUpdates(initResp);
                    if (!initUpdates.isEmpty()) {
                        lastUpdateId.set(initUpdates.get(initUpdates.size() - 1).updateId);
                    }
                }
                return; // Erster Poll nur zum Initialisieren
            }

            long offset = lastUpdateId.get() + 1;
            String url = API_BASE + config.getTelegramBotToken()
                    + "/getUpdates?timeout=2&limit=10"
                    + (offset > 0 ? "&offset=" + offset : "");

            String response = getJson(url);
            if (response == null || !response.contains("\"ok\":true")) return;

            java.util.List<TelegramUpdate> updates = parseUpdates(response);

            for (TelegramUpdate update : updates) {
                if (update.updateId > lastUpdateId.get()) {
                    lastUpdateId.set(update.updateId);
                }
                if (update.text == null || update.text.isEmpty()) continue;
                if (update.isBot) continue;

                // ── Token-Einl\u00f6sung: /link <TOKEN> ──
                if (update.text.startsWith("/link ") || update.text.startsWith("/link@")) {
                    String[] parts = update.text.split("\\s+", 2);
                    if (parts.length == 2 && linkManager != null) {
                        String token = parts[1].trim().toUpperCase();
                        AccountLinkManager.LinkedAccount acc =
                                linkManager.redeemTelegram(token, update.fromId, update.fromName);
                        if (acc != null) {
                            sendToTelegram(update.chatId, update.threadId,
                                    "✅ Verkn\u00fcpfung erfolgreich! Minecraft-Account: <b>"
                                    + escapeHtml(acc.minecraftName) + "</b>");
                        } else {
                            sendToTelegram(update.chatId, update.threadId,
                                    "❌ Ung\u00fcltiger oder abgelaufener Token. Bitte /telegramlink im Spiel erneut ausf\u00fchren.");
                        }
                    }
                    continue; // Nicht als Chat-Nachricht weiterleiten
                }

                // Bot-Befehle ignorieren
                if (update.text.startsWith("/")) continue;

                // ── Account-Name aufl\u00f6sen ──
                String displayName = (linkManager != null)
                        ? linkManager.resolveTelegramName(update.fromId, update.fromName)
                        : update.fromName;

                // Welchem Minecraft-Kanal geh\u00f6rt diese Telegram-Chat-ID + Thread?
                final boolean isAdminChat = update.chatId.equals(config.getTelegramAdminChatId())
                        && (config.getTelegramAdminTopicId() == 0
                            || config.getTelegramAdminTopicId() == update.threadId);

                // Pr\u00fcfen ob die Nachricht zu einem konfigurierten Kanal-Thema geh\u00f6rt
                final boolean matchesChannel = isAdminChat || matchesTelegramChannel(update);

                if (!matchesChannel && !isAdminChat) continue;

                final String format = config.getTelegramFromFormat();
                final String finalDisplay = displayName;
                final String formatted = ChatColor.translateAlternateColorCodes('&',
                        format.replace("{user}", finalDisplay)
                              .replace("{message}", update.text));

                ProxyServer.getInstance().getScheduler().runAsync(plugin, () -> {
                    if (isAdminChat) {
                        for (net.md_5.bungee.api.connection.ProxiedPlayer p :
                                ProxyServer.getInstance().getPlayers()) {
                            if (p.hasPermission("chat.admin.bypass")) {
                                p.sendMessage(new TextComponent(formatted));
                            }
                        }
                    } else {
                        ProxyServer.getInstance().broadcast(new TextComponent(formatted));
                    }
                });
            }
        } catch (Exception e) {
            logger.fine("[ChatModule-Telegram] Poll-Fehler: " + e.getMessage());
        }
    }

    // ===== HTTP-Hilfsmethoden =====

    private String getJson(String urlStr) throws Exception {
        HttpURLConnection conn = (HttpURLConnection) new URL(urlStr).openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(6000);
        conn.setReadTimeout(10000);
        conn.setRequestProperty("User-Agent", "StatusAPI-ChatModule/1.0");
        int code = conn.getResponseCode();
        String result = readStream(code == 200 ? conn.getInputStream() : conn.getErrorStream());
        conn.disconnect();
        return result;
    }

    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 TelegramUpdate {
        long   updateId;
        String chatId   = "";
        String fromId   = "";   // Telegram User-ID (f\u00fcr Account-Link)
        String fromName = "";
        String text     = "";
        boolean isBot   = false;
        int    threadId = 0;    // message_thread_id f\u00fcr Themen-Gruppen (0 = kein Thema)
    }

    private java.util.List<TelegramUpdate> parseUpdates(String json) {
        java.util.List<TelegramUpdate> result = new java.util.ArrayList<>();
        // Suche nach "result":[...]
        int resultStart = json.indexOf("\"result\":[");
        if (resultStart < 0) return result;

        // Extrahiere alle Update-Objekte
        int depth = 0, start = -1;
        boolean inResult = false;
        for (int i = resultStart + 10; i < json.length(); i++) {
            char c = json.charAt(i);
            if (c == '[' && !inResult) { inResult = true; continue; }
            if (!inResult) continue;
            if (c == '{') { if (depth++ == 0) start = i; }
            else if (c == '}') {
                if (--depth == 0 && start >= 0) {
                    TelegramUpdate upd = parseUpdate(json.substring(start, i + 1));
                    if (upd != null) result.add(upd);
                    start = -1;
                }
            } else if (c == ']' && depth == 0) break;
        }
        return result;
    }

    /** Pr\u00fcft ob ein Update zu einem konfigurierten Kanal-Thema geh\u00f6rt. */
    private boolean matchesTelegramChannel(TelegramUpdate update) {
        for (net.viper.status.modules.chat.ChatChannel ch : config.getChannels().values()) {
            if (!ch.getTelegramChatId().equals(update.chatId)) continue;
            // Thema konfiguriert? → Thread-ID muss \u00fcbereinstimmen
            if (ch.getTelegramThreadId() > 0 && ch.getTelegramThreadId() != update.threadId) continue;
            return true;
        }
        return false;
    }

    private TelegramUpdate parseUpdate(String obj) {
        try {
            TelegramUpdate upd = new TelegramUpdate();
            upd.updateId = Long.parseLong(extractValue(obj, "update_id"));

            // message-Block
            int msgIdx = obj.indexOf("\"message\"");
            if (msgIdx < 0) return null;
            String msgBlock = extractObject(obj, msgIdx);

            upd.text = unescapeJson(extractString(msgBlock, "text"));

            // message_thread_id (Themen-Gruppen)
            String threadIdStr = extractValue(msgBlock, "message_thread_id");
            if (!threadIdStr.isEmpty()) {
                try { upd.threadId = Integer.parseInt(threadIdStr); } catch (Exception ignored) {}
            }

            // from-Block (Absender)
            int fromIdx = msgBlock.indexOf("\"from\"");
            if (fromIdx >= 0) {
                String fromBlock = extractObject(msgBlock, fromIdx);
                String firstName = unescapeJson(extractString(fromBlock, "first_name"));
                String lastName  = unescapeJson(extractString(fromBlock, "last_name"));
                String username  = unescapeJson(extractString(fromBlock, "username"));
                upd.fromId   = extractValue(fromBlock, "id");
                upd.fromName = !username.isEmpty() ? "@" + username
                             : (firstName + (lastName.isEmpty() ? "" : " " + lastName)).trim();
                String botFlag = extractValue(fromBlock, "is_bot");
                upd.isBot = "true".equals(botFlag);
            }

            // chat-Block (Chat-ID)
            int chatIdx = msgBlock.indexOf("\"chat\"");
            if (chatIdx >= 0) {
                String chatBlock = extractObject(msgBlock, chatIdx);
                upd.chatId = extractValue(chatBlock, "id");
            }

            return upd;
        } catch (Exception e) {
            return null;
        }
    }

    private String extractValue(String json, String key) {
        String fullKey = "\"" + key + "\"";
        int idx = json.indexOf(fullKey);
        if (idx < 0) return "";
        int colon = json.indexOf(':', idx + 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 == '"') {
            return extractString(json.substring(valStart - 1 - key.length()), key);
        }
        int end = valStart;
        while (end < json.length() && ",}\n".indexOf(json.charAt(end)) < 0) end++;
        return json.substring(valStart, end).trim();
    }

    private String extractString(String json, String key) {
        String fullKey = "\"" + key + "\":\"";
        int idx = json.indexOf(fullKey);
        if (idx < 0) return "";
        int start = idx + fullKey.length();
        int end = start;
        while (end < json.length()) {
            if (json.charAt(end) == '"' && json.charAt(end - 1) != '\\') break;
            end++;
        }
        return json.substring(start, end);
    }

    private String extractObject(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 unescapeJson(String s) {
        if (s == null) return "";
        return s.replace("\\\"", "\"").replace("\\n", "\n")
                .replace("\\r", "\r").replace("\\\\", "\\");
    }

    private static String escapeHtml(String s) {
        if (s == null) return "";
        return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
    }
}