-- ==============================================================
-- MODUL: ServerNet - Serverweite Praesenz (BETA)
-- ==============================================================
-- Zaehlt, wie viele GuildDeathLog-Nutzer SERVERWEIT online sind.
-- Nutzt einen versteckten, passwortgeschuetzten Addon-Channel und
-- sendet dort periodisch einen Heartbeat. Es werden NUR Addon-
-- Nachrichten (unsichtbar) gesendet, kein sichtbarer Chat.
--
-- WICHTIG: Das hier ist reine PRAESENZ/Statistik. Der eigentliche
-- Tod-Backfill laeuft weiterhin ueber:
--   - GUILD-Sync (Sync.lua) zwischen Online-Gildenmitgliedern
--   - Deathlog/DNL-Import (Deathlog.lua: ScanData/TryImportEntry),
--     der verpasste EIGENE Gildentode aus der serverweit von DNL
--     gesammelten Datenbank zieht (gildengefiltert).
-- ==============================================================

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

local NET_PREFIX   = "GDLNet"
local CHANNEL_NAME = "guilddeathlognet"
local CHANNEL_PW   = "gdlnet0001"
local DELIM        = "|"

local HEARTBEAT_SEC = 120     -- alle 2 Minuten ein Lebenszeichen
local ONLINE_TTL    = 360     -- als "online" zaehlen, wenn <= 6 min her

-- name -> { version, guild, lastSeen }
local netUsers = {}

C_ChatInfo.RegisterAddonMessagePrefix(NET_PREFIX)

-- ── Channel-Handling ───────────────────────────────────────────
local function findChannelId()
    -- GetChannelName liefert 0/nil, wenn (noch) nicht beigetreten
    local id = GetChannelName(CHANNEL_NAME)
    if id and id > 0 then return id end
    return nil
end

function ServerNet:JoinNetChannel()
    if findChannelId() then return true end
    local id = JoinChannelByName(CHANNEL_NAME, CHANNEL_PW)
    if id and id > 0 then
        GDL:Debug("ServerNet: Netz-Channel beigetreten (ID: " .. id .. ")")
        return true
    end
    GDL:Debug("ServerNet: Konnte Netz-Channel nicht beitreten (evtl. Channel-Limit)")
    return false
end

-- ── Senden ─────────────────────────────────────────────────────
function ServerNet:SendHello()
    -- Gilde: currentGuildName wird erst vom Guild-Scan gesetzt — beim ersten
    -- Heartbeat (12s nach Login) war das oft noch nil -> guild="" im Paket
    -- -> Empfaenger konnten uns keiner Gilde zuordnen ("Gilde: 1").
    local guild = GDL.currentGuildName or (GetGuildInfo and GetGuildInfo("player")) or ""
    local msg = "HI" .. DELIM .. (GDL.version or "?") .. DELIM .. guild

    -- Kanal 1: GUILD-Addon-Message — zuverlaessig, kein Channel-Limit.
    -- Damit ist die Gilden-Zaehlung auch dann korrekt, wenn der versteckte
    -- Channel nicht beigetreten werden konnte (Classic: max 10 Channels,
    -- HC-Spieler haben oft hardcoredeaths + hcdeathalerts + world + ...).
    if IsInGuild() then
        pcall(C_ChatInfo.SendAddonMessage, NET_PREFIX, msg, "GUILD")
    end

    -- Kanal 2: versteckter Server-Channel (serverweite Zaehlung)
    local id = findChannelId()
    if not id then
        -- nochmal versuchen beizutreten, dann beim naechsten Tick senden
        self:JoinNetChannel()
        return
    end
    pcall(C_ChatInfo.SendAddonMessage, NET_PREFIX, msg, "CHANNEL", id)
end

-- ── Empfangen ──────────────────────────────────────────────────
function ServerNet:HandleNetMessage(message, sender)
    if not message or not sender then return end
    sender = strsplit("-", sender)              -- Realm-Suffix weg
    if sender == GDL.playerName then return end  -- nicht sich selbst zaehlen

    local cmd, version, guild = strsplit(DELIM, message)
    if cmd ~= "HI" then return end

    netUsers[sender] = netUsers[sender] or {}
    netUsers[sender].version  = version or "?"
    netUsers[sender].guild    = guild ~= "" and guild or nil
    netUsers[sender].lastSeen = time()
end

-- ── Auswertung ─────────────────────────────────────────────────
local function isOnline(data)
    return data and data.lastSeen and (time() - data.lastSeen) <= ONLINE_TTL
end

-- Serverweit online (inkl. einem selbst)
function ServerNet:GetServerOnlineCount()
    local n = 1  -- wir selbst
    for _, u in pairs(netUsers) do
        if isOnline(u) then n = n + 1 end   -- TTL beachten, keine Karteileichen
    end
    -- Gildenkollegen sind per Definition auf dem Server — nie weniger anzeigen
    local g = self:GetGuildOnlineCount()
    if g > n then n = g end
    return n
end

-- Davon in der EIGENEN Gilde online
function ServerNet:GetGuildOnlineCount()
    local myGuild = GDL.currentGuildName or (GetGuildInfo and GetGuildInfo("player"))
    -- Quelle 1: ServerNet-Heartbeats mit passender Gilde (TTL-gefiltert)
    local seen = {}
    local n = 1  -- wir selbst
    if myGuild then
        for name, u in pairs(netUsers) do
            if isOnline(u) and u.guild == myGuild then
                seen[name] = true
                n = n + 1
            end
        end
    end
    -- Quelle 2: Sync-Onlineliste (GUILD-Addon-Messages) — faengt auch
    -- Gildenkollegen mit aelteren Versionen, deren Heartbeat (noch)
    -- nicht ankommt. WICHTIG: Sync speichert Namen MIT Realm-Suffix
    -- ("Name-Realm"), netUsers OHNE — fuer die Union normalisieren,
    -- sonst zaehlt derselbe Spieler doppelt.
    local Sync = GDL:GetModule("Sync")
    if Sync and Sync.GetOnlineUsers then
        for _, u in ipairs(Sync:GetOnlineUsers()) do
            local short = u.name and strsplit("-", u.name)
            if short and short ~= GDL.playerName and not seen[short] then
                seen[short] = true
                n = n + 1
            end
        end
    end
    return n
end

-- Liste fuer Debug/Detailansicht
function ServerNet:GetServerUsers()
    local list = {}
    for name, data in pairs(netUsers) do
        if isOnline(data) then
            list[#list + 1] = { name = name, version = data.version, guild = data.guild }
        end
    end
    return list
end

-- ── Init / Events ──────────────────────────────────────────────
function ServerNet:Initialize()
    -- Channel beitreten (leicht verzoegert, nach den HC-Channels)
    C_Timer.After(8, function() self:JoinNetChannel() end)

    -- Erstes Lebenszeichen leicht gestreut, damit nicht alle gleichzeitig senden
    C_Timer.After(12 + math.random(0, 8), function() self:SendHello() end)

    -- Danach periodisch
    C_Timer.NewTicker(HEARTBEAT_SEC, function() self:SendHello() end)

    -- Aufraeumen: alte Eintraege regelmaessig verwerfen
    C_Timer.NewTicker(ONLINE_TTL, function()
        for name, data in pairs(netUsers) do
            if not isOnline(data) then netUsers[name] = nil end
        end
    end)

    GDL:Debug("ServerNet: Serverweite Praesenz aktiv (BETA)")
end

function ServerNet:HandleEvent(event, ...)
    if event == "CHAT_MSG_ADDON" then
        local prefix, message, channel, sender = ...
        -- Prefix ist exklusiv unser — Verteiler (CHANNEL/GUILD) egal.
        -- Der alte Filter auf channel == "CHANNEL" verwarf den GUILD-Pfad.
        if prefix == NET_PREFIX then
            self:HandleNetMessage(message, sender)
        end
    elseif event == "CHANNEL_UI_UPDATE" or event == "GUILD_ROSTER_UPDATE" then
        -- Nur sicherstellen, dass wir im Channel sind. KEIN Heartbeat hier
        -- (diese Events feuern oft -> sonst Channel-Spam). Praesenz macht der Takt.
        if not findChannelId() then self:JoinNetChannel() end
    end
end

local netFrame = CreateFrame("Frame")
netFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
netFrame:RegisterEvent("CHAT_MSG_ADDON")
netFrame:RegisterEvent("CHANNEL_UI_UPDATE")
netFrame:RegisterEvent("GUILD_ROSTER_UPDATE")
netFrame:SetScript("OnEvent", function(_, event, ...)
    if event == "PLAYER_ENTERING_WORLD" then
        C_Timer.After(1, function() ServerNet:Initialize() end)
        netFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
    else
        ServerNet:HandleEvent(event, ...)
    end
end)

GDL:RegisterModule("ServerNet", ServerNet)
