---@diagnostic disable: undefined-field, undefined-global
---@type string
local AddonName = ...
---@class Private
local Private = select(2, ...)

-- Shipped (packaged-build) live-raid-state accessor (SNAP-01): the single source
-- of truth for "who is in the raid right now, and what are they". Byte-identical
-- relocation of RosterArrange.lua's CanonicalizeLiveName/SnapshotRaidStatus --
-- no new logic and no behavioral change beyond three additive fields (isAssist/
-- raidIndex/unit). Loads after RosterKeys.lua (whose CanonicalKey/GetSelfRealm it
-- derives every key through) and before the dev-only Roster stack, outside the
-- do-not-package boundary below, so every shipped file -- the Summoning Window
-- and Raid Inspect page included -- can read class/role/online/subgroup/lead
-- without depending on the battletag-privacy-scoped dev tier. Pure functions
-- plus one chat diagnostic only -- no Ace/UI/comm dependency, no SavedVars
-- read/write, and nothing from the fenced alt-data tables.

-- Splits a live GetRaidRosterInfo name on the FIRST hyphen into (name, realm);
-- a bare same-realm name has no hyphen, so it falls back to the presser's own
-- realm. Guarded so a nil/empty rawName cannot error -- returns the
-- CanonicalKey (RosterKeys.lua) of an empty name/realm rather than raising.
-- Promoted from RosterArrange.lua's file-local so both the shipped tier and the
-- dev-only arrange engine share one implementation (SNAP-02).
function Private:CanonicalizeLiveName(rawName)
    if not rawName or rawName == "" then
        return Private:CanonicalKey("", "")
    end
    local name, realm = rawName:match("^(.-)%-(.+)$")
    if not name then
        name, realm = rawName, Private:GetSelfRealm()
    end
    return Private:CanonicalKey(name, realm)
end

-- Live per-player raid-status accessor (SNAP-01). One fresh GetRaidRosterInfo
-- sweep, keyed by Private:CanonicalizeLiveName, carrying the fuller field set
-- consumers need to derive status badges/class-color/role-icon: fileName is the
-- 6th return, combatRole the 12th (confirmed wow_api_wiki.txt:6481) -- note that
-- the 10th return is the MAINTANK/MAINASSIST raid role, NOT the combat role, and
-- is deliberately not what `role` holds here. Do not "fix" it to the 10th. A
-- member absent from the returned map is, by definition, not in the raid --
-- consumers treat a missing key as not-in-raid and fall back to
-- RosterAlts.charInfo (DATA-02). Returns the raw fileName/combatRole strings;
-- color/icon resolution is the caller's job (Theme.GetClassColor/GetIconPath),
-- not this accessor's.
--
-- GetRaidRosterInfo's own `online` return is unreliable for connected-realm raid
-- members -- Blizzard's API can report it false for a player on a different
-- sub-realm within the same connected realm even though they are fully online
-- and in the raid (the exact "player card darkened despite being in the raid"
-- symptom). UnitIsConnected(unit) is the accurate signal, so it's OR'd in.
--
-- STALENESS CONTRACT: `raidIndex` and `unit` are snapshot-time values only.
-- SetRaidSubgroup/SwapRaidSubgroup re-enumerate raid indices as a side effect
-- (RosterArrange.lua's move primitives do exactly this), so a caller that
-- mutates the raid MUST re-snapshot rather than cache those two fields. Every
-- other field is safe to hold across a mutation.
--
-- The result is a MAP keyed by canonical key, not a list, and iteration over it
-- is unspecified Lua hash order -- any consumer needing a stable order must sort
-- for itself. Solo / not in a raid returns an EMPTY TABLE, never nil.
function Private:SnapshotRaidStatus()
    local status = {}
    local numMembers = GetNumGroupMembers() or 0
    for i = 1, numMembers do
        local name, rank, subgroup, _, _, fileName, _, online, _, _, _, combatRole = GetRaidRosterInfo(i)
        if name then
            local unit = "raid" .. i
            status[Private:CanonicalizeLiveName(name)] = {
                inRaid = true,
                online = (online or UnitIsConnected(unit)) and true or false,
                subgroup = subgroup,
                class = fileName,
                role = combatRole,
                isLead = rank == 2, -- GetRaidRosterInfo rank: 2 = leader, 1 = assistant, 0 = member
                isAssist = rank == 1,
                raidIndex = i, -- snapshot-time only; see the contract above
                unit = unit, -- snapshot-time only; see the contract above
            }
        end
    end
    return status
end

-- Dev diagnostic (SNAP-01 proof). Chat print only -- no frame, no widget, no
-- SavedVars, no comm, so this file's zero-dependency contract holds.
-- Deliberately available in packaged builds: proving the snapshot resolves with
-- the dev-only Roster stack absent is the whole point of the command.
--
-- Leads with a count line so a truncated chat frame still shows the total. The
-- per-member lines are sorted by raidIndex ascending -- that ordering is this
-- command's own choice, not a property of the returned map (which carries no
-- order guarantee at all); without it two dumps of the same unchanged raid can
-- print in different orders, which makes side-by-side comparison useless. Every
-- interpolated value goes through tostring() into a %s field -- a player name is
-- externally-influenced text and is never concatenated into a format string.
function Private:DumpRaidSnapshot()
    local status = Private:SnapshotRaidStatus()
    local keys = {}
    for key in pairs(status) do
        keys[#keys + 1] = key
    end
    table.sort(keys, function(a, b)
        return (status[a].raidIndex or 0) < (status[b].raidIndex or 0)
    end)
    print(("|cff99ff00Wild Things:|r raid snapshot -- %d member(s)"):format(#keys))
    for _, key in ipairs(keys) do
        local s = status[key]
        print(("|cff99ff00Wild Things:|r %s  class=%s role=%s online=%s grp=%s rank=%s idx=%s unit=%s")
            :format(tostring(key), tostring(s.class), tostring(s.role), tostring(s.online),
                tostring(s.subgroup),
                s.isLead and "LEAD" or (s.isAssist and "ASSIST" or "member"),
                tostring(s.raidIndex), tostring(s.unit)))
    end
end
