-- test_catalog_lifecycle.lua
-- Verifies RequestLoad + UI_SET_PERSISTENT subscriber lifecycle.
dofile("wow_mock.lua")
dofile("../Core/HDGR_Constants.lua")
dofile("../Core/HDGR_Format.lua")
dofile("../Core/HDGR_StoreFurnishings.lua")
dofile("../Core/HDGR_Store.lua")

-- Stub Selectors so Store:Dispatch doesn't blow up
HDG.Selectors = HDG.Selectors or {}
function HDG.Selectors:InvalidateMemos(_) end

-- Stub Modules so observer's Declare() call at file load doesn't fail
HDG.Modules = HDG.Modules or {}
HDG.Modules._decls = {}
function HDG.Modules:Declare(def)
    self._decls[def.name] = def
end

-- Stub StaticData.CatalogOverrides so BuildRow's strict :Get call has a target.
-- Returning nil = "no override for this item" (the default path the test wants).
HDG.StaticData = HDG.StaticData or {}
HDG.StaticData.CatalogOverrides = HDG.StaticData.CatalogOverrides or {
    Get = function(self, itemID) return nil end,
}

dofile("../Modules/HDGR_HousingCatalogObserver.lua")

HDG.Store:LoadFromSavedVariables()
local A = HDG.Constants.ACTIONS

local function assertEq(actual, expected, msg)
    if actual ~= expected then
        error(string.format("FAIL [%s]: expected %s got %s", msg or "?",
            tostring(expected), tostring(actual)))
    end
end

-- Stub the sweep path so RequestLoad doesn't try real WoW APIs
local sweepFired = 0
HDG.HousingCatalogObserver._RunSweep = function() sweepFired = sweepFired + 1 end

-- Initially idle -- no sweep
assertEq(sweepFired, 0, "no sweep at boot")

-- Non-catalog view does NOT trigger sweep
HDG.HousingCatalogObserver:_OnViewChange("config")
assertEq(sweepFired, 0, "config view does not trigger sweep")

-- Canonical catalog view "decor" triggers sweep (status was idle)
HDG.HousingCatalogObserver:_OnViewChange("decor")
assertEq(sweepFired, 1, "decor view triggers sweep when idle")

-- After the sweep completes (status -> ready), repeat decor view does NOT trigger
HDG.Store:Dispatch({
    type = A.CATALOG_LOAD_COMPLETED,
    payload = { loadedAt=0, itemCount=0, vendorCount=0, generation=1 },
})
HDG.HousingCatalogObserver:_OnViewChange("decor")
assertEq(sweepFired, 1, "ready state, repeat view does not re-trigger")

-- Refresh queued -> next view activation triggers Refresh (canonical name: "acquisition")
HDG.Store:Dispatch({type = A.CATALOG_REFRESH_QUEUED})
HDG.HousingCatalogObserver:_OnViewChange("acquisition")
assertEq(sweepFired, 2, "refreshPending triggers sweep on next catalog view")

-- Verify the other canonical catalog view names all pass the guard.
-- Dispatch CATALOG_LOAD_COMPLETED before each to keep refreshPending=false so
-- we don't double-count; then set refreshPending for the actual guard check.
local function viewPassesGuard(view)
    local before = sweepFired
    HDG.Store:Dispatch({ type = A.CATALOG_REFRESH_QUEUED })
    HDG.HousingCatalogObserver:_OnViewChange(view)
    return sweepFired > before
end
-- Clear refreshPending from the "acquisition" sweep above before starting guard checks.
-- CATALOG_REFRESH_COMPLETED is what clears refreshPending.
HDG.Store:Dispatch({ type = A.CATALOG_REFRESH_COMPLETED, payload = { generation=2 } })
assertEq(viewPassesGuard("shoppingList"), true, "shoppingList is a catalog-consuming view")
HDG.Store:Dispatch({ type = A.CATALOG_REFRESH_COMPLETED, payload = { generation=3 } })
assertEq(viewPassesGuard("zoneScanner"),  true, "zoneScanner is a catalog-consuming view")
HDG.Store:Dispatch({ type = A.CATALOG_REFRESH_COMPLETED, payload = { generation=4 } })
assertEq(viewPassesGuard("houseTab"),     true, "houseTab is a catalog-consuming view")

-- ===== Subscriber dispatch path (tests real tab-click wiring) ================
-- Simulate subscriber registration via onEnable. wow_mock has no Modules engine
-- to run onEnable automatically, so we wire it here.
sweepFired = 0
-- Start fresh: ready state, no refreshPending.
-- CATALOG_REFRESH_COMPLETED clears refreshPending; CATALOG_LOAD_COMPLETED does not.
HDG.Store:Dispatch({ type = A.CATALOG_REFRESH_COMPLETED, payload = { generation=5 } })
local modDef = HDG.Modules._decls["HousingCatalogObserver"]
modDef.onEnable(modDef)

-- Simulate a real tab click: dispatch UI_SET_PERSISTENT{key="view", value="decor"}.
-- This is what ChromeController:SwitchTab dispatches. The subscriber must fire
-- _OnViewChange("decor"). Force refreshPending first so Refresh() fires.
HDG.Store:Dispatch({ type = A.CATALOG_REFRESH_QUEUED })
HDG.Store:Dispatch({
    type    = A.UI_SET_PERSISTENT,
    payload = { key = "view", value = "decor" },
})
assertEq(sweepFired, 1, "UI_SET_PERSISTENT{key=view} subscriber triggers sweep")

-- Non-view key UI_SET_PERSISTENT must NOT trigger sweep when catalog is
-- in ready state with no refreshPending.
-- CATALOG_REFRESH_COMPLETED is what clears refreshPending (CATALOG_LOAD_COMPLETED does not).
HDG.Store:Dispatch({ type = A.CATALOG_REFRESH_COMPLETED, payload = { generation=6 } })
local prevSweep = sweepFired
HDG.Store:Dispatch({
    type    = A.UI_SET_PERSISTENT,
    payload = { key = "someOtherKey", value = true },
})
-- status=ready, refreshPending=false: _OnViewChange returns without sweeping
-- regardless of which UI_SET_PERSISTENT key was written.
assertEq(sweepFired, prevSweep, "UI_SET_PERSISTENT non-view key (ready+no-pending) does not trigger sweep")

print("test_catalog_lifecycle: PASS")
