package net.viper.status.modules.chat;

import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

/**
 * Verwaltet die Verkn\u00fcpfung von Minecraft-Accounts mit Discord/Telegram.
 *
 * Ablauf:
 *   1. Spieler tippt /linkdiscord oder /linktelegram → Token wird generiert
 *   2. Spieler schickt Token an den Bot
 *   3. Bot-Polling erkennt Token → Verkn\u00fcpfung wird gespeichert
 *
 * Speicherformat (chat_links.dat):
 *   minecraft:<uuid>|name:<spielername>|discord:<discord-user-id>|telegram:<telegram-user-id>
 */
public class AccountLinkManager {

    private final File file;
    private final Logger logger;

    // UUID → verkn\u00fcpfte Accounts
    private final ConcurrentHashMap<UUID, LinkedAccount> links = new ConcurrentHashMap<>();

    // Ausstehende Token: token → UUID (l\u00e4uft nach 10 Min ab)
    private final ConcurrentHashMap<String, PendingToken> pendingTokens = new ConcurrentHashMap<>();

    public AccountLinkManager(File dataFolder, Logger logger) {
        this.file   = new File(dataFolder, "chat_links.dat");
        this.logger = logger;
    }

    // ===== Datenklassen =====

    public static class LinkedAccount {
        public UUID   minecraftUUID;
        public String minecraftName;
        public String discordUserId   = "";  // leer = nicht verkn\u00fcpft
        public String telegramUserId  = "";  // leer = nicht verkn\u00fcpft
        public String telegramUsername = ""; // @username f\u00fcr Anzeige
        public String discordUsername  = ""; // f\u00fcr Anzeige
    }

    private static class PendingToken {
        UUID   uuid;
        String playerName;
        String type;       // "discord" oder "telegram"
        long   expiresAt;  // Unix-Millis

        PendingToken(UUID uuid, String playerName, String type) {
            this.uuid       = uuid;
            this.playerName = playerName;
            this.type       = type;
            this.expiresAt  = System.currentTimeMillis() + (10 * 60 * 1000L); // 10 Min
        }

        boolean isExpired() {
            return System.currentTimeMillis() > expiresAt;
        }
    }

    // ===== Token-Generierung =====

    /**
     * Generiert einen neuen Verkn\u00fcpfungs-Token f\u00fcr einen Spieler.
     * Bestehende Token f\u00fcr denselben Spieler+Typ werden \u00fcberschrieben.
     *
     * @param uuid       UUID des Spielers
     * @param playerName Anzeigename
     * @param type       "discord" oder "telegram"
     * @return 6-stelliger alphanumerischer Token (z.B. "A3F9K2")
     */
    public String generateToken(UUID uuid, String playerName, String type) {
        // Alte Token f\u00fcr diesen Spieler+Typ entfernen
        pendingTokens.entrySet().removeIf(e ->
                e.getValue().uuid.equals(uuid) && e.getValue().type.equals(type));

        // Abgelaufene Token bereinigen
        pendingTokens.entrySet().removeIf(e -> e.getValue().isExpired());

        String token;
        do {
            token = generateRandomToken();
        } while (pendingTokens.containsKey(token));

        pendingTokens.put(token, new PendingToken(uuid, playerName, type));
        return token;
    }

    private String generateRandomToken() {
        String chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // ohne I,O,0,1 (Verwechslungsgefahr)
        Random rnd = new Random();
        StringBuilder sb = new StringBuilder(6);
        for (int i = 0; i < 6; i++) sb.append(chars.charAt(rnd.nextInt(chars.length())));
        return sb.toString();
    }

    // ===== Token einl\u00f6sen =====

    /**
     * Versucht einen Token einzul\u00f6sen (aufgerufen wenn Bot eine Nachricht empf\u00e4ngt).
     *
     * @param token          Der eingesendete Token
     * @param externalId     Discord User-ID oder Telegram User-ID (als String)
     * @param externalName   Discord-Username oder Telegram-@username
     * @return LinkedAccount wenn erfolgreich, null wenn Token ung\u00fcltig/abgelaufen
     */
    public LinkedAccount redeemToken(String token, String externalId, String externalName, String type) {
        token = token.trim().toUpperCase();
        PendingToken pending = pendingTokens.get(token);

        if (pending == null || pending.isExpired()) {
            pendingTokens.remove(token);
            return null;
        }
        // Typ muss \u00fcbereinstimmen
        if (!pending.type.equals(type)) return null;

        pendingTokens.remove(token);

        // Bestehenden Account holen oder neu anlegen
        LinkedAccount account = links.computeIfAbsent(pending.uuid, k -> {
            LinkedAccount a = new LinkedAccount();
            a.minecraftUUID = pending.uuid;
            a.minecraftName = pending.playerName;
            return a;
        });
        account.minecraftName = pending.playerName; // aktuell halten

        if ("discord".equals(pending.type)) {
            account.discordUserId   = externalId;
            account.discordUsername = externalName;
        } else if ("telegram".equals(pending.type)) {
            account.telegramUserId   = externalId;
            account.telegramUsername = externalName;
        }

        save();
        return account;
    }

    // ===== Lookup =====

    public LinkedAccount getByUUID(UUID uuid) {
        return links.get(uuid);
    }

    public LinkedAccount getByDiscordId(String discordUserId) {
        for (LinkedAccount a : links.values()) {
            if (discordUserId.equals(a.discordUserId)) return a;
        }
        return null;
    }

    public LinkedAccount getByTelegramId(String telegramUserId) {
        for (LinkedAccount a : links.values()) {
            if (telegramUserId.equals(a.telegramUserId)) return a;
        }
        return null;
    }

    /** Gibt den Minecraft-Namen f\u00fcr eine Discord-User-ID zur\u00fcck, oder null. */
    public String getMinecraftNameByDiscordId(String discordUserId) {
        LinkedAccount a = getByDiscordId(discordUserId);
        return a != null ? a.minecraftName : null;
    }

    /** Gibt den Minecraft-Namen f\u00fcr eine Telegram-User-ID zur\u00fcck, oder null. */
    public String getMinecraftNameByTelegramId(String telegramUserId) {
        LinkedAccount a = getByTelegramId(telegramUserId);
        return a != null ? a.minecraftName : null;
    }

    /** Pr\u00fcft ob ein Token gerade aussteht (f\u00fcr Tab-Complete etc.). */
    public boolean hasPendingToken(UUID uuid, String type) {
        for (PendingToken t : pendingTokens.values()) {
            if (t.uuid.equals(uuid) && t.type.equals(type) && !t.isExpired()) return true;
        }
        return false;
    }

    // ===== Verkn\u00fcpfung aufheben =====

    public boolean unlinkDiscord(UUID uuid) {
        LinkedAccount a = links.get(uuid);
        if (a == null || a.discordUserId.isEmpty()) return false;
        a.discordUserId   = "";
        a.discordUsername = "";
        cleanupEmpty(uuid);
        save();
        return true;
    }

    public boolean unlinkTelegram(UUID uuid) {
        LinkedAccount a = links.get(uuid);
        if (a == null || a.telegramUserId.isEmpty()) return false;
        a.telegramUserId   = "";
        a.telegramUsername = "";
        cleanupEmpty(uuid);
        save();
        return true;
    }

    private void cleanupEmpty(UUID uuid) {
        LinkedAccount a = links.get(uuid);
        if (a != null && a.discordUserId.isEmpty() && a.telegramUserId.isEmpty()) {
            links.remove(uuid);
        }
    }

    // ===== Convenience-Methoden f\u00fcr Bridges =====

    /**
     * L\u00f6st einen Telegram-Token ein.
     * Wrapper f\u00fcr redeemToken mit type="telegram".
     */
    public LinkedAccount redeemTelegram(String token, String telegramUserId, String telegramUsername) {
        return redeemToken(token, telegramUserId, telegramUsername, "telegram");
    }

    /**
     * L\u00f6st einen Discord-Token ein.
     * Wrapper f\u00fcr redeemToken mit type="discord".
     */
    public LinkedAccount redeemDiscord(String token, String discordUserId, String discordUsername) {
        return redeemToken(token, discordUserId, discordUsername, "discord");
    }

    /**
     * Gibt den Anzeigenamen f\u00fcr einen Telegram-Nutzer zur\u00fcck.
     * Wenn verkn\u00fcpft: "MinecraftName (@telegram)", sonst: "@telegram"
     */
    public String resolveTelegramName(String telegramUserId, String fallbackName) {
        String mc = getMinecraftNameByTelegramId(telegramUserId);
        return mc != null ? mc : fallbackName;
    }

    /**
     * Gibt den Anzeigenamen f\u00fcr einen Discord-Nutzer zur\u00fcck.
     * Wenn verkn\u00fcpft: Minecraft-Name, sonst: Discord-Username
     */
    public String resolveDiscordName(String discordUserId, String fallbackName) {
        String mc = getMinecraftNameByDiscordId(discordUserId);
        return mc != null ? mc : fallbackName;
    }

    /**
     * Gibt das korrekte Format-String zur\u00fcck abh\u00e4ngig ob Account verkn\u00fcpft.
     * linked=true → linkedFormat (mit {player}), false → unlinkedFormat (mit {user})
     */
    public boolean isLinkedTelegram(String telegramUserId) {
        return getByTelegramId(telegramUserId) != null;
    }

    public boolean isLinkedDiscord(String discordUserId) {
        return getByDiscordId(discordUserId) != null;
    }

    // ===== Persistenz =====

    public void save() {
        try (BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) {
            for (LinkedAccount a : links.values()) {
                bw.write(a.minecraftUUID
                        + "|" + esc(a.minecraftName)
                        + "|" + esc(a.discordUserId)
                        + "|" + esc(a.discordUsername)
                        + "|" + esc(a.telegramUserId)
                        + "|" + esc(a.telegramUsername));
                bw.newLine();
            }
        } catch (IOException e) {
            logger.warning("[ChatModule] Fehler beim Speichern der Account-Links: " + e.getMessage());
        }
    }

    public void load() {
        links.clear();
        if (!file.exists()) return;
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
            String line;
            while ((line = br.readLine()) != null) {
                line = line.trim();
                if (line.isEmpty()) continue;
                String[] p = line.split("\\|", -1);
                if (p.length < 6) continue;
                try {
                    LinkedAccount a      = new LinkedAccount();
                    a.minecraftUUID      = UUID.fromString(p[0]);
                    a.minecraftName      = unesc(p[1]);
                    a.discordUserId      = unesc(p[2]);
                    a.discordUsername    = unesc(p[3]);
                    a.telegramUserId     = unesc(p[4]);
                    a.telegramUsername   = unesc(p[5]);
                    if (!a.discordUserId.isEmpty() || !a.telegramUserId.isEmpty()) {
                        links.put(a.minecraftUUID, a);
                    }
                } catch (Exception ignored) {}
            }
        } catch (IOException e) {
            logger.warning("[ChatModule] Fehler beim Laden der Account-Links: " + e.getMessage());
        }
    }

    private static String esc(String s) {
        if (s == null) return "";
        return s.replace("\\", "\\\\").replace("|", "\\p");
    }

    private static String unesc(String s) {
        if (s == null) return "";
        return s.replace("\\p", "|").replace("\\\\", "\\");
    }
}