-- test_state_factories.lua
-- ============================================================================
-- State-factory completeness. NewAccount() and NewSession() must produce
-- every slot the reducer / selectors expect, so strict reads work the moment
-- LoadFromSavedVariables returns.
--
-- LESSON (VFN c80fef41): first paint errored because a selector read
-- state.account.collections.foo but NewAccount didn't create the
-- collections slot. The strict-read discipline makes this a fatal error,
-- so the factory MUST be exhaustive.

---@diagnostic disable: undefined-global
dofile("wow_mock.lua")
dofile("../Core/VDS_Constants.lua")
dofile("../Core/VDS_SchemeConstants.lua")
dofile("../Core/VDS_Paths.lua")
dofile("../Core/VDS_Log.lua")
dofile("../Core/VDS_Store.lua")
dofile("../Core/VDS_ActionMeta.lua")
VDS.Store:ValidateActionMetaExhaustiveness()
VDS_DB = {}
VDS.Store:LoadFromSavedVariables()

local function check(path, value, label)
    if value == nil then error("missing slot: " .. path .. " (" .. label .. ")", 2) end
end
local function checkType(path, value, want, label)
    if type(value) ~= want then
        error("wrong type at " .. path .. ": expected " .. want .. ", got " .. type(value)
              .. " (" .. label .. ")", 2)
    end
end

print("State factories:")

-- ===== NewAccount: persisted fields from VDS_CONVERSION_SPEC.md =====
local A = VDS.Store:GetState().account

checkType("account.config",            A.config,            "table",  "config present")
checkType("account.ui",                A.ui,                "table",  "ui present")
-- panelPosition is nil by default per donor; verify the slot can be nil:
if A.panelPosition ~= nil then
    checkType("account.panelPosition", A.panelPosition,    "table",  "panelPosition if set")
end
checkType("account.minimap",           A.minimap,           "table",  "minimap present")
checkType("account.dyeableItems",      A.dyeableItems,      "table",  "dyeableItems present")
checkType("account.customThemes",      A.customThemes,      "table",  "customThemes present")
-- account.cart removed: Send-to-AH reads stock.buy.cart / stock.herbs.list directly.
if A.cart ~= nil then error("account.cart should be removed (selector-derived now)") end
checkType("account.canvas",            A.canvas,            "table",  "canvas present")
checkType("account.live",              A.live,              "table",  "live present")
checkType("account.migratedFrom",      A.migratedFrom,      "table",  "migratedFrom present")
checkType("account.warehouse",         A.warehouse,         "table",  "warehouse present")
check("account.schemaVersion",         A.schemaVersion,     "schemaVersion present")
print("  [OK] account: top-level slots present (cart removed)")

-- ===== NewAccount.config sub-fields =====
check("account.config.debug",            A.config.debug,            "config.debug")
check("account.config.scheme",           A.config.scheme,           "config.scheme")
check("account.config.replaceBlizzard",  A.config.replaceBlizzard,  "config.replaceBlizzard")
check("account.config.ownedOnly",        A.config.ownedOnly,        "config.ownedOnly")
print("  [OK] account.config: 4 fields (debug, scheme, replaceBlizzard, ownedOnly)")

-- ===== Canvas / live nested (cart removed) =====
checkType("account.canvas.loadouts",   A.canvas.loadouts,   "table", "canvas.loadouts")
check("account.canvas.autoSave",       A.canvas.autoSave,   "canvas.autoSave")
check("account.canvas.previewDetached", A.canvas.previewDetached, "canvas.previewDetached")

checkType("account.live.recentDyes",   A.live.recentDyes,   "table", "live.recentDyes")
check("account.live.autoSave",         A.live.autoSave,     "live.autoSave")

-- migratedFrom.vds (HDG migration intentionally dropped per spec amendment)
check("account.migratedFrom.vds",      A.migratedFrom.vds,  "migratedFrom.vds")
print("  [OK] account nested: canvas/live/migratedFrom sub-fields present")

-- ===== NewSession: per VDS_CONVERSION_PLAN.md =====
local S = VDS.Store:GetState().session

checkType("session.ui",       S.ui,       "table", "session.ui present")
checkType("session.bag",      S.bag,      "table", "session.bag present")
checkType("session.identity", S.identity, "table", "session.identity present")
checkType("session.catalog",  S.catalog,  "table", "session.catalog present (ADR-001)")
checkType("session.dyes",     S.dyes,     "table", "session.dyes present")
checkType("session.log",      S.log,      "table", "session.log present")
checkType("session.combat",   S.combat,   "table", "session.combat present")
checkType("session.stock",    S.stock,    "table", "session.stock present")
print("  [OK] session: 8 top-level slots present")

-- session.catalog.tick (CATALOG_READY invalidation seam)
check("session.catalog.tick", S.catalog.tick, "session.catalog.tick is the CATALOG_READY seam")

-- session.ui.canvas + session.ui.live
local C = S.ui.canvas
checkType("session.ui.canvas",       C,           "table", "session.ui.canvas present")
checkType("session.ui.canvas.dyes",  C.dyes,      "table", "session.ui.canvas.dyes present")
check    ("session.ui.canvas.activeChannel", C.activeChannel, "canvas.activeChannel")
check    ("session.ui.canvas.numChannels",   C.numChannels,   "canvas.numChannels")
check    ("session.ui.canvas.pickerMode",    C.pickerMode,    "canvas.pickerMode")
check    ("session.ui.canvas.pickerSearch",  C.pickerSearch,  "canvas.pickerSearch")

local L = S.ui.live
checkType("session.ui.live",          L,             "table",   "session.ui.live present")
check    ("session.ui.live.active",   L.active,      "live.active")
checkType("session.ui.live.originalDyes", L.originalDyes, "table", "live.originalDyes")
print("  [OK] session.ui nested: canvas + live sub-shapes present")

-- ===== Filter / sort slots default to nil (intentional) but the parent UI table is present =====
-- The reducer relies on these BEING readable, not having values; verify the
-- parent table is non-nil. (A strict read of a nil sub-field is OK -- the
-- bug is reading from a NIL parent table.)
check("session.ui.filterMode",     S.ui.filterMode,     "session.ui.filterMode default")
check("session.ui.activeTab",      S.ui.activeTab,      "session.ui.activeTab default")
check("session.ui.searchText",     S.ui.searchText,     "session.ui.searchText default")
print("  [OK] session.ui default fields: filterMode/activeTab/searchText")

print("test_state_factories.lua passed")
