-- ============================================================
-- Asylum Compagnon — Vault.lua
-- Clés Mythique+ de la semaine + Coffre convoité (Great Vault).
-- Chaque client lit sa clé (C_MythicPlus) et ses 9 cases de coffre
-- (C_WeeklyRewards.GetActivities) et les diffuse (« V ») ; les autres
-- les mettent en cache dans peers[key].keystone / peers[key].vault.
-- La page « Clés & Coffre » liste tout le monde ; le cadre des 9 cases
-- s'affiche au survol du nom.
-- ============================================================

local ADDON_NAME, NS = ...

local Vault = {}
NS.Vault = Vault

-- Libellés des pistes du coffre (Raid / Donjons / Monde / PvP)
local TYPE_LABEL = { [1] = "Donjons", [2] = "PvP", [3] = "Raid", [6] = "Monde", [7] = "Gouffres" }
do
    local E = Enum and Enum.WeeklyRewardChestThresholdType
    if E then
        if E.Raid then TYPE_LABEL[E.Raid] = "Raid" end
        if E.Activities then TYPE_LABEL[E.Activities] = "Donjons" end
        if E.MythicPlus then TYPE_LABEL[E.MythicPlus] = "Donjons" end
        if E.World then TYPE_LABEL[E.World] = "Monde" end
        if E.RankedPvP then TYPE_LABEL[E.RankedPvP] = "PvP" end
    end
end
NS.VAULT_TYPE_LABEL = TYPE_LABEL

-- ---------- Lecture locale ----------
local function ReadKeystone()
    local mapID, level = 0, 0
    if C_MythicPlus then
        if C_MythicPlus.GetOwnedKeystoneMapID then
            local ok, v = pcall(C_MythicPlus.GetOwnedKeystoneMapID)
            if ok and v then mapID = v end
        end
        if C_MythicPlus.GetOwnedKeystoneLevel then
            local ok, v = pcall(C_MythicPlus.GetOwnedKeystoneLevel)
            if ok and v then level = v end
        end
    end
    return mapID, level
end

local function ReadVault()
    local out = {}
    if C_WeeklyRewards and C_WeeklyRewards.GetActivities then
        local ok, acts = pcall(C_WeeklyRewards.GetActivities)
        if ok and type(acts) == "table" then
            for _, a in ipairs(acts) do
                out[#out + 1] = {
                    type = a.type or 0, index = a.index or 0,
                    progress = a.progress or 0, threshold = a.threshold or 0,
                    level = a.level or 0,
                }
            end
        end
    end
    return out
end

function Vault.KeystoneName(mapID)
    if not mapID or mapID == 0 then return nil end
    if C_ChallengeMode and C_ChallengeMode.GetMapUIInfo then
        local ok, name = pcall(C_ChallengeMode.GetMapUIInfo, mapID)
        if ok and name then return name end
    end
    return nil
end

-- ---------- Sérialisation / diffusion ----------
function Vault.SerializeMine()
    local mapID, level = ReadKeystone()
    local acts = ReadVault()
    local parts = {}
    for _, a in ipairs(acts) do
        parts[#parts + 1] = ("%d,%d,%d,%d,%d"):format(a.type, a.index, a.progress, a.threshold, a.level)
    end
    return ("%d:%d\t%s"):format(mapID, level, table.concat(parts, ";"))
end

local entered = false
function Vault.BroadcastMine()
    if not (NS.Sync and NS.Sync.available and NS.Sync.BroadcastVault) then return end
    NS.Sync.BroadcastVault(Vault.SerializeMine())
end

-- Réception d'un « V »
function Vault.OnPeerVault(sender, msg)
    local body = msg:match("^V\t(.*)$")
    if not body or not NS.db then return end
    local keyPart, vaultPart = body:match("^([^\t]*)\t(.*)$")
    if not keyPart then keyPart = body; vaultPart = "" end
    NS.db.peers = NS.db.peers or {}
    local p = NS.db.peers[sender]
    if not p then p = {}; NS.db.peers[sender] = p end
    local mapID, level = keyPart:match("^(%-?%d+):(%-?%d+)$")
    p.keystone = { mapID = tonumber(mapID) or 0, level = tonumber(level) or 0 }
    local vault = {}
    for chunk in (vaultPart or ""):gmatch("[^;]+") do
        local t, i, pr, th, lv = chunk:match("^(%-?%d+),(%-?%d+),(%-?%d+),(%-?%d+),(%-?%d+)$")
        if t then
            vault[#vault + 1] = {
                type = tonumber(t), index = tonumber(i),
                progress = tonumber(pr), threshold = tonumber(th), level = tonumber(lv),
            }
        end
    end
    p.vault = vault
    p.lastSeen = (GetServerTime and GetServerTime()) or time()
    if NS.RefreshVaultUI then NS.RefreshVaultUI() end
end

-- Écho aux hellos (anti-rafale) — comme le module butin
local helloEcho
function Vault.OnPeerHello()
    if helloEcho then return end
    helloEcho = C_Timer.NewTimer(6, function()
        helloEcho = nil
        Vault.BroadcastMine()
    end)
end

-- ---------- Rapport pour l'UI ----------
local function KeyOf(unit)
    local name, realm = UnitFullName(unit)
    if not name then return nil end
    realm = (realm and realm ~= "" and realm)
        or (GetNormalizedRealmName and GetNormalizedRealmName()) or ""
    return (realm ~= "") and (name .. "-" .. realm) or name
end

-- Regroupe une liste d'activités par piste (Raid/Donjons/…), 3 cases chacune
local function BuildTracks(acts)
    local order, byType = {}, {}
    for _, a in ipairs(acts or {}) do
        if not byType[a.type] then
            byType[a.type] = { label = NS.L(TYPE_LABEL[a.type] or "Piste"), cells = {} }
            order[#order + 1] = a.type
        end
        local cell = {
            filled = (a.progress or 0) >= (a.threshold or 0) and (a.threshold or 0) > 0,
            progress = a.progress or 0, threshold = a.threshold or 0, level = a.level or 0,
        }
        byType[a.type].cells[a.index or (#byType[a.type].cells + 1)] = cell
    end
    local tracks = {}
    for _, t in ipairs(order) do tracks[#tracks + 1] = byType[t] end
    return tracks
end

-- Liste { name, class, isMe, keyText, tracks } : moi + pairs ayant partagé
function Vault.GetReport()
    local list = {}
    local mapID, level = ReadKeystone()
    local _, myClass = UnitClass("player")
    local kn = Vault.KeystoneName(mapID)
    list[#list + 1] = {
        name = UnitName("player") or "?", class = myClass, isMe = true,
        keyText = (kn and level > 0) and (kn .. " +" .. level) or NS.L("aucune clé"),
        tracks = BuildTracks(ReadVault()),
    }
    for k, p in pairs((NS.db and NS.db.peers) or {}) do
        if p.keystone or p.vault then
            local mid = p.keystone and p.keystone.mapID or 0
            local lvl = p.keystone and p.keystone.level or 0
            local nm = Vault.KeystoneName(mid)
            list[#list + 1] = {
                name = k:match("^(.-)%-") or k, class = p.class,
                keyText = (nm and lvl > 0) and (nm .. " +" .. lvl) or NS.L("aucune clé"),
                tracks = BuildTracks(p.vault),
            }
        end
    end
    table.sort(list, function(a, b)
        if a.isMe ~= b.isMe then return a.isMe end
        return a.name < b.name
    end)
    return list
end

-- ---------- Événements ----------
local sendTimer
local function QueueSend()
    if sendTimer then return end
    sendTimer = C_Timer.NewTimer(5, function()
        sendTimer = nil
        Vault.BroadcastMine()
    end)
end

local ev = CreateFrame("Frame")
-- RegisterEvent sous pcall : un nom d'événement inconnu (variantes Midnight)
-- ne doit pas casser le chargement du module.
pcall(ev.RegisterEvent, ev, "PLAYER_ENTERING_WORLD")
pcall(ev.RegisterEvent, ev, "WEEKLY_REWARDS_UPDATE")
pcall(ev.RegisterEvent, ev, "CHALLENGE_MODE_COMPLETED")
ev:SetScript("OnEvent", function(_, event)
    if event == "PLAYER_ENTERING_WORLD" then
        if not entered then
            entered = true
            C_Timer.After(8, function() Vault.BroadcastMine() end)
        end
    elseif entered then
        QueueSend()
    end
end)
