-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
-- MODUL: Deathlog - Integration mit Deathlog / DeathNotificationLib
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
-- UnterstÃ¼tzte Quellen:
--   A) DeathNotificationLib_HookOnNewEntry  (bevorzugt, lokal)
--   B) CHAT_MSG_ADDON "HCDeathAlerts" GUILD (Fallback: DNL nicht lokal)
--   C) SavedVariables-Scan                 (einmalig beim Login)
-- Alle Pfade laufen durch Sync:ProcessIncomingDeath â†’ IsDuplicate-Guard.
-- NUR Gildenmitglieder werden verarbeitet!
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•

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

local deathlogAvailable  = false
local deathlogEntryCount = 0
local deathlogGuildDeaths = 0
local lastScan = 0
local deathlogAddonName  = nil
local extListenerActive  = false

-- Prefixes registrieren (ueber Compat-Wrapper fuer alle Expansionen)
local _reg = (addon and addon._compat and addon._compat.RegisterAddonMessagePrefix)
    or (C_ChatInfo and C_ChatInfo.RegisterAddonMessagePrefix)
    or RegisterAddonMessagePrefix
if _reg then
    _reg("HCDeathAlerts")
    _reg("DLG")
end

-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
-- DNL / Deathlog Nachrichtenformat (Protokoll v3)
-- Separator: "~"
-- Felder: cmd~name~guild~sourceId~raceId~classId~level~instanceId~mapId~posX~posY~date~lastWords~checksum
-- cmd "1" = BROADCAST_DEATH_PING
-- cmd "4" = GUILD_DEATH_NOTIFICATION (GUILD-Channel)
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
local DNL_SEP = "~"
local DNL_GUILD_NOTIFY = "4"
local DNL_BROADCAST    = "1"

function Deathlog:Initialize()
    C_Timer.After(2,  function() self:DetectDeathlog() end)
    C_Timer.After(5,  function() self:DetectDeathlog() end)
    C_Timer.After(10, function() self:DetectDeathlog() end)
    -- Periodischer Re-Scan: DNL synct serverweite Tode im Hintergrund nach;
    -- so kommen verpasste EIGENE Gildentode auch nachtraeglich rein (ScanData
    -- hat 30s-Eigenthrottle, daher ist alle 3 Min unkritisch).
    C_Timer.NewTicker(180, function() self:ScanData() end)
    -- Externer Listener immer aufsetzen (Fallback wenn DNL nicht lokal)
    C_Timer.After(1,  function() self:SetupExternalListeners() end)
end

-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
-- EXTERNER LISTENER: empfÃ¤ngt HCDeathAlerts GUILD-Messages auch
-- wenn DeathNotificationLib NICHT lokal installiert ist.
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
function Deathlog:SetupExternalListeners()
    if extListenerActive then return end
    extListenerActive = true

    self.extFrame = CreateFrame("Frame")
    self.extFrame:RegisterEvent("CHAT_MSG_ADDON")
    self.extFrame:SetScript("OnEvent", function(_, event, prefix, message, channel, sender)
        if event ~= "CHAT_MSG_ADDON" then return end
        -- Nur GUILD-Channel â†’ nur Gildenmitglieder kÃ¶nnen das senden
        if channel ~= "GUILD" then return end
        if prefix ~= "HCDeathAlerts" and prefix ~= "DLG" then return end
        -- Wenn DNL lokal aktiv ist, Ã¼bernimmt der Hook alles â€“ nur als Backup
        self:HandleExternalDeathMessage(prefix, message, sender)
    end)

    GDL:Debug("Deathlog: Externer HCDeathAlerts-Listener aktiv")
end

-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
-- DNL-NACHRICHT PARSEN (GUILD-Channel, nur Gildenmitglieder)
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
function Deathlog:HandleExternalDeathMessage(prefix, message, sender)
    if not GDL.currentGuildName then return end

    local parts = {strsplit(DNL_SEP, message)}
    if #parts < 7 then return end

    local cmd = parts[1]
    if cmd ~= DNL_GUILD_NOTIFY and cmd ~= DNL_BROADCAST then return end

    -- Felder extrahieren
    local name       = parts[2]  or ""
    local guild      = parts[3]  or ""
    local sourceId   = tonumber(parts[4])  or 0
    -- parts[5] = race_id  (ignoriert)
    local classId    = tonumber(parts[6])  or 0
    local level      = tonumber(parts[7])  or 0
    -- parts[8] = instance_id (ignoriert)
    local mapId      = tonumber(parts[9])  or 0
    local posX       = tonumber(parts[10]) or 0
    local posY       = tonumber(parts[11]) or 0
    local timestamp  = tonumber(parts[12]) or time()
    local lastWords  = parts[13] or ""

    -- Realm-Suffix entfernen, trimmen
    name  = (strsplit("-", name)):match("^%s*(.-)%s*$")
    guild = guild:match("^%s*(.-)%s*$")

    if name == "" then return end

    -- â”€â”€ Gildenmitglied-PrÃ¼fung (zweistufig) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
    local isGuildMember = (guild ~= "" and guild == GDL.currentGuildName)
    if not isGuildMember then
        local Guild = GDL:GetModule("Guild")
        isGuildMember = Guild and Guild:IsMember(name)
    end
    if not isGuildMember then
        GDL:Debug("ExtDeath [" .. prefix .. "]: " .. name .. " NICHT in Gilde â€“ ignoriert")
        return
    end

    GDL:Debug("ExtDeath [" .. prefix .. "]: " .. name .. " Lvl " .. level .. " von " .. (strsplit("-", sender)))

    local death = {
        name        = name,
        level       = level,
        classId     = classId,
        zone        = self:GetZoneName(mapId),
        mapId       = mapId,
        -- DNL sendet Koordinaten als normalisierte Floats (0â€“1); ggf. durch 10000 teilen
        posX        = posX > 1 and (posX / 10000) or posX,
        posY        = posY > 1 and (posY / 10000) or posY,
        timestamp   = timestamp,
        lastWords   = lastWords,
        killerId    = sourceId > 0 and sourceId or nil,
        fromDeathlog = true,
        source      = "HCDeathAlerts",
    }

    local Sync = GDL:GetModule("Sync")
    if Sync then
        -- isSilent = Tod Ã¤lter als 60s (historisch) â†’ kein Sound/Overlay
        local isSilent = (time() - timestamp) > 60
        Sync:ProcessIncomingDeath(death, strsplit("-", sender) .. " [DNL]", isSilent)
    end
end

function Deathlog:DetectDeathlog()
    if deathlogAvailable then
        self:ScanData()
        return
    end

    GDL:Debug("Deathlog: Suche nach Deathlog-Addon...")

    -- â•â•â• METHODE 1: DeathNotificationLib Hook (bevorzugt) â•â•â•â•â•â•â•
    if _G["DeathNotificationLib_HookOnNewEntry"] then
        -- HINWEIS (9.7.4): Live-Feed laeuft ausschliesslich ueber DNLBridge
        -- (HookOnNewAddonEntry). Frueher registrierte Deathlog.lua HIER zusaetzlich
        -- HookOnNewEntry + HookOnNewEntrySecure -> jeder Tod wurde doppelt/dreifach
        -- durch RecordLocalDeath geschoben. Nur noch Fallback, falls DNLBridge fehlt.
        if GDL:GetModule("DNLBridge") then
            GDL:Debug("Deathlog: Live-Feed via DNLBridge - eigener Hook uebersprungen (kein Doppel-Eintrag)")
        else
            GDL:Debug("Deathlog: DNLBridge fehlt -> eigener DNL-Hook als Fallback registriert")
            DeathNotificationLib_HookOnNewEntry(function(playerData, checksum, peerReport, inGuild)
                self:OnDeathNotification(playerData, inGuild)
            end)
        end
        deathlogAvailable = true
        deathlogAddonName = "DeathNotificationLib"
        self:ScanData()
        return
    end

    -- â•â•â• METHODE 2: SavedVariables â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    local possibleDataTables = {
        "deathlog_data", "DeathlogData", "DEATHLOG_DATA", "Deathlog_Data",
        "deathlogData", "DeathlogDB", "deathlogDB",
        "hardcore_character_data", "HardcoreDeathlogData", "HardcoreDeaths",
    }
    for _, varName in ipairs(possibleDataTables) do
        if _G[varName] and type(_G[varName]) == "table" then
            GDL:Debug("Deathlog: SavedVariable '" .. varName .. "' gefunden!")
            deathlogAvailable = true
            deathlogAddonName = varName
            self:ScanData()
            return
        end
    end

    -- â•â•â• METHODE 3: Addon-Tabelle â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    local possibleAddonTables = {
        "Deathlog", "DeathLog", "DEATHLOG", "deathlog",
        "HardcoreDeathLog", "HardcoreDeathlog", "Hardcore", "HCDeathlog",
    }
    for _, varName in ipairs(possibleAddonTables) do
        local addonTable = _G[varName]
        if addonTable and type(addonTable) == "table" then
            if addonTable.data or addonTable.deaths or addonTable.entries or addonTable.db or
               addonTable.GetDeaths or addonTable.OnDeath or addonTable.character_data then
                GDL:Debug("Deathlog: Addon-Tabelle '" .. varName .. "' gefunden!")
                deathlogAvailable = true
                deathlogAddonName = varName
                self:ScanData()
                return
            end
        end
    end

    -- â•â•â• METHODE 4: GetAddOnInfo â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
    local deathlogAddons = {
        "Deathlog", "DeathLog", "deathlog",
        "Hardcore_Deathlog", "HardcoreDeathlog", "Hardcore", "HardcoreAddon",
        "ClassicHardcore", "Classic_Hardcore",
    }
    for _, name in ipairs(deathlogAddons) do
        local loaded = (C_AddOns and C_AddOns.IsAddOnLoaded or IsAddOnLoaded)(name)
        if loaded then
            GDL:Debug("Deathlog: Addon '" .. name .. "' geladen â€“ warte auf Daten")
            deathlogAddonName = name .. " (geladen)"
            C_Timer.After(5, function() self:DetectDeathlog() end)
            return
        end
    end

    GDL:Debug("Deathlog: Kein Deathlog-Addon gefunden")
end

function Deathlog:OnEvent(event)
    if event == "PLAYER_READY" then
        C_Timer.After(5, function() self:DetectDeathlog() end)
    end
end

function Deathlog:SetupHooks()
    self:DetectDeathlog()
end

-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
-- OnDeathNotification: vom DeathNotificationLib Hook (lokal)
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
function Deathlog:OnDeathNotification(playerData, inGuild)
    GDL:Debug("DeathNotification: " .. (playerData and playerData.name or "nil"))

    if not playerData or not playerData.name or not GDL.currentGuildName then return end

    -- Nur Gildenmitglieder
    local isGuildDeath = inGuild or (playerData.guild and playerData.guild == GDL.currentGuildName)
    if not isGuildDeath then
        -- Roster-Fallback: vielleicht sind wir neu beigetreten
        local Guild = GDL:GetModule("Guild")
        isGuildDeath = Guild and Guild:IsMember(playerData.name)
    end
    if not isGuildDeath then
        GDL:Debug("DeathNotification: " .. playerData.name .. " nicht in unserer Gilde")
        return
    end

    GDL:Debug("DeathNotification: " .. playerData.name .. " â€“ Gildenmitglied!")

    local posX, posY = 0, 0
    if playerData.map_pos then
        if playerData.map_pos.x then
            posX, posY = playerData.map_pos.x, playerData.map_pos.y
        elseif playerData.map_pos[1] then
            posX, posY = playerData.map_pos[1], playerData.map_pos[2]
        end
    end

    local death = {
        name      = playerData.name,
        level     = playerData.level or 0,
        classId   = playerData.class_id or 0,
        zone      = self:GetZoneName(playerData.map_id),
        mapId     = playerData.map_id or 0,
        posX      = posX,
        posY      = posY,
        lastWords = playerData.last_words or "",
        timestamp = playerData.date or time(),
        fromDeathlog = true,
        source    = "DNL-Hook",
    }

    local Sync = GDL:GetModule("Sync")
    if Sync then Sync:RecordLocalDeath(death) end
end

function Deathlog:OnHardcoreDeathEvent(name, level, classId, zoneName)
    if not name then return end
    local Guild = GDL:GetModule("Guild")
    if not Guild or not Guild:IsMember(name) then return end
    local death = {name=name, level=level or 0, classId=classId or 0, zone=zoneName or GDL:L("UNKNOWN"), timestamp=time()}
    local Sync = GDL:GetModule("Sync")
    if Sync then Sync:RecordLocalDeath(death) end
end

-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
-- SavedVariables-Scan (einmaliger Import beim Login)
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
function Deathlog:ScanData()
    if time() - lastScan < 30 then return end
    lastScan = time()
    deathlogEntryCount  = 0
    deathlogGuildDeaths = 0

    local dataSource = nil
    local possibleSources = {
        _G["deathlog_data"], _G["DeathlogData"], _G["DEATHLOG_DATA"],
        _G["Deathlog_Data"], _G["deathlogData"],
    }
    for _, source in ipairs(possibleSources) do
        if source and type(source) == "table" then dataSource = source; break end
    end

    if not dataSource then
        for _, name in ipairs({"Deathlog","DeathLog","DEATHLOG","deathlog"}) do
            local t = _G[name]
            if t and type(t) == "table" then
                dataSource = t.data or t.deaths or t.entries or t.db or t.saved or t
                if dataSource and type(dataSource) == "table" and next(dataSource) then break end
            end
        end
    end

    if not dataSource or type(dataSource) ~= "table" then
        if not deathlogAvailable then GDL:Debug("Deathlog: Keine Datenquelle") end
        return
    end

    deathlogAvailable = true
    local guildData = GDL:GetGuildData()
    if not guildData then return end
    local Sync = GDL:GetModule("Sync")
    local importedCount = 0

    for key1, value1 in pairs(dataSource) do
        if type(value1) == "table" then
            if value1.name or value1.level or value1.class_id then
                deathlogEntryCount = deathlogEntryCount + 1
                if self:TryImportEntry(value1, key1, guildData, Sync) then importedCount = importedCount + 1 end
            else
                for key2, entry in pairs(value1) do
                    if type(entry) == "table" then
                        deathlogEntryCount = deathlogEntryCount + 1
                        if self:TryImportEntry(entry, key2, guildData, Sync) then importedCount = importedCount + 1 end
                    end
                end
            end
        end
    end

    if importedCount > 0 then GDL:Debug("Deathlog: " .. importedCount .. " Gildentode importiert") end
    local UI = GDL:GetModule("UI")
    if UI and UI.mainFrame and UI.mainFrame:IsShown() then UI:UpdateChronicle() end
end

function Deathlog:TryImportEntry(entry, playerKey, guildData, Sync)
    if not entry or type(entry) ~= "table" then return false end
    local entryGuild = entry.guild or entry.guildName or entry.guild_name
    if not GDL.currentGuildName then return false end
    if not entryGuild or entryGuild ~= GDL.currentGuildName then return false end
    deathlogGuildDeaths = deathlogGuildDeaths + 1

    local posX, posY = 0, 0
    local mapPos = entry.map_pos or entry.mapPos or entry.position
    if mapPos then
        posX = mapPos.x or mapPos[1] or 0
        posY = mapPos.y or mapPos[2] or 0
    end

    local name = entry.name or entry.playerName or entry.player_name
    if not name and playerKey then name = playerKey:match("^([^%-]+)") or playerKey end

    local death = {
        name      = name,
        level     = entry.level or entry.playerLevel or 0,
        classId   = entry.class_id or entry.classId or entry.class or 0,
        zone      = self:GetZoneName(entry.map_id or entry.mapId or entry.mapID),
        mapId     = entry.map_id or entry.mapId or entry.mapID or 0,
        posX      = posX, posY = posY,
        timestamp = entry.date or entry.timestamp or entry.time or 0,
        lastWords = entry.last_words or entry.lastWords or "",
        fromDeathlog = true,
    }
    local srcId = entry.source_id or entry.sourceId or entry.killer_id or 0
    local srcName = entry.source_name or entry.sourceName or entry.killerName or ""
    if tonumber(srcId) and tonumber(srcId) > 0 then
        death.killerId   = tonumber(srcId)
        death.killerName = srcName ~= "" and srcName or nil  -- NPC-ID allein ist kein lesbarer Name
    elseif srcName ~= "" then
        death.killerName = srcName
    end

    if Sync then
        if Sync:IsBlacklisted(death.name, death.timestamp) then return false end
        if not Sync:IsDuplicate(death) then
            table.insert(guildData.deaths, death)
            return true
        end
    end
    return false
end

function Deathlog:GetZoneName(mapId)
    if not mapId or mapId == 0 then return GDL:L("UNKNOWN") end
    local mapInfo = C_Map.GetMapInfo(mapId)
    return mapInfo and mapInfo.name or GDL:L("UNKNOWN")
end

function Deathlog:IsAvailable() return deathlogAvailable end

function Deathlog:GetStats()
    return {
        available     = deathlogAvailable,
        totalEntries  = deathlogEntryCount,
        guildDeaths   = deathlogGuildDeaths,
        addonName     = deathlogAddonName or "Nicht erkannt",
        extListener   = extListenerActive,
    }
end

function Deathlog:DebugGlobals()
    GDL:Print("=== Deathlog Debug ===")
    GDL:Print("Status: " .. (deathlogAvailable and "|cff00FF00JA|r" or "|cffFF0000NEIN|r"))
    GDL:Print("Erkannt als: " .. (deathlogAddonName or "nichts"))
    GDL:Print("Externer Listener: " .. (extListenerActive and "|cff00FF00AKTIV|r" or "|cffFF0000INAKTIV|r"))
    GDL:Print("Eintraege: " .. deathlogEntryCount .. " / Gildentode: " .. deathlogGuildDeaths)
    GDL:Print("--- Checks ---")
    GDL:Print("DeathNotificationLib_HookOnNewEntry:       " .. ((_G["DeathNotificationLib_HookOnNewEntry"]       and "|cff00FF00JA|r") or "|cffFF0000NEIN|r"))
    GDL:Print("DeathNotificationLib_HookOnNewEntrySecure: " .. ((_G["DeathNotificationLib_HookOnNewEntrySecure"] and "|cff00FF00JA|r") or "|cffFF0000NEIN|r"))
    GDL:Print("deathlog_data: "  .. ((_G["deathlog_data"]  and "|cff00FF00JA|r") or "|cffFF0000NEIN|r"))
    GDL:Print("Deathlog:      "  .. ((_G["Deathlog"]       and "|cff00FF00JA|r") or "|cffFF0000NEIN|r"))
    GDL:Print("DeathlogDB:    "  .. ((_G["DeathlogDB"]     and "|cff00FF00JA|r") or "|cffFF0000NEIN|r"))
    local found = 0
    GDL:Print("--- death/hardcore Globals ---")
    for k, v in pairs(_G) do
        local kl = k:lower()
        if (kl:find("death") or kl:find("hardcore") or kl:find("fallen")) and not k:find("GuildDeathLog") then
            if type(v) == "table" then
                local c = 0; for _ in pairs(v) do c=c+1 end
                GDL:Print("  |cff00FF00" .. k .. "|r (" .. c .. " Eintraege)")
                found = found + 1
            elseif type(v) == "function" then
                GDL:Print("  |cffFFFF00" .. k .. "|r (function)")
                found = found + 1
            end
        end
    end
    if found == 0 then GDL:Print("  |cffFF0000Keine relevanten Variablen gefunden!|r") end
    GDL:Print("--- Geladene Addons ---")
    local total = (C_AddOns and C_AddOns.GetNumAddOns or GetNumAddOns)()
    local foundA = 0
    for i = 1, total do
        local name = ((C_AddOns and C_AddOns.GetAddOnInfo or GetAddOnInfo)(i))
        local nl = (name or ""):lower()
        if nl:find("death") or nl:find("hardcore") then
            local loaded = (C_AddOns and C_AddOns.IsAddOnLoaded or IsAddOnLoaded)(i)
            GDL:Print("  " .. (name or "?") .. " - " .. (loaded and "|cff00FF00GELADEN|r" or "|cffFF0000NICHT GELADEN|r"))
            foundA = foundA + 1
        end
    end
    if foundA == 0 then GDL:Print("  |cffFF0000Keine Deathlog/Hardcore Addons gefunden|r") end
end

GDL:RegisterModule("Deathlog", Deathlog)
