local addonName, TOAST = ...

local currentLevel, currentMap = nil, nil

local lastWhisperRound = 0
local WHISPER_COOLDOWN  = 300

local guildMembers = {}

local memberByName = nil
local cachedTargets = nil

local function EnsureMemberLookup()
    if memberByName then return end
    memberByName = {}
    for _, m in ipairs(TOAST.members) do
        memberByName[m.name] = m
    end
end

local COMMUNITY_NAME = "TIDE and Crew"

TOAST.keys = {}
TOAST.inCommunity = false

C_ChatInfo.RegisterAddonMessagePrefix("TOAST")
C_ChatInfo.RegisterAddonMessagePrefix("LibKS")

local function BroadcastKey()
    local mapID = C_MythicPlus.GetOwnedKeystoneChallengeMapID()
    if not mapID or mapID == 0 then return end
    local level = C_MythicPlus.GetOwnedKeystoneLevel()
    local name  = C_ChallengeMode.GetMapUIInfo(mapID)
    name = name and name:match("^%s*(.-)%s*$") or ""
    local short = TOAST.DUNGEON_SHORT[name] or name
    C_ChatInfo.SendAddonMessage("TOAST", "KEY:" .. level .. ":" .. short, "GUILD")
    local playerName = UnitName("player")
    TOAST.keys[playerName] = { level = level, short = short, lastSeen = GetServerTime() }
    TOAST_DB.keys[playerName] = { level = level, short = short, lastSeen = GetServerTime() }
end

local function CheckKeyUpdate()
    C_Timer.NewTimer(1, function()
        local newLevel = C_MythicPlus.GetOwnedKeystoneLevel()
        local newMap   = C_MythicPlus.GetOwnedKeystoneChallengeMapID()
        if newLevel ~= currentLevel or newMap ~= currentMap then
            currentLevel = newLevel
            currentMap   = newMap
            BroadcastKey()
        end
    end)
end

local function IsInCommunity()
    if InCombatLockdown() then
        return TOAST.inCommunity or false  -- gebruik cached waarde
    end
    local clubs = C_Club.GetSubscribedClubs()
    if not clubs then return false end
    for _, club in ipairs(clubs) do
        local ok, match = pcall(function() return club.name == COMMUNITY_NAME end)
        if not ok then return TOAST.inCommunity or false end
        if match then
            return true
        end
    end
    return false
end
TOAST.IsInCommunity = IsInCommunity

local COMMUNITY_CLUB_ID = nil  -- cache, gevuld bij eerste call

local function GetCommunityClubId()
    if COMMUNITY_CLUB_ID then return COMMUNITY_CLUB_ID end
    if InCombatLockdown() then return nil end  -- geen cache, niet veilig om C_Club te callen
    local clubs = C_Club.GetSubscribedClubs()
    if not clubs then return nil end
    for _, club in ipairs(clubs) do
        local ok, match = pcall(function() return club.name == COMMUNITY_NAME end)
        if not ok then return COMMUNITY_CLUB_ID end
        if match then
            COMMUNITY_CLUB_ID = club.clubId
            return COMMUNITY_CLUB_ID
        end
    end
    return nil
end

function TOAST.GetOnlineMembers()
    if InCombatLockdown() then return nil end

    local online = {}
    local scannedSource = false

    local function IsLivePresence(presence)
        return presence == Enum.ClubMemberPresence.Online
            or presence == Enum.ClubMemberPresence.Away
            or presence == Enum.ClubMemberPresence.Busy
    end

    -- Check community
    local communityId = GetCommunityClubId()
    if communityId then
        local memberIds = C_Club.GetClubMembers(communityId)
        if memberIds and #memberIds > 0 then
            scannedSource = true
            for _, memberId in ipairs(memberIds) do
                local info = C_Club.GetMemberInfo(communityId, memberId)
                if info and info.name and IsLivePresence(info.presence) then
                    local charName = info.name:match("^([^%-]+)")
                    online[charName] = true
                end
            end
        end
    end

    -- Check guild (ook een club)
    local guildId = C_Club.GetGuildClubId()
    if guildId then
        local memberIds = C_Club.GetClubMembers(guildId)
        if memberIds and #memberIds > 0 then
            scannedSource = true
            for _, memberId in ipairs(memberIds) do
                local info = C_Club.GetMemberInfo(guildId, memberId)
                if info and info.name and IsLivePresence(info.presence) then
                    local charName = info.name:match("^([^%-]+)")
                    online[charName] = true
                end
            end
        end
    end

    return scannedSource and online or nil
end

local function RecognizePlayer()
    local playerName = UnitName("player")

    for i, entry in ipairs(TOAST.leaderboard) do
        if entry.name == playerName then
            TOAST_DB.me = {
                name = entry.name,
                realm = entry.realm,
                discord = entry.discord_nickname,
                rank = i,
                rio = entry.rio,
                role = entry.role,
                resi = entry.resi,
                class_ = entry.class_,
            }
            for _, m in ipairs(TOAST.members) do
                if m.name == playerName then
                    TOAST_DB.me.discord_id = m.discord_id
                    TOAST_DB.me.seamen_rank = m.rank
                    break
                end
            end
            TOAST.me = TOAST_DB.me
            return
        end
    end

    EnsureMemberLookup()
    local entry = memberByName[playerName]
    if entry then
        TOAST_DB.me = {
            name = entry.name,
            realm = entry.realm,
            discord = entry.discord_nickname,
            discord_id = entry.discord_id,
            seamen_rank = entry.rank,
            role = entry.role,
            class_ = entry.class_,
        }
        TOAST.me = TOAST_DB.me
        return
    end

    TOAST.me = nil
end

local function BuildGuildLookup()
    guildMembers = {}
    local n = GetNumGuildMembers()
    for i = 1, n do
        local name = GetGuildRosterInfo(i)
        if name then
            local charName = name:match("^([^%-]+)")
            guildMembers[charName] = true
        end
    end
    cachedTargets = nil
end

local function GetLeaderboardTargets()
    if cachedTargets then return cachedTargets end

    EnsureMemberLookup()

    local discordIds = {}
    for _, entry in ipairs(TOAST.leaderboard) do
        local m = memberByName[entry.name]
        if m and m.discord_id then discordIds[m.discord_id] = true end
    end

    local targets = {}
    for _, m in ipairs(TOAST.members) do
        if discordIds[m.discord_id] and not guildMembers[m.name] then
            table.insert(targets, { name = m.name, realm = m.realm })
        end
    end

    cachedTargets = targets
    return targets
end

local function DiscoveryWhispers()
    local delay = 0
    for _, target in ipairs(GetLeaderboardTargets()) do
        local whisperTarget = target.name .. "-" .. target.realm
        C_Timer.NewTimer(delay, function()
            C_ChatInfo.SendAddonMessage("TOAST", "KEY_REQUEST", "WHISPER", whisperTarget)
        end)
        delay = delay + 0.4
    end
end

local function MaybeRefreshUnknowns()
    local now = GetServerTime()
    if now - lastWhisperRound < WHISPER_COOLDOWN then return end
    lastWhisperRound = now
    local delay = 0
    for _, target in ipairs(GetLeaderboardTargets()) do
        local data = TOAST.keys[target.name]
        if data and data.lastSeen and (now - data.lastSeen) >= 3600 then
            local whisperTarget = target.name .. "-" .. target.realm
            C_Timer.NewTimer(delay, function()
                C_ChatInfo.SendAddonMessage("TOAST", "KEY_REQUEST", "WHISPER", whisperTarget)
            end)
            delay = delay + 0.4
        end
    end
end

local ev = CreateFrame("Frame")
ev:RegisterEvent("ADDON_LOADED")
ev:RegisterEvent("PLAYER_LOGIN")
ev:RegisterEvent("CLUB_STREAMS_LOADED")
ev:RegisterEvent("CHALLENGE_MODE_MAPS_UPDATE")
ev:RegisterEvent("CHALLENGE_MODE_COMPLETED")
ev:RegisterEvent("GUILD_ROSTER_UPDATE")
ev:RegisterEvent("CHAT_MSG_ADDON")
ev:SetScript("OnEvent", function(self, event, prefix, message, channel, sender)
    if event == "ADDON_LOADED" then
        if prefix ~= addonName then return end
        TOAST_DB = TOAST_DB or {}

        -- Weekly key reset (woensdag = EU weekly reset)
        local today = date("%Y-%m-%d")
        local weekday = tonumber(date("%w"))  -- 0=zondag, 3=woensdag
        local hour = tonumber(date("%H"))
        if weekday == 3 and hour >= 9 and TOAST_DB.lastKeyReset ~= today then
            if TOAST.MigrateKeyPresence then TOAST.MigrateKeyPresence() end
            TOAST_DB.keys = {}
            TOAST_DB.lastKeyReset = today
        end

        TOAST_DB.keys = TOAST_DB.keys or {}
        for k, v in pairs(TOAST_DB.keys) do
            TOAST.keys[k] = v
        end
        return
    end
    if event == "PLAYER_LOGIN" then
        BuildGuildLookup()
        BroadcastKey()
        RecognizePlayer()
        C_ChatInfo.SendAddonMessage("TOAST", "KEY_REQUEST", "GUILD")
        C_ChatInfo.SendAddonMessage("LibKS", "R", "GUILD")
        DiscoveryWhispers()
        return
    end
    if event == "CLUB_STREAMS_LOADED" then
        TOAST.inCommunity = IsInCommunity()
        ev:UnregisterEvent("CLUB_STREAMS_LOADED")
        return
    end
    if event == "GUILD_ROSTER_UPDATE" then
        BuildGuildLookup()
        return
    end
    if event == "CHALLENGE_MODE_MAPS_UPDATE" then
        BroadcastKey()
        return
    end
    if event == "CHALLENGE_MODE_COMPLETED" then
        currentLevel = C_MythicPlus.GetOwnedKeystoneLevel()
        currentMap   = C_MythicPlus.GetOwnedKeystoneChallengeMapID()
        ev:RegisterEvent("ITEM_CHANGED")
        ev:RegisterEvent("ITEM_PUSH")
        ev:RegisterEvent("PLAYER_LEAVING_WORLD")
        return
    end
    if event == "PLAYER_LEAVING_WORLD" then
        ev:UnregisterEvent("ITEM_CHANGED")
        ev:UnregisterEvent("ITEM_PUSH")
        ev:UnregisterEvent("PLAYER_LEAVING_WORLD")
        return
    end
    if event == "ITEM_CHANGED" then
        CheckKeyUpdate()
        return
    end
    if event == "ITEM_PUSH" then
        CheckKeyUpdate()
        return
    end
    -- CHAT_MSG_ADDON
    if (prefix == "LibKS" or prefix == "TOAST")
       and TOAST.MarkPresenceSenderSeen then
        local observed = TOAST.MarkPresenceSenderSeen(sender)
        if observed and TOAST.RefreshStatusDots then
            TOAST.RefreshStatusDots(false)
        end
    end

    if prefix == "LibKS" then
        if message == "R" then
            local mapID = C_MythicPlus.GetOwnedKeystoneChallengeMapID()
            local level = C_MythicPlus.GetOwnedKeystoneLevel()
            local rating = 0
            local rs = C_PlayerInfo.GetPlayerMythicPlusRatingSummary("player")
            if rs and rs.currentSeasonScore then
                rating = rs.currentSeasonScore
            end
            if mapID and mapID > 0 then
                C_ChatInfo.SendAddonMessage("LibKS",
                    level .. "," .. mapID .. "," .. rating, "GUILD")
            end
            return
        end
        local levelStr, mapIDStr = message:match("^(%d+),(%d+),")
        if levelStr and mapIDStr then
            local level = tonumber(levelStr)
            local mapID = tonumber(mapIDStr)
            if level and level > 0 and mapID and mapID > 0 then
                local name = C_ChallengeMode.GetMapUIInfo(mapID)
                local short = TOAST.DUNGEON_SHORT[name] or name
                local charName = sender:match("^([^%-]+)")
                TOAST.keys[charName] = { level = level, short = short, lastSeen = GetServerTime() }
                TOAST_DB.keys[charName] = { level = level, short = short, lastSeen = GetServerTime() }
                if TOAST.RefreshKeyColumn then TOAST.RefreshKeyColumn() end
            end
        end
        return
    end
    if prefix ~= "TOAST" then return end
    if message == "KEY_REQUEST" then
        if channel == "WHISPER" then
            local mapID = C_MythicPlus.GetOwnedKeystoneChallengeMapID()
            local level = C_MythicPlus.GetOwnedKeystoneLevel()
            if mapID and mapID > 0 then
                local name  = C_ChallengeMode.GetMapUIInfo(mapID)
                local short = TOAST.DUNGEON_SHORT[name] or name
                C_ChatInfo.SendAddonMessage("TOAST",
                    "KEY:" .. level .. ":" .. short, "WHISPER", sender)
            end
        else
            BroadcastKey()
        end
        return
    end
    if not message:match("^KEY:") then return end
    local payload = message:sub(5)
    local level, short = payload:match("^(%d+):(.+)$")
    if not level then return end
    local charName = sender:match("^([^%-]+)")
    TOAST.keys[charName] = { level = tonumber(level), short = short, lastSeen = GetServerTime() }
    TOAST_DB.keys[charName] = { level = tonumber(level), short = short, lastSeen = GetServerTime() }
    if TOAST.RefreshKeyColumn then TOAST.RefreshKeyColumn() end
end)

ChatFrame_AddMessageEventFilter("CHAT_MSG_SYSTEM", function(self, event, msg)
    if msg and msg:find("is currently playing") then
        return true
    end
end)

TOAST.RefreshUnknowns = MaybeRefreshUnknowns
