-- ============================================================
-- Asylum Compagnon — Buffs.lua
-- Check de préparation raid/M+ (esprit Method Raid Tools) :
-- flacon, nourriture, huile d'arme + buffs de groupe par classe.
--
-- · Buffs de classe : lus localement sur chaque unité du groupe
--   via C_UnitAuras (avec garde « secret values » de Midnight).
-- · Flacon / nourriture : chacun scanne SES buffs et l'état voyage
--   dans le « hello » de la synchro (flags "fno" → 1/0).
-- · Huile d'arme : GetWeaponEnchantInfo ne marche que sur soi →
--   uniquement via les flags de la synchro.
-- Tables éditables dans Core.lua : NS.FLASK_AURAS, NS.FOOD_AURAS,
-- NS.RAID_BUFFS. « /asylum auras » liste tes buffs pour aider.
--
-- Check à l'appel : sur READY_CHECK, on rediffuse ses flags au groupe
-- puis on imprime dans son chat qui manque quoi (Buffs.PrintReadyCheckReport).
-- ============================================================

local ADDON_NAME, NS = ...

local Buffs = {}
NS.Buffs = Buffs

-- L'unité a-t-elle l'une des auras du set ? (nil = pas lisible)
local function HasAnyAura(unit, ids)
    if not (C_UnitAuras and C_UnitAuras.GetBuffDataByIndex and ids) then return nil end
    for i = 1, 60 do
        local ok, aura = pcall(C_UnitAuras.GetBuffDataByIndex, unit, i)
        if not ok or not aura then break end
        local sid = aura.spellId
        if sid and not (issecretvalue and issecretvalue(sid)) then
            local ok2, hit = pcall(function() return ids[sid] end)
            if ok2 and hit then return true end
        end
    end
    return false
end
Buffs.HasAnyAura = HasAnyAura

-- Mon état consommables : flacon / nourriture / huile d'arme
function Buffs.GetSelfStatus()
    local flask = HasAnyAura("player", NS.FLASK_AURAS)
    local food  = HasAnyAura("player", NS.FOOD_AURAS)
    local oil   = nil
    if GetWeaponEnchantInfo then
        local ok, hasMH = pcall(GetWeaponEnchantInfo)
        if ok then oil = hasMH and true or false end
    end
    return { flask = flask, food = food, oil = oil }
end

-- Présence d'enchantement sur les 8 slots suivis → chaîne de bits "10110101"
-- (lien d'objet : le 1er champ après l'itemID = l'enchantID ; >0 = enchanté)
function Buffs.SelfEnchantBits()
    local bits = {}
    for _, sl in ipairs(NS.ENCHANT_SLOTS or {}) do
        local link = GetInventoryItemLink and GetInventoryItemLink("player", sl.inv)
        local ench = "0"
        if link then
            local e = link:match("item:%d+:(%d+)")
            if e and (tonumber(e) or 0) > 0 then ench = "1" end
        end
        bits[#bits + 1] = ench
    end
    return table.concat(bits)
end

-- Sérialise l'état pour le « hello » : "fno" (flacon/bouffe/huile) + bits enchant
function Buffs.SelfFlags()
    local s = Buffs.GetSelfStatus()
    return ("%d%d%d"):format(s.flask and 1 or 0, s.food and 1 or 0, s.oil and 1 or 0)
        .. Buffs.SelfEnchantBits()
end

local function ParseFlags(flags)
    if type(flags) ~= "string" or #flags < 3 then return nil end
    local ench
    local nslots = #(NS.ENCHANT_SLOTS or {})
    if #flags >= 3 + nslots and nslots > 0 then
        ench = {}
        for i = 1, nslots do ench[i] = flags:sub(3 + i, 3 + i) == "1" end
    end
    return {
        flask = flags:sub(1, 1) == "1",
        food  = flags:sub(2, 2) == "1",
        oil   = flags:sub(3, 3) == "1",
        ench  = ench,
    }
end

-- Roster : moi + amis (avec l'addon) présents dans le groupe
local function BuildRoster()
    local roster = {}
    local seen = {}
    local function addUnit(unit, forceInclude)
        local guid = UnitGUID(unit)
        if not guid or seen[guid] then return end
        local key = NS.UnitKey(unit)
        if not key then return end
        local name = key:match("^([^%-]+)") or key
        if not forceInclude then
            if not (NS.db and NS.db.peers and NS.db.peers[key]) then return end
        end
        seen[guid] = true
        local _, classToken = UnitClass(unit)
        roster[#roster + 1] = { unit = unit, key = key, name = name,
            class = classToken, isMe = forceInclude and true or false }
    end
    addUnit("player", true)
    if IsInRaid() then
        for i = 1, 40 do
            if #roster >= 8 then break end
            addUnit("raid" .. i)
        end
    elseif IsInGroup() then
        for i = 1, 4 do
            addUnit("party" .. i)
        end
    end
    return roster
end

-- Classes présentes dans TOUT le groupe (pour savoir quels buffs exiger)
local function PresentClasses()
    local present = {}
    local function mark(unit)
        if UnitExists and not UnitExists(unit) then return end
        local _, token = UnitClass(unit)
        if token then present[token] = true end
    end
    mark("player")
    if IsInRaid() then
        for i = 1, 40 do mark("raid" .. i) end
    elseif IsInGroup() then
        for i = 1, 4 do mark("party" .. i) end
    end
    return present
end

-- Rapport complet pour la page « Buffs & auras » :
-- columns = libellés ; rows = { name, class, cells = {true/false/nil…} }
function Buffs.GetReport()
    local roster = BuildRoster()
    local present = PresentClasses()

    local columns = { NS.L("Flacon"), NS.L("Bouffe"), NS.L("Huile") }
    local buffCols = {}
    for _, b in ipairs(NS.RAID_BUFFS or {}) do
        if present[b.class] then
            columns[#columns + 1] = NS.L(b.label)
            buffCols[#buffCols + 1] = b
        end
    end

    local rows = {}
    for _, m in ipairs(roster) do
        local cells = {}
        if m.isMe then
            local s = Buffs.GetSelfStatus()
            cells[1], cells[2], cells[3] = s.flask, s.food, s.oil
        else
            local p = NS.db and NS.db.peers and NS.db.peers[m.key]
            local f = p and ParseFlags(p.flags)
            if f then
                cells[1], cells[2], cells[3] = f.flask, f.food, f.oil
            else
                cells[1], cells[2], cells[3] = nil, nil, nil
            end
        end
        for _, b in ipairs(buffCols) do
            cells[#cells + 1] = HasAnyAura(m.unit, b.ids)
        end
        rows[#rows + 1] = { name = m.name, class = m.class, isMe = m.isMe, cells = cells }
    end

    return { columns = columns, rows = rows }
end

-- Rapport enchantements : même forme que GetReport (colonnes = 8 slots,
-- cells = true enchanté / false manquant / nil inconnu).
function Buffs.GetEnchantReport()
    local roster = BuildRoster()
    local slots = NS.ENCHANT_SLOTS or {}
    local columns = {}
    for _, sl in ipairs(slots) do columns[#columns + 1] = NS.L(sl.label) end
    local rows = {}
    for _, m in ipairs(roster) do
        local cells = {}
        if m.isMe then
            local bits = Buffs.SelfEnchantBits()
            for i = 1, #slots do cells[i] = (bits:sub(i, i) == "1") end
        else
            local p = NS.db and NS.db.peers and NS.db.peers[m.key]
            local f = p and ParseFlags(p.flags)
            if f and f.ench then
                for i = 1, #slots do cells[i] = f.ench[i] end
            else
                for i = 1, #slots do cells[i] = nil end
            end
        end
        rows[#rows + 1] = { name = m.name, class = m.class, isMe = m.isMe, cells = cells }
    end
    return { columns = columns, rows = rows }
end

-- ============================================================
-- Check automatique à l'appel (ready check), esprit MRT
-- Quand un ready check démarre, on rediffuse nos flags au groupe,
-- puis on imprime dans NOTRE chat qui manque quoi (message ciblé,
-- local — rien n'est envoyé dans le canal de groupe).
-- ============================================================

-- Manques par joueur : { name, class, isMe, missing = {libellés},
-- unknown = {libellés} } — colonne fausse = manque, colonne nil = inconnu
function Buffs.GetMissing()
    local rep = Buffs.GetReport()
    local list = {}
    for _, row in ipairs(rep.rows) do
        local miss, unknown = {}, {}
        for j = 1, #rep.columns do
            local v = row.cells[j]
            if v == false then
                miss[#miss + 1] = rep.columns[j]
            elseif v == nil then
                unknown[#unknown + 1] = rep.columns[j]
            end
        end
        list[#list + 1] = {
            name = row.name, class = row.class, isMe = row.isMe,
            missing = miss, unknown = unknown,
        }
    end
    return list
end

-- Code couleur de classe pour le chat ("|cAARRGGBB")
local function ClassColor(token)
    if token and C_ClassColor and C_ClassColor.GetClassColor then
        local c = C_ClassColor.GetClassColor(token)
        if c and c.GenerateHexColor then return "|c" .. c:GenerateHexColor() end
    end
    local r = token and RAID_CLASS_COLORS and RAID_CLASS_COLORS[token]
    if r and r.colorStr then return "|c" .. r.colorStr end
    return "|cffffffff"
end

-- Imprime le rapport ciblé dans le chat local
function Buffs.PrintReadyCheckReport()
    if not NS.Msg then return end
    local list = Buffs.GetMissing()
    local problems = {}
    for _, e in ipairs(list) do
        if #e.missing > 0 or #e.unknown > 0 then problems[#problems + 1] = e end
    end
    if #problems == 0 then
        NS.Msg(NS.L("Appel — tout le monde est prêt |cff22c55e✓|r (flacon / bouffe / huile / buffs OK)"))
        return
    end
    NS.Msg(NS.L("Appel — préparation du groupe :"))
    for _, e in ipairs(problems) do
        local who = ("%s%s|r%s"):format(
            ClassColor(e.class), e.name,
            e.isMe and (" |cff8b8fa3" .. NS.L("(toi)") .. "|r") or "")
        local parts = {}
        if #e.missing > 0 then
            parts[#parts + 1] = NS.L("|cffea4850manque|r ") .. table.concat(e.missing, ", ")
        end
        if #e.unknown > 0 then
            parts[#parts + 1] = "|cff8b8fa3? " .. table.concat(e.unknown, ", ") .. "|r"
        end
        print(("   %s : %s"):format(who, table.concat(parts, "  ·  ")))
    end
end

-- ---------- Événement READY_CHECK ----------
local rcFrame = CreateFrame("Frame")
rcFrame:RegisterEvent("READY_CHECK")
local rcPending
rcFrame:SetScript("OnEvent", function()
    -- 1) rafraîchir MON état (flacon/bouffe/huile) et le rediffuser au
    --    groupe → les autres addons voient mes buffs à jour, et moi les leurs
    if NS.db and NS.db.myProfile and Buffs.SelfFlags then
        local ok, flags = pcall(Buffs.SelfFlags)
        if ok then NS.db.myProfile.flags = flags end
    end
    -- la diffusion de MES flags est TOUJOURS faite (mes amis me voient
    -- même si je choisis de ne pas afficher le rapport chez moi)
    if NS.Sync and NS.Sync.available and NS.Sync.AnnounceGroup then
        pcall(NS.Sync.AnnounceGroup)
    end
    -- 2) rapport chat local : seulement si la feature est activée
    if NS.db and NS.db.appelReport == false then return end
    if rcPending then rcPending:Cancel() end
    rcPending = C_Timer.NewTimer(1.2, function()
        rcPending = nil
        local ok, err = pcall(Buffs.PrintReadyCheckReport)
        if not ok and NS.Msg then NS.Msg(NS.L("check appel indisponible (") .. tostring(err) .. ")") end
    end)
end)
