-- test_first_paint.lua
-- ============================================================================
-- First-paint correctness for HDG — every registered selector must return
-- without error against the freshly-booted default state.
--
-- LESSON (VDS 2026-05): a selector that depends on a slot the reducer
-- hasn't initialized yet erupts on BindingEngine's initial apply pass.
-- For 353 selectors across 12 selector files, this test is the only way
-- to verify the full surface survives first paint without manually
-- exercising every binding in-game.
--
-- Empty-ctx semantics: calls each selector with ctx = {}. Selectors that
-- genuinely require ctx (e.g. acq.selected.headerLine reads ctx.itemID)
-- should return nil/"" gracefully rather than error. A selector that
-- erupts here = either a missing default-state slot OR an unsafe ctx
-- access (`ctx.itemID:GetText()` without nil-guard).

-- wow_mock.lua silences _G.print to keep addon logs out of test output.
-- Capture real print BEFORE loading the mock so this test's diagnostics
-- reach the terminal.
local _realPrint = print
local function out(s) _realPrint(s) end

dofile("wow_mock.lua")

-- ===== Load engine =========================================================
dofile("../Core/HDGR_SchemeConstants.lua")
dofile("../Core/HDGR_Constants.lua")
dofile("../Core/HDGR_Actions.lua")
dofile("../Core/HDGR_Resolvers.lua")
dofile("../Core/HDGR_Format.lua")
dofile("../Core/HDGR_Paths.lua")
dofile("../Core/HDGR_Log.lua")
dofile("../Core/HDGR_Memo.lua")
dofile("../Core/HDGR_TableUtils.lua")
dofile("../Core/HDGR_StoreFurnishings.lua")
dofile("../Core/HDGR_Store.lua")
dofile("../Core/HDGR_Middleware.lua")
dofile("../Core/HDGR_Environment.lua")
dofile("../Core/HDGR_BlizzardEvents.lua")
dofile("../Core/HDGR_Modules.lua")
dofile("../Core/HDGR_WidgetTypes.lua")
dofile("../Core/HDGR_Expansion.lua")
dofile("../Core/HDGR_Locale.lua")
dofile("../Core/HDGR_TagData.lua")

-- ===== Load LayoutConfig (chrome.activeTab needs window.views) =============
dofile("../UI/HDGR_LayoutConfig.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Decor.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Acquisition.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Recipes.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Alts.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Mogul.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Styles.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Trainers.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_HouseTab.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Debug.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Config.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Data.lua")
-- Window infra needs to load BEFORE LayoutConfig_Lumber because Lumber's
-- HDG.Window:Register call is at file-load time.
dofile("../UI/HDGR_Window.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Lumber.lua")
-- Nav LayoutConfig: references NAV_TREE (already loaded). Selectors used by
-- nav bindings (nav.isActive_*, nav.isLeafActive_*) load via Selectors_Status.
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Nav.lua")

-- ===== Theme + Selectors ==================================================
dofile("../UI/HDGR_Theme.lua")
dofile("../UI/HDGR_Selectors.lua")
dofile("../UI/HDGR_CategoryNav.lua")
dofile("../UI/HDGR_Selectors_Status.lua")
-- Phase 3a: decor.allItems now calls HousingCatalogObserver:IsReady().
-- Stub the observer so selectors can load; IsReady() returns false on cold
-- state, which means decor.allItems returns {} (correct first-paint value).
HDG.HousingCatalogObserver = HDG.HousingCatalogObserver or {
    byItemID     = {},
    tagIDToGroup = {},
    IsReady           = function() return false end,
    GetRow            = function() return nil end,
    GetIcon           = function() return nil end,
    GetExpansionForItem = function() return nil end,
    IterateRows       = function() end,
}
dofile("../UI/HDGR_Selectors_Decor.lua")
-- See test_acquisition_smoke for stub rationale (cookbook 10).
HDG.ItemNameResolver = HDG.ItemNameResolver or {
    ResolveIcon  = function() return nil end,
    ResolveName  = function() return nil end,
    GetSellPrice = function() return 0 end,   -- mogul.plan vendor-sell fallback (ADR-003b)
}
dofile("../UI/HDGR_Selectors_Acquisition.lua")
dofile("../UI/HDGR_Selectors_Recipes.lua")
dofile("../UI/HDGR_Selectors_Config.lua")
dofile("../UI/HDGR_Selectors_Alts.lua")
dofile("../UI/HDGR_Selectors_Mogul.lua")
dofile("../UI/HDGR_Selectors_Styles.lua")
dofile("../UI/HDGR_Selectors_Trainers.lua")
dofile("../UI/HDGR_Selectors_House.lua")
-- data selectors: need AchievementObserver stub; house.snapshot loaded above.
HDG.AchievementObserver = HDG.AchievementObserver or {
    IsEarned    = function() return false end,
    GetCriteria = function() return nil end,
}
dofile("../UI/HDGR_Selectors_Data.lua")
dofile("../UI/HDGR_Selectors_Companion.lua")
dofile("../UI/HDGR_Selectors_Lumber.lua")

-- ===== Data + StaticData ==================================================
-- Load every data file the selectors might touch. Production TOC loads all
-- of these before selectors run; the test must mirror that to avoid false
-- "missing DB" failures (which are test-setup gaps, not selector bugs).
dofile("../data/HDGR_DecorDB.lua")
HDGR_AllDecorDB = {}  -- stub: deprecated DB archived; test does not assert on its content
HDGR_VendorDB = {}  -- stub: deprecated DB archived; test does not assert on its content
dofile("../data/HDGR_ProfessionsDB.lua")
dofile("../data/HDGR_TrainersDB.lua")
dofile("../data/HDGR_StyleDefinitions.lua")
dofile("../data/HDGR_CollectionDefinitions.lua")
dofile("../data/HDGR_DecorNotes.lua")
dofile("../data/HDGR_FacetDB.lua")
dofile("../data/HDGR_HouseTab_WidgetDefaults.lua")
dofile("../data/HDGR_HouseTab_TitleTiers.lua")
dofile("../data/HDGR_HouseTab_DailyTitles.lua")
dofile("../data/HDGR_ProfessionThresholds.lua")
dofile("../data/HDGR_ReagentsDB.lua")
dofile("../Modules/HDGR_StaticData.lua")
-- Modules referenced by selectors (PriceSource for goblin.*, Mogul for
-- mogul.*, PowerCrafter for recipes.craftOrderRows, SessionIdentity for
-- alts/trainers selectors that read the current char tuple, BagObserver
-- for queue lumber/material rows).
dofile("../Modules/HDGR_SessionIdentity.lua")
dofile("../Modules/HDGR_PriceSource.lua")
dofile("../Modules/HDGR_BagObserver.lua")
dofile("../Modules/HDGR_PowerCrafter.lua")
dofile("../Modules/HDGR_Mogul.lua")
dofile("../Modules/HDGR_Goblin.lua")

-- ===== Boot default state =================================================
HDG_DB = {}
HDG.Store:LoadFromSavedVariables()
local state = HDG.Store:GetState()

-- ============================================================================
-- Iterate every selector, call with default state + empty ctx. Collect
-- failures so we see the full picture in one pass.
-- ============================================================================

local registry = HDG.Selectors:GetRegistry()
local selectorNames = {}
for name in pairs(registry) do selectorNames[#selectorNames + 1] = name end
table.sort(selectorNames)

local failures = {}
local successes = 0
for _, name in ipairs(selectorNames) do
    local ok, err = pcall(HDG.Selectors.Call, HDG.Selectors, name, state, {})
    if ok then
        successes = successes + 1
    else
        failures[#failures + 1] = string.format("  %s: %s", name, tostring(err))
    end
end

out(string.format("HDG first-paint: %d/%d selectors survived empty-ctx call",
    successes, #selectorNames))

if #failures > 0 then
    out("FAILURES (selectors that errored on default state + empty ctx):")
    for i = 1, math.min(#failures, 50) do out(failures[i]) end
    if #failures > 50 then out(string.format("  ... and %d more failures", #failures - 50)) end
    out()
    out("Each failure is either a missing default-state slot OR an unsafe")
    out("ctx access. Fix by initializing the slot in HDGR_Store.lua's")
    out("default factory, OR by returning nil/'' gracefully when ctx is empty.")
    error(string.format("test_first_paint: %d selectors failed first-paint check", #failures), 0)
end

out("test_first_paint.lua passed")
