-- Over-spec smoke: runs Layout:Compute against every view in the shipped
-- LayoutConfig and asserts that the over-spec validator emits zero
-- warnings. Catches the class of bug where a fixed-height row + child
-- heights sum to more than the parent slot offers (the validator's
-- "X available but children need Y" message in HDGR_Layout.lua:481).
--
-- Without this test, over-spec regressions only show up at runtime via
-- HDG.Log:Warn("layout", ...) entries that fire once per (container,
-- axis) key and then dedupe -- easy to miss when a CI run doesn't
-- include a /reload + visual sweep.

-- FULL-BOOT SETUP (mirrors test_first_paint.lua). This test computes EVERY
-- view, and Layout:Compute evaluates every widget's `visible` selector during
-- buildChildIndex -- including cross-view selectors that compose the
-- HousingCatalogObserver vendor walk (acq.allVendors) and the Professions DB
-- (recipes.*). Production TOC loads all of these before selectors run; the
-- test must mirror that or it faults with "missing DB" / nil-observer errors
-- that are test-setup gaps, NOT selector bugs (see test_first_paint:96-99).
-- An under-provisioned setup here crashed the acquisition view (pairs()-order
-- flaky: observer-nil OR ProfessionsDB-missing) and masked the real over-spec
-- check this test exists for.
dofile("wow_mock.lua")

-- ===== Core 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_TooltipEngine.lua")
dofile("../Core/HDGR_TagData.lua")

-- ===== LayoutConfig (all 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")
-- Window infra must load before LayoutConfig_Lumber (Lumber's Window:Register
-- runs at file-load time).
dofile("../UI/HDGR_Window.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Lumber.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Projects.lua")
dofile("../UI/LayoutConfig/HDGR_LayoutConfig_Nav.lua")

-- ===== Theme + Selectors (all view selector files) ========================
dofile("../UI/HDGR_Theme.lua")
dofile("../UI/HDGR_Selectors.lua")
dofile("../UI/HDGR_CategoryNav.lua")
dofile("../UI/HDGR_Selectors_Status.lua")
-- Stub the catalog observer (IsReady() false on cold state -> acq.allVendors
-- returns {}). Selectors index HDG.HousingCatalogObserver, a TOC-load
-- singleton; the test must provide it (NOT guard the selector).
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")
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")
dofile("../UI/HDGR_Selectors_Companion.lua")
dofile("../UI/HDGR_Selectors_Lumber.lua")

-- ===== Layout engine (this test computes layouts; first_paint doesn't) =====
dofile("../UI/HDGR_Components.lua")
dofile("../UI/HDGR_Layout.lua")
dofile("../UI/HDGR_BindingEngine.lua")

-- ===== Data + StaticData ==================================================
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 ====================================
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")

-- Initialize Store so visibility selectors that read state don't fault.
HDG_DB = {}
HDG.Store:LoadFromSavedVariables()
local state = HDG.Store:GetState()

-- Capture Log:Warn("layout", ...) calls. The validator routes through
-- HDG.Log:Warn under the "layout" tag, so intercepting that surface
-- catches every over-spec emission without coupling to log internals.
local capturedWarnings = {}
local origWarn = HDG.Log.Warn
HDG.Log.Warn = function(self, tag, text, metadata)
    if tag == "layout" then
        table.insert(capturedWarnings, text)
    end
    -- Still call through so any other layout warnings (anchor inconsistency,
    -- etc.) surface in the test transcript if they fire.
    return origWarn(self, tag, text, metadata)
end

-- Over-spec geometry check for EVERY view -- accumulate, never bail on the
-- first failure. The old test threw on the first over-spec view, so `config`
-- (alphabetically first) masked every view sorted after it -- including the
-- `projects*` views. Now each view's over-spec warnings are collected and the
-- whole batch is reported at the end. (Structural validation -- font roles,
-- spec fields, kind registration -- is the SEPARATE test_layoutconfig_smoke,
-- which runs Layout:Validate over the full config incl. the Projects views.)
local config   = HDG.LayoutConfig
local failures = {}

local viewsToCheck = {}
for viewName in pairs(config.window.views) do
    viewsToCheck[#viewsToCheck + 1] = viewName
end
table.sort(viewsToCheck)

for _, viewName in ipairs(viewsToCheck) do
    HDG.Layout._overSpecWarned = {}   -- reset the validator's per-(container,axis) dedupe per view
    capturedWarnings = {}
    local ok, err = pcall(function()
        HDG.Layout:Compute(config, { view = viewName, state = state, intrinsics = {} })
    end)
    if not ok then
        failures[#failures + 1] = ("compute crashed on view %q: %s"):format(viewName, tostring(err))
    else
        for _, w in ipairs(capturedWarnings) do
            if w:find("over-spec", 1, true) then
                failures[#failures + 1] = ("over-spec [%s]: %s"):format(viewName, w)
            end
        end
    end
end

HDG.Log.Warn = origWarn

-- ===== Report EVERYTHING at once ===========================================
if #failures > 0 then
    local msg = ("test_overspec_smoke: %d over-spec failure(s) across %d views:\n")
        :format(#failures, #viewsToCheck)
    for _, f in ipairs(failures) do msg = msg .. "  - " .. f .. "\n" end
    error(msg, 0)
end

print(string.format("test_overspec_smoke.lua passed (%d views checked, validate clean)", #viewsToCheck))
