-- ══════════════════════════════════════════════════════════════
-- SLASH COMMANDS - Dispatch-Table basiert
-- Jeder Command ist eine Funktion in der Table.
-- Neue Commands: einfach eintragen, fertig.
-- ══════════════════════════════════════════════════════════════

local addonName, addon = ...
local GDL = _G["GuildDeathLog"]

-- ── COMMAND REGISTRY ──────────────────────────────────────────
-- Format: [command] = { fn = function(msg), aliases = {"alias1", "alias2"} }
-- Oder einfach: [command] = function(msg)
local commands = {}
local aliases  = {}

local function RegisterCmd(name, fn, aliasList)
    commands[name] = fn
    if aliasList then
        for _, alias in ipairs(aliasList) do
            aliases[alias] = name
        end
    end
end

-- ── HELPER ────────────────────────────────────────────────────
local function GetModule(name)
    return GDL:GetModule(name)
end

-- ══════════════════════════════════════════════════════════════
-- COMMAND DEFINITIONEN
-- ══════════════════════════════════════════════════════════════

-- Buch oeffnen (Default)
RegisterCmd("show", function()
    local UI = GetModule("UI")
    if UI then UI:ShowBook() end
end)

RegisterCmd("hide", function()
    local UI = GetModule("UI")
    if UI then UI:HideBook() end
end)

RegisterCmd("settings", function()
    local UI = GetModule("UI")
    if UI then UI:ShowSettings() end
end, {"config"})

RegisterCmd("news", function()
    local WN = GetModule("WhatsNew")
    if WN then WN:Toggle() end
end, {"whatsnew", "changelog", "wn"})

-- Sprache umschalten (Override). auto = Client-Sprache.
RegisterCmd("lang", function(msg)
    local arg = msg and msg:match("^%S+%s+(%S+)")
    arg = arg and arg:lower() or nil
    GuildDeathLogDB = GuildDeathLogDB or {}
    if arg == "en" or arg == "english" or arg == "enus" then
        GuildDeathLogDB.forcedLocale = "enUS"
        GDL:Print("|cff00FF00Language: English|r  (/reload recommended for full effect)")
    elseif arg == "de" or arg == "deutsch" or arg == "dede" then
        GuildDeathLogDB.forcedLocale = "deDE"
        GDL:Print("|cff00FF00Sprache: Deutsch|r  (/reload empfohlen)")
    elseif arg == "auto" then
        GuildDeathLogDB.forcedLocale = nil
        GDL:Print("Language = client default: |cffFFD100" .. GetLocale() .. "|r  (/reload)")
    else
        local cur = GuildDeathLogDB.forcedLocale or ("auto (" .. GetLocale() .. ")")
        GDL:Print("Usage: /gdl lang en | de | auto   (current: |cffFFD100" .. cur .. "|r)")
        return
    end
    local Locale = GetModule("Locale")
    if Locale and Locale.Initialize then Locale:Initialize() end
    local Mile = GetModule("Milestones")
    if Mile and Mile.ApplyLocale then Mile:ApplyLocale() end
    local Titl = GetModule("Titles")
    if Titl and Titl.ApplyLocale then Titl:ApplyLocale() end
    local UI = GetModule("UI")
    if UI then
        if UI.InvalidateChapters then UI:InvalidateChapters() end
        if UI.mainFrame and UI.mainFrame:IsShown() and UI.UpdateChronicle then UI:UpdateChronicle() end
    end
end, {"language", "sprache"})

-- Community: Erzfeind der Gilde anzeigen
RegisterCmd("nemesis", function()
    local N = GetModule("Nemesis")
    if N and N.Print then N:Print() end
end, {"erzfeind"})

-- Community: Geluebde ablegen / erfuellen / aufheben
RegisterCmd("vow", function(msg)
    local Vows = GetModule("Vows")
    if not Vows then return end
    local rest = msg and msg:match("^%S+%s+(.+)")
    rest = rest and rest:gsub("%s+$", "") or nil
    if not rest then Vows:SetVow(nil); return end
    local low = rest:lower()
    if low == "done" or low == "erfuellt" or low == "erfüllt" then Vows:FulfillVow()
    elseif low == "clear" or low == "cancel" or low == "abbrechen" then Vows:ClearVow()
    else Vows:SetVow(rest) end
end, {"schwur"})

-- Community: alle Gilden-Geluebde auflisten
RegisterCmd("vows", function()
    local Vows = GetModule("Vows")
    if Vows and Vows.ListVows then Vows:ListVows() end
end, {"schwuere", "geluebde"})

-- Community: Tage ohne Verlust (Gilden-Sicherheits-Serie)
RegisterCmd("safedays", function()
    local SD = GetModule("SafeDays")
    if SD and SD.Print then SD:Print() end
end, {"safe", "tage", "streak"})

RegisterCmd("sync", function()
    local Sync = GetModule("Sync")
    if Sync then Sync:RequestFullSync() end
end)

-- ── ROSTER / ALTS ─────────────────────────────────────────────
RegisterCmd("roster", function()
    local Roster = GetModule("Roster")
    if Roster then Roster:ListAlts(); Roster:PrintHelp() end
end)

-- ── SUPPORTER ─────────────────────────────────────────────────
RegisterCmd("supporter", function()
    if GDL:IsSupporterActive() then
        local untilStr = GDL:GetSupporterExpiry() or "?"
        print("|cffffd200[GDL]|r Supporter: |cff00ff00AKTIV|r bis " .. untilStr)
    else
        print("|cffffd200[GDL]|r Supporter: |cffff4040inaktiv|r. Redeem mit: /gdl redeem GDL-YYYYMM-TTTTTTTT-CCCCCC")
    end
end)

-- ── DEBUG / DIAGNOSTIK ────────────────────────────────────────
-- Trauer-Nachricht bei Gildentod konfigurieren
RegisterCmd("trauer", function(msg)
    local st = GuildDeathLogDB.settings
    local arg = msg and msg:match("^%S+%s+(.+)$")  -- alles nach "trauer "
    if not arg or arg == "" then
        GDL:Print("Trauer-Nachricht: " .. (st.mournEnabled and "|cff1A5A24AN|r" or "|cff7A2C14AUS|r"))
        GDL:Print("Text: |cffDDB060" .. (st.mournMessage or "Ruhe in Frieden, {name}.") .. "|r")
        GDL:Print("Nutzung: /gdl trauer an|aus  oder  /gdl trauer <Text mit {name}>")
        return
    end
    local low = arg:lower()
    if low == "an" or low == "on" then
        st.mournEnabled = true
        GDL:Print("Trauer-Nachricht |cff1A5A24aktiviert|r.")
    elseif low == "aus" or low == "off" then
        st.mournEnabled = false
        GDL:Print("Trauer-Nachricht |cff7A2C14deaktiviert|r.")
    else
        st.mournMessage = arg
        st.mournEnabled = true
        GDL:Print("Trauer-Text gesetzt (+aktiviert): |cffDDB060" .. arg .. "|r")
        if not arg:find("{name}") then
            GDL:Print("|cff8A5E14Hinweis:|r {name} fehlt im Text - der Name des Gefallenen wird nicht eingesetzt.")
        end
    end
end, {"mourn"})

RegisterCmd("debug", function()
    local Debug = GetModule("Debug")
    if Debug then Debug:ShowWindow() end
end)

RegisterCmd("scan", function()
    local Deathlog = GetModule("Deathlog")
    if Deathlog then
        Deathlog:DetectDeathlog()
        Deathlog:ScanData()
        GDL:Print("Deathlog-Scan durchgefuehrt!")
    end
end)

RegisterCmd("dlcheck", function()
    local Deathlog = GetModule("Deathlog")
    if Deathlog then
        Deathlog:DebugGlobals()
    else
        GDL:Print("|cffFF0000Deathlog-Modul nicht geladen!|r")
    end
end, {"deathlogcheck", "dlstatus"})

-- ── KAPITEL / VIEWS ───────────────────────────────────────────
RegisterCmd("hof", function()
    local UI = GetModule("UI")
    if UI then UI:ShowHallOfFame() end
end, {"halloffame", "ruhmeshalle"})

RegisterCmd("stats", function()
    local UI = GetModule("UI")
    if UI then UI:ShowStatistics() end
end, {"statistics", "statistiken"})

RegisterCmd("ach", function()
    local UI = GetModule("UI")
    if UI then UI:ShowAchievements() end
end, {"achievements", "erfolge", "milestones", "meilensteine"})

RegisterCmd("export", function()
    local Export = GetModule("Export")
    if Export then Export:ShowExportWindow() end
end)

RegisterCmd("report", function()
    local Report = GetModule("Report")
    if Report then Report:GenerateWeeklyReport() end
end, {"bericht"})

RegisterCmd("cal", function()
    local Calendar = GetModule("Calendar")
    if Calendar then
        local UI2 = GDL:GetModule("UI")
        if UI2 then
            UI2:ShowBook()
            C_Timer.After(0.45, function() UI2:RenderChapter("calendar") end)
        end
    else
        GDL:Print("|cffFF0000Calendar-Modul nicht geladen!|r")
    end
end, {"calendar", "kalender"})

-- ── GUILD LEADER (geheimer Command) ──────────────────────────
RegisterCmd("leader", function()
    local GL = GetModule("GuildLeader")
    if GL then GL:Toggle() end
end, {"leitung", "gl"})

-- ── TEST COMMANDS ─────────────────────────────────────────────
RegisterCmd("test", function()
    local Sync = GetModule("Sync")
    if Sync then Sync:SendTestDeath() end
end)

-- Solo-Synctest: simuliert einen EMPFANGENEN Peer-Tod (ohne zweiten Client).
-- "/gdl synctest"        -> Fake-Peer + Fake-Tod durch den Empfangs-Pfad
-- "/gdl synctest clear"  -> Fake-Peer + Test-Tode wieder entfernen
RegisterCmd("synctest", function(msg)
    local Sync = GetModule("Sync")
    if not Sync then GDL:Print("|cffFF0000Sync-Modul nicht geladen!|r"); return end
    local arg = msg and msg:match("^%S+%s+(%S+)")
    if arg and arg:lower() == "clear" then
        if Sync.ClearSelfTest then Sync:ClearSelfTest() end
    elseif Sync.RunSelfTest then
        Sync:RunSelfTest()
    end
end, {"selftest"})

-- Diagnose: loest die ECHTE Todes-Pipeline aus (HandleDeath -> ... -> notify),
-- damit /gdl log zeigt, wo Popup/Announce ggf. verloren gehen.
RegisterCmd("pipetest", function()
    local DB = GetModule("DNLBridge")
    if DB and DB.SendTestDeath then
        GDL:Print("|cffFFFF00[PIPETEST]|r Echter Pipeline-Tod ausgeloest. Danach: /gdl log")
        DB:SendTestDeath()
    else
        GDL:Print("|cffFF0000DNLBridge-Modul nicht geladen!|r")
    end
end, {"ptest", "deathtest"})

RegisterCmd("mtest", function()
    local Milestones = GetModule("Milestones")
    if Milestones then
        Milestones:TestLevelMilestones()
    else
        GDL:Print("|cffFF0000Milestones-Modul nicht geladen!|r")
    end
end, {"milestonetest"})

RegisterCmd("mforce", function()
    local Milestones = GetModule("Milestones")
    if Milestones then
        Milestones:ForceUnlockCurrentLevel()
    else
        GDL:Print("|cffFF0000Milestones-Modul nicht geladen!|r")
    end
end, {"milestoneforce"})

RegisterCmd("mcheck", function()
    local Milestones = GetModule("Milestones")
    if Milestones then
        Milestones:CheckCurrentLevel()
    else
        GDL:Print("|cffFF0000Milestones-Modul nicht geladen!|r")
    end
end, {"milestonecheck"})

-- ── PASSWORT ──────────────────────────────────────────────────
RegisterCmd("haspw", function()
    if GuildDeathLogDB.adminPassword then
        GDL:Print("|cff00FF00Passwort ist gesetzt|r (" .. #GuildDeathLogDB.adminPassword .. " Zeichen)")
        GDL:Print("Loesch-Buttons sollten sichtbar sein (X rechts neben Eintraegen)")
    else
        GDL:Print("|cffFFFF00Kein Passwort gesetzt.|r Verwende /gdl setpw <passwort>")
    end
end)

RegisterCmd("clearpw", function()
    if IsGuildLeader() or GDL:IsGuildOfficer() then
        GuildDeathLogDB.adminPassword = nil
        GDL:Print("|cffFFFF00Admin-Passwort entfernt.|r")
    else
        GDL:Print("|cffFF0000Fehler:|r Nur Gildenleiter oder Offiziere!")
    end
end)

-- ── MAP / COORDS ──────────────────────────────────────────────
RegisterCmd("coords", function()
    local MapMarkers = GetModule("MapMarkers")
    if MapMarkers then
        MapMarkers:PrintCoordDebug()
    else
        GDL:Print("|cffFF0000MapMarkers-Modul nicht geladen!|r")
    end
end, {"markers"})

-- ── KILLS ─────────────────────────────────────────────────────
RegisterCmd("kills", function()
    local KillStats = GetModule("KillStats")
    if KillStats then
        KillStats:PrintStats()
    else
        GDL:Print("|cffFF0000KillStats-Modul nicht geladen!|r")
    end
end, {"killstats"})

RegisterCmd("killcheck", function()
    local KillStats = GetModule("KillStats")
    if KillStats then
        KillStats:RecheckAllMilestones()
    else
        GDL:Print("|cffFF0000KillStats-Modul nicht geladen!|r")
    end
end, {"recheckills"})

-- ── INSIGHTS (Mehrwert) ───────────────────────────────────────
RegisterCmd("threats", function()
    local I = GetModule("Insights")
    if I then I:PrintThreats() else GDL:Print("|cffFF0000Insights-Modul nicht geladen!|r") end
end, {"gefahren", "danger", "fear"})

RegisterCmd("away", function()
    local I = GetModule("Insights")
    if I then I:ShowWelcomeBack(true) else GDL:Print("|cffFF0000Insights-Modul nicht geladen!|r") end
end, {"digest", "weg", "wiederda"})

-- ── GILDEN-WETTBEWERB ─────────────────────────────────────────
RegisterCmd("guildcomp", function()
    local GC = GetModule("GuildCompetition")
    if GC then
        GC:PrintStats()
    else
        GDL:Print("|cffFF0000GuildCompetition-Modul nicht geladen!|r")
    end
end, {"gc", "wettbewerb", "competition", "leaderboard"})

-- ── TITEL ─────────────────────────────────────────────────────
RegisterCmd("titles", function()
    local UI = GetModule("UI")
    if UI then UI:ToggleTitles() end
end, {"titel"})

RegisterCmd("nptitles", function()
    if not GDL:RequireSupporter() then return end
    local Titles = GetModule("Titles")
    if Titles then Titles:ToggleNameplateTitles() end
end, {"nptitel", "nameplatetitles"})

RegisterCmd("mytitles", function()
    if not GDL:RequireSupporter() then return end
    local Titles = GetModule("Titles")
    if Titles then
        Titles:PrintTitles()
    else
        GDL:Print("|cffFF0000Titles-Modul nicht geladen!|r")
    end
end, {"meinetitel"})

RegisterCmd("titlecheck", function()
    if not GDL:RequireSupporter() then return end
    local Titles = GetModule("Titles")
    if Titles then
        local selected = Titles:GetSelectedTitle()
        local isFemale = Titles:IsPlayerFemale()
        GDL:Print("=== Titel-Debug ===")
        GDL:Print("Geschlecht: " .. (isFemale and "Weiblich" or "Maennlich"))
        if selected then
            local titleText = Titles:GetTitleText(selected, isFemale)
            GDL:Print("Aktiver Titel: |cff00FF00" .. titleText .. "|r")
            GDL:Print("Titel-ID: " .. selected.id)
        else
            GDL:Print("Aktiver Titel: |cffFF0000Kein Titel freigeschaltet|r")
            GDL:Print("Erreiche Meilensteine um Titel freizuschalten!")
            GDL:Print("Nutze /gdl ach um Meilensteine zu sehen")
        end
    end
end, {"titelcheck"})

RegisterCmd("titlesync", function()
    if not GDL:RequireSupporter() then return end
    local Titles = GetModule("Titles")
    if Titles then
        Titles:RequestAllTitles()
        GDL:Print("|cffFFD100Titel werden angefordert...|r")
    end
end, {"titelsync"})

-- ── SYNC / PUSH ───────────────────────────────────────────────
RegisterCmd("push", function()
    local Sync = GetModule("Sync")
    if Sync then
        Sync:BroadcastRecentDeaths(30)
        GDL:Print("|cff00AAFF[Sync]|r Pushe Tode an alle Gildenmitglieder...")
    end
end)

-- ── GUILD TRACKER ─────────────────────────────────────────────
RegisterCmd("track", function()
    local GuildTracker = GetModule("GuildTracker")
    if GuildTracker then
        GuildTracker:Toggle()
    else
        GDL:Print("|cffFF0000GuildTracker-Modul nicht geladen!|r")
    end
end, {"tracker", "map"})

RegisterCmd("trackstatus", function()
    local GuildTracker = GetModule("GuildTracker")
    if GuildTracker then
        GuildTracker:PrintStatus()
    else
        GDL:Print("|cffFF0000GuildTracker-Modul nicht geladen!|r")
    end
end, {"mapstatus", "trackerstatus"})

RegisterCmd("who", function()
    local GuildTracker = GetModule("GuildTracker")
    if GuildTracker then
        local players = GuildTracker:GetOnlinePlayers()
        if #players == 0 then
            GDL:Print("|cffAAAAFFKeine Gildenmitglieder mit Addon auf der Karte.|r")
        else
            GDL:Print("|cff00AAFF" .. #players .. " Gildenmitglieder mit Addon:|r")
            for _, p in ipairs(players) do
                local lvl = p.level and p.level > 0 and (" Lv" .. p.level) or ""
                local status = p.lastSeen < 5 and "|cff00FF00LIVE|r" or ("|cffFFFF00" .. p.lastSeen .. "s|r")
                GDL:Print("  - " .. p.name .. lvl .. " (" .. status .. ")")
            end
        end
    end
end, {"online"})

RegisterCmd("users", function()
    local GuildTracker = GetModule("GuildTracker")
    if GuildTracker then
        GuildTracker:PrintStatus()
    else
        GDL:Print("|cffFF0000GuildTracker-Modul nicht geladen!|r")
    end
end, {"nutzer", "count"})

-- ── BERUFE ────────────────────────────────────────────────────
RegisterCmd("prof", function()
    local Professions = GetModule("Professions")
    if Professions then
        Professions:ShowWindow()
    else
        GDL:Print("|cffFF0000Professions-Modul nicht geladen!|r")
    end
end, {"professions", "berufe"})

-- ── MEMORIAL ──────────────────────────────────────────────────
RegisterCmd("memorial", function()
    local Memorial = GetModule("Memorial")
    if Memorial then
        Memorial:ShowMemorialWindow()
    else
        GDL:Print("|cffFF0000Memorial-Modul nicht geladen!|r")
    end
end, {"gedenken", "rip"})

-- ── GILDENREGELN ──────────────────────────────────────────────
RegisterCmd("rules", function()
    local GuildRules = GetModule("GuildRules")
    if GuildRules then
        GuildRules:RequestRules()
        GuildRules:ShowWindow()
    else
        GDL:Print("|cffFF0000GuildRules-Modul nicht geladen!|r")
    end
end, {"regeln", "gildenregeln"})

RegisterCmd("rulessync", function()
    local GuildRules = GetModule("GuildRules")
    if GuildRules then
        GuildRules:RequestRules()
        GDL:Print("|cffFFD100Gildenregeln werden angefordert...|r")
    end
end, {"regelsync"})

-- ── GILDENSTATS ───────────────────────────────────────────────
RegisterCmd("guildstats", function()
    local GuildStats = GetModule("GuildStats")
    if GuildStats then
        GuildStats:ShowStatsWindow()
    else
        GDL:Print("|cffFF0000GuildStats-Modul nicht geladen!|r")
    end
end, {"gstats", "gildenstat"})

-- ── DANGER ZONE ───────────────────────────────────────────────
RegisterCmd("dz", function()
    local DZ = GetModule("DangerZone")
    if DZ then
        DZ:Toggle()
    else
        GDL:Print("|cffFF0000DangerZone-Modul nicht geladen!|r")
    end
end, {"dangerzone", "gefahrenzone"})

RegisterCmd("dztest", function()
    local DZ = GetModule("DangerZone")
    if DZ then
        DZ:Reset()
        DZ:CheckCurrentZone()
    end
end)

-- ── KONDOLENZ TEST ────────────────────────────────────────────
RegisterCmd("msgtest", function()
    local Condolences = GetModule("Condolences")
    if Condolences then
        Condolences:TestMessages(10)
    else
        GDL:Print("|cffFF0000Condolences-Modul nicht geladen!|r")
    end
end, {"condtest"})

RegisterCmd("msgstats", function()
    local Condolences = GetModule("Condolences")
    if Condolences then
        Condolences:PrintStats()
    else
        GDL:Print("|cffFF0000Condolences-Modul nicht geladen!|r")
    end
end, {"condstats"})

-- ── BLACKLIST ─────────────────────────────────────────────────
RegisterCmd("blacklist", function()
    local Sync = GetModule("Sync")
    if Sync then
        local blacklist = Sync:GetBlacklist()
        local count = 0
        GDL:Print("|cffFFCC00=== Geloeschte Eintraege (Blacklist) ===|r")
        for key, timestamp in pairs(blacklist) do
            count = count + 1
            local name = key:match("^([^:]+)") or key
            local ago = math.floor((time() - timestamp) / 3600)
            GDL:Print(count .. ". |cffFF6666" .. name .. "|r (vor " .. ago .. " Std geloescht)")
        end
        if count == 0 then
            GDL:Print("|cff888888Keine geloeschten Eintraege.|r")
        else
            GDL:Print("|cffAAAAFFUm Blacklist zu leeren: /gdl clearblacklist|r")
        end
    end
end, {"bl"})

RegisterCmd("clearblacklist", function()
    if IsGuildLeader() or GDL:IsGuildOfficer() then
        local guildData = GDL:GetGuildData()
        if guildData then
            local count = 0
            for _ in pairs(guildData.deletedDeaths or {}) do count = count + 1 end
            guildData.deletedDeaths = {}
            GDL:Print("|cff00FF00Blacklist geleert!|r " .. count .. " Eintraege entfernt.")
            GDL:Print("|cffFFAAAAGeloeschte Tode koennen jetzt wieder gesynct werden.|r")
        end
    else
        GDL:Print("|cffFF0000Fehler:|r Nur Gildenleiter oder Offiziere!")
    end
end, {"clearbl"})

-- ── PLAYED TIME ──────────────────────────────────────────────
RegisterCmd("played", function()
    local PT = GetModule("PlayedTime")
    if PT then PT:PrintStatus()
    else GDL:Print("|cffFF4444PlayedTime-Modul nicht verfuegbar.|r") end
end, {"time", "playtime"})

-- ── SURVIVAL STREAK ───────────────────────────────────────────
RegisterCmd("streak", function()
    local SS = GetModule("SurvivalStreak")
    if SS then SS:PrintStatus()
    else GDL:Print("|cffFF4444SurvivalStreak-Modul nicht verfuegbar.|r") end
end, {"survival", "alive"})

-- ── HEATMAP ───────────────────────────────────────────────────
RegisterCmd("heatmap", function()
    local DH = GetModule("DeathHeatmap")
    if DH then DH:Toggle()
    else GDL:Print("|cffFF4444DeathHeatmap-Modul nicht verfuegbar.|r") end
end, {"deathmap"})

-- ── SCREENSHOT ON DEATH ───────────────────────────────────────
RegisterCmd("screenshot", function()
    local db = GuildDeathLogDB
    if not (db and db.settings) then return end
    db.settings.screenshotOnDeath = not db.settings.screenshotOnDeath
    GDL:Print("Screenshot bei Tod: " ..
        (db.settings.screenshotOnDeath and "|cff00FF00AN|r" or "|cffFF4444AUS|r"))
end, {"ss"})

-- ── DNL STATUS ────────────────────────────────────────────────
RegisterCmd("dnl", function()
    local Bridge = GetModule("DNLBridge")
    if Bridge then Bridge:PrintStatus()
    else GDL:Print("|cffFF4444DNLBridge-Modul nicht geladen.|r") end
end, {"bridge"})

-- ── SELBST-DIAGNOSE ───────────────────────────────────────────
-- Ein-Blick-Gesundheitscheck: Version, Gilde/Fraktion, Tod-Erkennung,
-- Sync-Reichweite, Buch-Eintraege + Duplikate, wichtige Settings, Cache.
-- Read-only — aendert nichts.
RegisterCmd("status", function()
    local function yn(b) return b and "|cff1A5A24AN|r" or "|cff7A2C14AUS|r" end

    GDL:Print("|cffFFD100=== Buch der Gefallenen — Status v" .. (GDL.version or "?") .. " ===|r")
    local faction = (UnitFactionGroup and UnitFactionGroup("player")) or "?"
    GDL:Print("Client: |cffDDB060" .. (GDL.expansionLabel or "Classic") ..
        "|r  ·  Char: |cffDDB060" .. (GDL.playerName or "?") ..
        "|r  ·  Gilde: |cffDDB060" .. (GDL.currentGuildName or "—") .. "|r (" .. faction .. ")")

    -- Tod-Erkennung
    local Bridge = GetModule("DNLBridge")
    local detect = "|cffFF4444inaktiv|r"
    if Bridge then
        if Bridge.dnlActive then detect = "|cff1A5A24DeathNotificationLib|r"
        elseif Bridge.fallbackActive then detect = "|cffFFCC00Fallback (Kampflog)|r" end
    end
    GDL:Print("Tod-Erkennung: " .. detect ..
        "  ·  Tode/Session: |cffDDB060" .. ((Bridge and Bridge.deathsThisSession) or 0) .. "|r")

    -- Sync-Reichweite
    local Sync = GetModule("Sync")
    if Sync then
        local online = (Sync.GetOnlineUsers and Sync:GetOnlineUsers()) or {}
        local last   = (Sync.GetLastSyncTime and Sync:GetLastSyncTime()) or 0
        local lastStr = last > 0 and (math.floor((time() - last) / 60) .. "m her") or "nie"
        GDL:Print("Sync: |cffDDB060" .. #online .. "|r Mitglieder mit Addon  ·  letzter Sync: " .. lastStr)
    end

    -- Eintraege + Duplikat-Pruefung (gleicher Name + Zeitpunkt)
    local gd = GDL:GetGuildData()
    local total, dupes = 0, 0
    if gd and gd.deaths then
        total = #gd.deaths
        local seen = {}
        for _, d in ipairs(gd.deaths) do
            local nm  = ((d.name or ""):gsub("%-.*$", "")):lower()
            local key = nm .. "@" .. (d.timestamp or 0)
            if seen[key] then dupes = dupes + 1 else seen[key] = true end
        end
    end
    GDL:Print("Buch-Eintraege: |cffDDB060" .. total .. "|r" .. (dupes > 0
        and ("  ·  |cffFF6666" .. dupes .. " Duplikate|r — bereinigen mit /gdl dedupe")
        or  "  ·  |cff1A5A24keine Duplikate|r"))

    -- Settings + Kapitel-Cache
    local st = (GuildDeathLogDB and GuildDeathLogDB.settings) or {}
    GDL:Print("Todes-Popup: " .. yn(st.overlay) .. "  ·  Gildenchat-Ansage: " .. yn(st.announce))
    local UI = GetModule("UI")
    local pages = 0
    if UI and UI.mainFrame and UI.mainFrame._pages then
        for _ in pairs(UI.mainFrame._pages) do pages = pages + 1 end
    end
    GDL:Print("Kapitel-Cache: |cffDDB060" .. pages .. "|r Seiten gebaut")
end, {"diag", "health", "selfcheck"})

-- ── DUPLIKATE BEREINIGEN ──────────────────────────────────────
-- Entfernt LOKAL exakte Doppel-Eintraege (gleicher Name + exakter Zeitpunkt),
-- wie sie der alte Realm-Suffix-Bug erzeugen konnte. Konservativ: nur echte
-- Duplikate, kein Blacklist/Broadcast -> legitime Tode bleiben sync-faehig.
RegisterCmd("dedupe", function()
    local gd = GDL:GetGuildData()
    if not gd or not gd.deaths then
        GDL:Print("|cffFF4444Keine Gildendaten.|r")
        return
    end
    local deaths = gd.deaths
    local seen, removed = {}, 0
    for i = #deaths, 1, -1 do
        local d   = deaths[i]
        local nm  = ((d.name or ""):gsub("%-.*$", "")):lower()
        local key = nm .. "@" .. (d.timestamp or 0)
        if seen[key] then
            table.remove(deaths, i)
            removed = removed + 1
        else
            seen[key] = true
        end
    end
    if removed > 0 then
        GDL:Print("|cff00FF00" .. removed .. " doppelte Eintraege entfernt|r (gleicher Name + Zeitpunkt).")
        local UI = GetModule("UI")
        if UI then
            if UI.InvalidateChapters then UI:InvalidateChapters() end
            if UI.mainFrame and UI.mainFrame:IsShown() and UI.RenderChapter then
                UI:RenderChapter(UI.activeAction or "memorial")
            end
        end
    else
        GDL:Print("|cff1A5A24Keine Duplikate gefunden — alles sauber.|r")
    end
end, {"clean", "entdoppeln"})

-- ── GRABSTEIN-KARTE (teilbar) ─────────────────────────────────
-- /gdl card            -> neuester Gildentod als Karte
-- /gdl card <Name>     -> neuester Tod, dessen Name <Name> enthaelt
RegisterCmd("card", function(msg)
    local name = msg and msg:match("^card%s+(.+)$")
    local MC = GetModule("MemorialCard")
    if MC then
        MC:ShowLatest(name)
    else
        GDL:Print("|cffFF4444MemorialCard-Modul nicht geladen.|r")
    end
end, {"karte", "grabstein", "teilen"})

-- ── LOGBUCH / LOGBOOK ─────────────────────────────────────────
-- /gdl log            -> letzte 25 Eintraege im Chat
-- /gdl log 50         -> letzte 50
-- /gdl log export     -> Copy-Fenster mit allem
-- /gdl log clear      -> Logbuch leeren
RegisterCmd("log", function(msg)
    local LB = GetModule("Logbook")
    if not LB then GDL:Print("|cffFF4444Logbook-Modul nicht geladen.|r") return end
    local arg = msg and msg:match("^log%s+(%S+)")
    if arg == "export" then
        LB:ShowExportWindow()
    elseif arg == "clear" or arg == "leeren" then
        LB:Clear()
    else
        LB:Print(tonumber(arg) or 25)
    end
end, {"logbuch", "logbook"})

-- ── HELP ──────────────────────────────────────────────────────
RegisterCmd("help", function()
    GDL:Print("=== Befehle / Commands v" .. (GDL.version or "?") .. " ===")
    GDL:Print("/gdl - Buch oeffnen")
    GDL:Print("/gdl news - |cffFFD100Was ist neu? (Timeline)|r")
    GDL:Print("/gdl cal - |cffFFD100Gilden-Kalender|r")
    GDL:Print("/gdl titles - Titel-Fenster")
    GDL:Print("/gdl nptitles - Nameplate-Titel an/aus")
    GDL:Print("/gdl ach - Meilensteine")
    GDL:Print("/gdl prof - Gilden-Berufe")
    GDL:Print("/gdl rules - Gildenregeln")
    GDL:Print("/gdl dz - |cffFF6666Gefahrenzonen-Warnung an/aus|r |cffFFAA00(Supporter)|r")
    GDL:Print("/gdl sync - Sync anfordern")
    GDL:Print("/gdl played - |cff00CCFF/played Zeit anzeigen|r")
    GDL:Print("/gdl streak - |cff00CCFF Ueberlebens-Streak der Gilde|r")
    GDL:Print("/gdl heatmap - |cff00CCFFTodes-Heatmap auf Weltkarte an/aus|r")
    GDL:Print("/gdl screenshot - |cff00CCFFScreenshot bei eigenem Tod an/aus|r")
    GDL:Print("/gdl dnl - |cff00CCFFDeathNotificationLib Status|r")
    GDL:Print("/gdl status - |cff00FF00Selbst-Diagnose (Sync, Tode, Cache)|r")
    GDL:Print("/gdl log [n|export|clear] - |cff00CCFFLogbuch (zeitgenau, persistent)|r")
    GDL:Print("/gdl card [Name] - |cffFFD100Teilbare Grabstein-Karte (Screenshot/Discord)|r")
    GDL:Print("/gdl dedupe - |cffAAAAAADoppelte Buch-Eintraege bereinigen|r")
    GDL:Print("/gdl blacklist - |cffAAAAAAGeloeschte Eintraege zeigen|r")
    GDL:Print("/gdl clearblacklist - |cffAAAAAABlacklist leeren (Offiziere)|r")
    GDL:Print("/gdl dlcheck - |cffAAAAAADeathlog-Status pruefen|r")
    GDL:Print("/gdl report - |cffFFD100Woechentlicher Gildenbericht|r")
    GDL:Print("/gdl guildcomp - |cffFFD100Gilden-Wettbewerb & Leaderboard|r")
end)

-- ══════════════════════════════════════════════════════════════
-- DISPATCH (wird von Core aufgerufen)
-- ══════════════════════════════════════════════════════════════

function GDL:HandleSlashCommand(msg)
    msg = (msg or ""):lower():trim()

    -- Default: Buch oeffnen
    if msg == "" then
        commands["show"]()
        return
    end

    -- Einfache Commands (exakte Match)
    local cmd = msg:match("^(%S+)")
    local resolved = commands[cmd] or commands[aliases[cmd] or ""]

    if resolved then
        resolved(msg)
        return
    end

    -- ── PARAMETRISIERTE COMMANDS ──────────────────────────────
    -- discord <id>
    if msg:match("^discord%s+") then
        local discordId = msg:match("^discord%s+(%d+)$")
        if discordId then
            GuildDeathLogDB.discordId = discordId
            GDL:Print("|cff00FF00Discord-ID gespeichert:|r " .. discordId)
            GDL:Print("Jetzt kannst du deinen Supporter-Code einloesen.")
        else
            GDL:Print("|cffFF4444Fehler:|r /gdl discord DEINE_DISCORD_ID")
            GDL:Print("Deine ID findest du in der DM vom Patreon Bot.")
            local saved = GuildDeathLogDB and GuildDeathLogDB.discordId or "nicht gesetzt"
            GDL:Print("Aktuelle ID: |cffFFD100" .. saved .. "|r")
        end
        return
    end

    -- redeem <code>
    if msg:match("^redeem%s+") then
        local code = msg:gsub("^redeem%s+", "")
        local ok, info = GDL:RedeemSupporterCode(code)
        if ok then
            print("|cffffd200[GDL]|r " .. info)
        else
            print("|cffffd200[GDL]|r " .. (info or "Redeem fehlgeschlagen."))
        end
        return
    end

    -- setpw <password>
    if msg:match("^setpw%s+") then
        local password = msg:match("^setpw%s+(.+)$")
        if password and password ~= "" then
            if IsGuildLeader() or GDL:IsGuildOfficer() then
                GuildDeathLogDB.adminPassword = password
                GDL:Print("|cff00FF00Admin-Passwort gesetzt!|r (" .. #password .. " Zeichen)")
                GDL:Print("|cffAAAAFFNur du und Offiziere koennen es aendern.|r")
                local UI = GetModule("UI")
                if UI and UI.mainFrame and UI.mainFrame:IsShown() then
                    UI:UpdateChronicle()
                end
            else
                GDL:Print("|cffFF0000Fehler:|r Nur der Gildenleiter oder Offiziere!")
            end
        else
            GDL:Print("Verwendung: /gdl setpw <passwort>")
        end
        return
    end

    -- alt / role / note (Roster)
    if msg:match("^alt%s") then
        local Roster = GetModule("Roster")
        if Roster then Roster:HandleSlash(msg:match("^alt%s+(.+)")) else GDL:Print("Roster-Modul nicht geladen.") end
        return
    end
    if msg:match("^role%s") then
        local Roster = GetModule("Roster")
        if Roster then Roster:SetRole(msg:match("^role%s+(.+)")) end
        return
    end
    if msg:match("^note%s") then
        local Roster = GetModule("Roster")
        if Roster then Roster:SetNote(msg:match("^note%s+(.+)")) end
        return
    end

    -- find / suche <suchbegriff>
    if msg:match("^find%s+") or msg:match("^suche%s+") then
        local searchTerm = msg:match("^%S+%s+(.+)$")
        if searchTerm then
            local Professions = GetModule("Professions")
            if Professions then
                local results = Professions:FindByProfession(searchTerm)
                if #results == 0 then
                    GDL:Print("|cffFFAAAAKein Gildenmitglied mit '" .. searchTerm .. "' gefunden.|r")
                else
                    GDL:Print("|cff00AAFF" .. #results .. " Spieler mit '" .. searchTerm .. "':|r")
                    for _, r in ipairs(results) do
                        local p1 = r.data.prof1 and r.data.prof1.name or ""
                        local s1 = r.data.prof1 and r.data.prof1.skill or 0
                        local p2 = r.data.prof2 and r.data.prof2.name or ""
                        local s2 = r.data.prof2 and r.data.prof2.skill or 0
                        GDL:Print(string.format("  %s: %s (%d), %s (%d)", r.name, p1, s1, p2, s2))
                    end
                end
            end
        else
            GDL:Print("Verwendung: /gdl find <beruf>")
        end
        return
    end

    -- Nichts gefunden
    GDL:Print("Unbekannter Befehl. /gdl help fuer Hilfe")
end
