-- ==============================================================
-- MODUL: Badges - Verdienstabzeichen (sichtbar im Tooltip)
-- Gekoppelt an die VORHANDENEN Meilensteine + Errungenschaften
-- (keine Parallel-Logik). Sync 1:1 nach dem bewaehrten Titel-Muster.
-- Frei sichtbar; eigenes SENDEN ist Supporter-Feature (wie Titel).
-- DE/EN, Classic 1.15.8 + TBC 2.5.x.
-- ==============================================================

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

local BADGE_PREFIX       = "GDLBadge"
local BROADCAST_INTERVAL = 300   -- 5 Min Ticker (wie Titel)
local SYNC_DELAY         = 12     -- Erstsendung nach Login
local COMM_DELIM         = "|"
local LIST_DELIM         = ","
local MAX_TOOLTIP_LINES  = 10     -- Anzeige-Limit (Tooltip-Hoehe)

if C_ChatInfo and C_ChatInfo.RegisterAddonMessagePrefix then
    C_ChatInfo.RegisterAddonMessagePrefix(BADGE_PREFIX)
end

-- Laufzeit-Cache fremder Abzeichen (NICHT persistent): name -> { id=true, ... }
local guildBadges = {}

-- ==============================================================
-- HILFS-CHECKS (Andock an bestehende Systeme)
-- ==============================================================

local function _ms(id)   -- Meilenstein des eigenen Chars freigeschaltet?
    local M = GDL:GetModule("Milestones")
    return (M and M.IsMilestoneUnlocked and M:IsMilestoneUnlocked(id)) and true or false
end
local function _gach(id) -- Gilden-Errungenschaft (accountweit)
    local A = GDL:GetModule("Achievements")
    return (A and A.IsGuildUnlocked and A:IsGuildUnlocked(id)) and true or false
end

-- ==============================================================
-- ABZEICHEN-DEFINITIONEN
-- Jedes Abzeichen = genau EINE bestehende Leistung (Meilenstein
-- ODER Errungenschaft). Keine doppelten Inhalte. id ist kurz
-- (Sync-Last). icon = Anzeige. de/en + ddesc = Tooltip-Text.
-- ==============================================================
local BADGE_DEFS = {
    -- ── Frueh erreichbar (Einstieg/Progression) ────────────
    { id="l10",  icon="Interface\\Icons\\INV_Sword_04",                 de="Stufe 10", en="Level 10",
      dde="Stufe 10 erreicht", den="Reached level 10",
      prio=50, check=function() return _ms("level_10") end },
    { id="l20",  icon="Interface\\Icons\\INV_Shield_05",                de="Stufe 20", en="Level 20",
      dde="Stufe 20 erreicht", den="Reached level 20",
      prio=60, check=function() return _ms("level_20") end },
    { id="l40",  icon="Interface\\Icons\\Ability_Mount_RidingHorse",    de="Stufe 40 - Reittier", en="Level 40 - Mount",
      dde="Stufe 40 erreicht (Reittier)", den="Reached level 40 (mount)",
      prio=120, check=function() return _ms("level_40") end },
    { id="a7",   icon="Interface\\Icons\\Spell_Nature_TimeStop",        de="7 Tage ueberlebt", en="7 Days Survived",
      dde="7 Tage am Leben", den="7 days alive",
      prio=70, check=function() return _ms("alive_7") end },
    { id="a30",  icon="Interface\\Icons\\Spell_Holy_PrayerOfHealing",   de="30 Tage ueberlebt", en="30 Days Survived",
      dde="30 Tage am Leben", den="30 days alive",
      prio=110, check=function() return _ms("alive_30") end },
    { id="p168", icon="Interface\\Icons\\INV_Misc_PocketWatch_02",      de="168 Stunden gespielt", en="168 Hours Played",
      dde="7 Tage /played", den="7 days /played",
      prio=90, check=function() return _ms("played_168h") end },
    { id="k1k",  icon="Interface\\Icons\\Ability_Rogue_Ambush",         de="1.000 Erschlagene", en="1,000 Slain",
      dde="1.000 Kreaturen getoetet", den="1,000 creatures killed",
      prio=80, check=function() return _ms("kills_total_1000") end },
    { id="k5k",  icon="Interface\\Icons\\Spell_Shadow_ShadowWordPain",  de="5.000 Erschlagene", en="5,000 Slain",
      dde="5.000 Kreaturen getoetet", den="5,000 creatures killed",
      prio=160, check=function() return _ms("kills_total_5000") end },
    { id="pr1",  icon="Interface\\Icons\\Trade_Tailoring",              de="Geselle", en="Journeyman",
      dde="Beruf auf 150", den="Profession at 150",
      prio=100, check=function() return _ms("prof_150") end },

    -- ── Stufe (Max) ────────────────────────────────────────
    { id="l60",  icon="Interface\\Icons\\INV_Crown_01",                 de="Stufe 60", en="Level 60",
      dde="Maximalstufe erreicht (Classic)", den="Reached max level (Classic)",
      prio=300, check=function() return _ms("level_60") end },
    { id="l70",  icon="Interface\\Icons\\INV_Crown_02",                 de="Stufe 70", en="Level 70",
      dde="Maximalstufe erreicht (TBC)", den="Reached max level (TBC)",
      prio=305, check=function() return _ms("level_70") end },

    -- ── Raid-Bosse ─────────────────────────────────────────
    { id="ony",  icon="Interface\\Icons\\INV_Misc_Head_Dragon_Black",   de="Drachenbezwinger", en="Dragon Slayer",
      dde="Onyxia bezwungen", den="Defeated Onyxia",
      prio=250, check=function() return _ms("boss_ony") end },
    { id="rag",  icon="Interface\\Icons\\Spell_Fire_Lavaspawn",         de="Ragnaros-Toeter", en="Ragnaros Slayer",
      dde="Ragnaros bezwungen", den="Defeated Ragnaros",
      prio=255, check=function() return _ms("boss_mc_rag") end },
    { id="nef",  icon="Interface\\Icons\\INV_Misc_Head_Dragon_01",      de="Nefarian-Toeter", en="Nefarian Slayer",
      dde="Nefarian bezwungen", den="Defeated Nefarian",
      prio=258, check=function() return _ms("boss_bwl_nef") end },
    { id="hak",  icon="Interface\\Icons\\Ability_Creature_Poison_05",   de="Haekkar-Toeter", en="Hakkar Slayer",
      dde="Hakkar bezwungen", den="Defeated Hakkar",
      prio=256, check=function() return _ms("boss_zg_hakkar") end },
    { id="cth",  icon="Interface\\Icons\\INV_Misc_Eye_01",              de="Bezwinger von C'Thun", en="C'Thun Slayer",
      dde="C'Thun bezwungen", den="Defeated C'Thun",
      prio=270, check=function() return _ms("boss_aq40_cthun") end },
    { id="kt",   icon="Interface\\Icons\\INV_Trinket_Naxxramas06",      de="Bezwinger von Kel'Thuzad", en="Kel'Thuzad Slayer",
      dde="Kel'Thuzad bezwungen", den="Defeated Kel'Thuzad",
      prio=290, check=function() return _ms("boss_naxx_kt") end },

    -- ── Meisterschaft (Meta-Meilensteine) ──────────────────
    { id="dl",   icon="Interface\\Icons\\Spell_Holy_ChampionsBond",     de="Kerker-Legende", en="Dungeon Legend",
      dde="Alle Dungeons UND Raids gemeistert", den="All dungeons AND raids mastered",
      prio=295, check=function() return _ms("meta_dungeon_legend") end },
    { id="ar",   icon="Interface\\Icons\\INV_Helmet_06",                de="Raidmeister", en="Raid Master",
      dde="Alle Raid-Endbosse bezwungen", den="All raid end bosses defeated",
      prio=275, check=function() return _ms("meta_all_raids") end },
    { id="ad",   icon="Interface\\Icons\\INV_Jewelry_Talisman_08",      de="Kerkermeister", en="Dungeon Master",
      dde="Alle Dungeon-Endbosse bezwungen", den="All dungeon end bosses defeated",
      prio=265, check=function() return _ms("meta_all_dungeons") end },
    { id="mc",   icon="Interface\\Icons\\Trade_Engineering",            de="Meisterhandwerker", en="Master Crafter",
      dde="Hauptberuf 300 + alle Nebenberufe 300", den="Main prof 300 + all secondary 300",
      prio=240, check=function() return _ms("meta_complete_crafter") end },
    { id="pr3",  icon="Interface\\Icons\\Trade_BlackSmithing",          de="Berufsmeister", en="Profession Master",
      dde="Hauptberuf auf 300", den="Main profession at 300",
      prio=150, check=function() return _ms("prof_300") end },

    -- ── Hardcore-Ausdauer ──────────────────────────────────
    { id="a365", icon="Interface\\Icons\\Spell_Holy_AshesToAshes",      de="Ein Jahr ueberlebt", en="Survived a Year",
      dde="365 Tage am Leben", den="365 days alive",
      prio=285, check=function() return _ms("alive_365") end },
    { id="p720", icon="Interface\\Icons\\INV_Misc_PocketWatch_01",      de="720 Stunden gespielt", en="720 Hours Played",
      dde="30 Tage /played", den="30 days /played",
      prio=200, check=function() return _ms("played_720h") end },

    -- ── Schlachten ─────────────────────────────────────────
    { id="k50",  icon="Interface\\Icons\\Spell_Shadow_DeathScream",     de="50.000 Erschlagene", en="50,000 Slain",
      dde="50.000 Kreaturen getoetet", den="50,000 creatures killed",
      prio=210, check=function() return _ms("kills_total_50000") end },
    { id="pv1k", icon="Interface\\Icons\\Achievement_PVP_A_14",         de="1000 Ehrenhafte Siege", en="1000 Honorable Kills",
      dde="1000 ehrenhafte Siege", den="1000 honorable kills",
      prio=205, check=function() return _ms("pvp_1000") end },

    -- ── Gilden-Errungenschaften (Team-Leistungen) ──────────
    { id="gp",   icon="Interface\\Icons\\INV_Misc_GroupNeedMore",       de="Fuenf von Uns", en="Five of Us",
      dde="5/5 reine Gilden-Gruppe", den="5/5 pure guild party",
      prio=180, check=function() return _gach("g_full_party") end },
    { id="gw",   icon="Interface\\Icons\\Spell_Holy_Resurrection",      de="Wir kaempfen weiter", en="We Fight On",
      dde="Nach einem Wipe zurueckgekommen", den="Came back after a wipe",
      prio=170, check=function() return _gach("g_wipe_back") end },
    { id="gn30", icon="Interface\\Icons\\Spell_Holy_AuraOfLight",       de="Eiserne Ordnung", en="Iron Order",
      dde="30 Tage ohne Gildentod", den="30 days without a guild death",
      prio=220, check=function() return _gach("g_nodeaths_30d") end },
}

local BADGE_BY_ID = {}
for _, b in ipairs(BADGE_DEFS) do BADGE_BY_ID[b.id] = b end

function Badges:GetAll() return BADGE_DEFS end
function Badges:GetById(id) return BADGE_BY_ID[id] end

-- Prestige-Rang aus der Anzahl erfuellter Abzeichen (von 29 moeglichen)
local RANK_TIERS = {
    { min=23, de="Diamant", en="Diamond",  color={0.62, 0.85, 1.00} },
    { min=16, de="Platin",  en="Platinum", color={0.82, 0.90, 0.95} },
    { min=10, de="Gold",    en="Gold",     color={1.00, 0.82, 0.00} },
    { min=5,  de="Silber",  en="Silver",   color={0.78, 0.80, 0.84} },
    { min=1,  de="Bronze",  en="Bronze",   color={0.80, 0.52, 0.26} },
}
function Badges:GetRank(count)
    for _, t in ipairs(RANK_TIERS) do
        if (count or 0) >= t.min then return t end
    end
    return nil
end

local function _name(b)
    local en = GDL.WantEnglish and GDL:WantEnglish()
    return (en and b.en) or b.de
end

-- ==============================================================
-- EIGENE ABZEICHEN ERMITTELN (nur eigener Char; Checks lokal)
-- ==============================================================
function Badges:ComputeMyBadges()
    local out = {}
    for _, b in ipairs(BADGE_DEFS) do
        local ok = false
        pcall(function() ok = b.check() end)
        if ok then out[#out + 1] = b.id end
    end
    return out
end

-- Sortierte Anzeige-Liste (nach Prio absteigend) aus einer ID-Menge
local function _sortedFromSet(set)
    local list = {}
    for _, b in ipairs(BADGE_DEFS) do
        if set[b.id] then list[#list + 1] = b end
    end
    table.sort(list, function(a, c) return (a.prio or 0) > (c.prio or 0) end)
    return list
end

-- Abzeichen-Menge fuer einen Spielernamen (eigener Char live, sonst Sync-Cache)
function Badges:GetBadgeSet(playerName)
    local myName = UnitName("player")
    if playerName == myName then
        local set = {}
        for _, id in ipairs(self:ComputeMyBadges()) do set[id] = true end
        return set
    end
    return guildBadges[playerName]
end

-- ==============================================================
-- SYNC (Muster: Titel) - Empfang frei, Senden Supporter-gated
-- ==============================================================
local function _send(msg)
    if not (C_ChatInfo and C_ChatInfo.SendAddonMessage) then return end
    if IsInGuild() then
        pcall(C_ChatInfo.SendAddonMessage, BADGE_PREFIX, msg, "GUILD")
    end
    if IsInGroup and IsInGroup() then
        local chan = (IsInRaid and IsInRaid()) and "RAID" or "PARTY"
        pcall(C_ChatInfo.SendAddonMessage, BADGE_PREFIX, msg, chan)
    end
end

function Badges:BroadcastBadges()
    if not (GDL.IsSupporterActive and GDL:IsSupporterActive()) then return end
    local ids = self:ComputeMyBadges()
    -- Auf ELEMENT-Grenze kappen (nie mitten in eine ID schneiden); Prefix "B|"
    -- einkalkuliert. Verhindert stillen Badge-Verlust durch byteweisen sub().
    local out, len = {}, 2  -- "B" + COMM_DELIM
    for _, id in ipairs(ids) do
        local add = #id + (#out > 0 and #LIST_DELIM or 0)
        if len + add > 240 then break end
        out[#out + 1] = id
        len = len + add
    end
    local msg = "B" .. COMM_DELIM .. table.concat(out, LIST_DELIM)
    _send(msg)
    if GDL.Debug then GDL:Debug("Badges: SENT " .. #out .. " badges") end
end

function Badges:RequestAll()
    _send("REQ")
    if GDL.Debug then GDL:Debug("Badges: REQUEST sent") end
end

function Badges:HandleAddonMessage(msg, sender)
    if not msg or msg == "" then return end
    local name = strsplit("-", sender)
    local myName = UnitName("player")

    if msg == "REQ" then
        if name == myName then return end   -- eigenen Request ignorieren
        -- Mit Jitter antworten (gegen Antwort-Salven), nur Supporter senden
        C_Timer.After(math.random() * 2, function() self:BroadcastBadges() end)
        return
    end

    local cmd, payload = strsplit(COMM_DELIM, msg)
    if cmd ~= "B" then return end

    local set = {}
    local n = 0
    if payload and payload ~= "" then
        for id in string.gmatch(payload, "([^" .. LIST_DELIM .. "]+)") do
            if BADGE_BY_ID[id] then set[id] = true; n = n + 1 end  -- nur bekannte IDs
        end
    end
    guildBadges[name] = (n > 0) and set or nil
    if GDL.Debug then GDL:Debug("Badges: RECV from " .. name .. " | " .. n .. " badges") end

    -- Falls dieser Spieler gerade unter dem Mauszeiger ist: Tooltip neu
    if GameTooltip and GameTooltip:IsShown() then
        local _, u = GameTooltip:GetUnit()
        if u and UnitName(u) == name then
            GameTooltip:Hide()  -- naechster OnEnter zeichnet frisch
        end
    end
end

-- Ex-Mitglieder aus dem Cache entfernen. Der Tooltip ist ohnehin
-- gilden-gated -> Nicht-Mitglieder werden nie angezeigt, also raus.
function Badges:CleanupNonMembers()
    local Guild = GDL:GetModule("Guild")
    if not Guild or not Guild.IsMember then return end
    for name in pairs(guildBadges) do
        if not Guild:IsMember(name) then
            guildBadges[name] = nil
        end
    end
end

-- ==============================================================
-- TOOLTIP-ANZEIGE (frei) - Icon-Reihe der Abzeichen
-- ==============================================================
function Badges:SetupTooltipHook()
    if self._tooltipHooked then return end
    self._tooltipHooked = true
    GameTooltip:HookScript("OnTooltipSetUnit", function(tooltip)
        local _, unit = tooltip:GetUnit()
        if not unit or not UnitIsPlayer(unit) then return end
        -- Nur Gildenmitglieder (wie Titel-Tooltip)
        local ug = GetGuildInfo(unit)
        local mg = GetGuildInfo("player")
        if not ug or not mg or ug ~= mg then return end

        local name = UnitName(unit)
        if not name then return end
        local set = Badges:GetBadgeSet(name)
        if not set or not next(set) then return end

        local list = _sortedFromSet(set)
        local total = #list
        if total == 0 then return end

        local en = GDL.WantEnglish and GDL:WantEnglish()
        tooltip:AddLine(" ")
        tooltip:AddLine((en and "Merit Badges" or "Verdienstabzeichen")
            .. " |cffAAAAAA(" .. total .. ")|r", 1, 0.84, 0)

        -- Prestige-Rang aus der Abzeichen-Anzahl
        local rank = Badges:GetRank(total)
        if rank then
            local c = rank.color
            local rname = (en and rank.en) or rank.de
            local colored = string.format("|cff%02x%02x%02x%s|r", c[1] * 255, c[2] * 255, c[3] * 255, rname)
            tooltip:AddLine(((en and "Prestige Rank: ") or "Prestige-Rang: ") .. colored, 0.7, 0.7, 0.7)
        end

        -- Icon + Name pro Zeile (klar erkennbar), nach Prio sortiert
        local shown = math.min(total, MAX_TOOLTIP_LINES)
        for i = 1, shown do
            local b = list[i]
            tooltip:AddLine("|T" .. b.icon .. ":16:16:0:0:64:64:5:59:5:59|t "
                .. _name(b), 0.95, 0.85, 0.55)
        end
        if total > shown then
            tooltip:AddLine("|cff888888+" .. (total - shown) .. " "
                .. (en and "more" or "weitere") .. "|r")
        end
        tooltip:Show()
    end)
    if GDL.Debug then GDL:Debug("Badges: Tooltip-Hook aktiv") end
end

-- ==============================================================
-- INIT
-- ==============================================================
function Badges:RegisterEvents()
    if self.eventFrame then return end
    self.eventFrame = CreateFrame("Frame")
    self.eventFrame:RegisterEvent("CHAT_MSG_ADDON")
    self.eventFrame:RegisterEvent("GUILD_ROSTER_UPDATE")
    self.eventFrame:SetScript("OnEvent", function(_, event, prefix, message, channel, sender)
        if event == "CHAT_MSG_ADDON" and prefix == BADGE_PREFIX then
            if channel == "GUILD" or channel == "PARTY" or channel == "RAID" then
                self:HandleAddonMessage(message, sender)
            end
        elseif event == "GUILD_ROSTER_UPDATE" then
            C_Timer.After(5, function() self:CleanupNonMembers() end)
        end
    end)
end

-- Broadcast (Supporter-Feature) starten. Guard verhindert Doppel-Ticker, falls
-- mehrfach aufgerufen (Init + Mid-Session-Supporter-Freischaltung).
function Badges:EnableBroadcasting()
    if self._broadcastStarted then return end
    self._broadcastStarted = true
    C_Timer.After(SYNC_DELAY, function() self:BroadcastBadges() end)
    C_Timer.NewTicker(BROADCAST_INTERVAL, function()
        if GDL.IsSupporterActive and GDL:IsSupporterActive() then
            self:BroadcastBadges()
        end
    end)
end

function Badges:Initialize()
    if self.initialized then return end
    self.initialized = true

    -- Empfang + Anzeige: IMMER aktiv (frei, wie Nameplate-Titel)
    self:RegisterEvents()
    self:SetupTooltipHook()
    C_Timer.After(SYNC_DELAY + 4, function() self:RequestAll() end)

    -- Senden des eigenen Status: Supporter-Feature (wie Titel-Broadcast)
    if GDL.IsSupporterActive and GDL:IsSupporterActive() then
        self:EnableBroadcasting()
    end

    if GDL.Debug then
        GDL:Debug("Badges: Modul initialisiert (" .. #BADGE_DEFS .. " Abzeichen)"
            .. ((GDL.IsSupporterActive and GDL:IsSupporterActive()) and " (+Supporter-Broadcast)" or ""))
    end
end

GDL:RegisterModule("Badges", Badges)
