-- test_rep_observer.lua
-- ============================================================================
-- The live-rep pattern (fix for the C1 baked-rep-staleness finding):
--   UPDATE_FACTION / renown change -> RepObserver:OnRepChanged
--     -> REP_PROGRESS_TICK -> session.resolvers.rep.tick++ -> rep-gate selectors re-run
--     -> selector calls HDG.RepObserver:GetProgress (live) + ComposeRepProgressSuffix
-- Verifies the tick reducer, the observer declaration + handler, GetProgress
-- against the mock rep APIs, and the suffix formatter.

dofile("wow_mock.lua")
dofile("../Core/HDGR_Constants.lua")
dofile("../Core/HDGR_Actions.lua")
dofile("../Core/HDGR_Resolvers.lua")
dofile("../Core/HDGR_Format.lua")
dofile("../Core/HDGR_Log.lua")
dofile("../Core/HDGR_StoreFurnishings.lua")
dofile("../Core/HDGR_Store.lua")

-- Stub the Modules engine (RepObserver self-registers + registers logTags at load).
HDG.Modules = HDG.Modules or {}
HDG.Modules._decls = {}
function HDG.Modules:Declare(def)
    self._decls[def.name] = def
    if def and type(def.logTags) == "table" then HDG.Log:RegisterTags(def.logTags) end
end
HDG.Selectors = HDG.Selectors or {}
function HDG.Selectors:InvalidateMemos(_) end

dofile("../Modules/HDGR_RepObserver.lua")
HDG.Store:LoadFromSavedVariables()

local A = HDG.Constants.ACTIONS

-- ===== 1. REP_PROGRESS_TICK reducer bumps session.resolvers.rep.tick =================
assert(HDG.Store:GetState().session.resolvers.rep.tick == 0, "rep tick starts at 0")
HDG.Store:Dispatch({ type = A.REP_PROGRESS_TICK })
assert(HDG.Store:GetState().session.resolvers.rep.tick == 1, "REP_PROGRESS_TICK bumps tick to 1")

-- ===== 2. Module declaration + handler =====================================
local def = HDG.Modules._decls["RepObserver"]
assert(def, "RepObserver declared")
-- §13: this module owns the rep namespaces (catalog observer no longer does).
local owns = {}
for _, ns in ipairs(def.ownsBlizzardNamespaces or {}) do owns[ns] = true end
assert(owns["C_Reputation"] and owns["C_MajorFactions"] and owns["C_GossipInfo"],
    "RepObserver owns C_Reputation / C_MajorFactions / C_GossipInfo")
assert(def.blizzardEvents.UPDATE_FACTION, "listens for UPDATE_FACTION")
assert(def.blizzardEvents.MAJOR_FACTION_RENOWN_LEVEL_CHANGED, "listens for renown change")
-- handler dispatches the tick
def.OnRepChanged(def)
assert(HDG.Store:GetState().session.resolvers.rep.tick == 2, "OnRepChanged dispatches REP_PROGRESS_TICK")

-- ===== 3. GetProgress against the mock rep APIs ============================
assert(HDG.RepObserver:GetProgress(nil) == nil, "GetProgress(nil) -> nil")
-- default mock: IsMajorFaction false, GetFactionDataByID nil -> no progress
assert(HDG.RepObserver:GetProgress(99999) == nil, "GetProgress(unknown) -> nil under default mock")

-- standard path with mocked faction data: Friendly(5), gate requires Honored(6)
_G.C_Reputation.GetFactionDataByID = function(id)
    if id == 42 then
        return { reaction = 5, currentReactionThreshold = 0,
                 nextReactionThreshold = 21000, currentStanding = 12000 }
    end
end
local p = HDG.RepObserver:GetProgress(42, 6)
assert(p and p.type == "standard", "standard progress shape")
assert(p.met == false, "Friendly(5) does NOT meet Honored(6)")
assert(p.current == 12000 and p.max == 21000, "current/max within band")

-- ===== 4. ComposeRepProgressSuffix formatting ==============================
assert(HDG.Format.ComposeRepProgressSuffix(nil) == nil, "nil progress -> nil suffix")
local met = HDG.Format.ComposeRepProgressSuffix({ met = true, label = "Honored" })
assert(met:find("Honored", 1, true), "met suffix carries the label")
local unmet = HDG.Format.ComposeRepProgressSuffix(
    { met = false, label = "Friendly", current = 12000, max = 21000 })
assert(unmet:find("(12000/21000)", 1, true), "unmet suffix carries (cur/max)")

io.write("test_rep_observer: PASS\n")
