-- test_layoutconfig_smoke.lua
-- ============================================================================
-- Run Layout:Validate on the shipped LayoutConfig (Default + Canvas + Live)
-- and assert zero errors. Catches regressions where a new panel/section/widget
-- breaks the spec contract (missing cell map key, dangling parent, sub-table
-- mis-keying, widget kind not registered, etc.).
--
-- Layout:Validate is invoked at MainFrame:Build time -- without this test, a
-- malformed config would only surface when the user opens the addon (not CI).

---@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()

dofile("../Modules/VDS_StaticData.lua")
dofile("../UI/VDS_Selectors.lua")
dofile("../UI/VDS_WidgetTypes.lua")
dofile("../UI/VDS_Theme.lua")
dofile("../UI/VDS_Palette.lua")
dofile("../UI/VDS_Components.lua")
dofile("../UI/VDS_Components_Custom.lua")
dofile("../UI/VDS_Components_CanvasScope.lua")
dofile("../UI/VDS_Components_CanvasCategory.lua")
dofile("../UI/VDS_Components_CanvasLoadouts.lua")
-- Harmony engine + Components: harmonyBand kind must be registered before Validate.
dofile("../Core/VDS_ColorMath.lua")
dofile("../Core/VDS_HarmonyWheel.lua")
dofile("../Core/VDS_Harmony.lua")
dofile("../UI/VDS_Components_Harmony.lua")
dofile("../UI/VDS_BindingEngine.lua")
dofile("../UI/VDS_Layout.lua")
dofile("../UI/VDS_LayoutNormalize.lua")

-- Seal the WidgetTypes registry now that every kind has been registered.
-- The Layout validator's closed-schema check reads kindDef.specFields off
-- the registry; without Flush, _registry is empty and the check silently
-- no-ops.
VDS.WidgetTypes:Flush()

-- Tooltip recipes registry: required because Layout:Validate checks
-- `VDS.TooltipRecipes[spec.tooltip.recipe] ~= nil` for every widget that
-- declares tooltip = { recipe = "Name" }. Without this load the registry
-- is empty and every recipe-referenced widget fails validation.
-- Mirrors TOC load order (TooltipRecipes loads before LayoutConfig).
dofile("../Core/VDS_TooltipRecipes.lua")

-- LayoutConfig: main + per-view extensions. Order matters (main first, then
-- per-view files that extend it).
local function tryDofile(path)
    local f = io.open(path, "r")
    if f then f:close(); dofile(path); return true end
    return false
end
dofile("../UI/VDS_LayoutConfig.lua")
tryDofile("../UI/VDS_LayoutConfig_Default.lua")
tryDofile("../UI/VDS_LayoutConfig_Canvas.lua")
tryDofile("../UI/VDS_LayoutConfig_Live.lua")
tryDofile("../UI/VDS_LayoutConfig_Warehouse.lua")

local function assertTrue(v, label) if not v then error(label, 2) end end
local function hasMatch(errs, pat)
    for _, e in ipairs(errs) do
        if e:find(pat, 1, true) then return true end
    end
    return false
end

-- ===== 1. Shipped LayoutConfig MUST validate clean =====
local errors = VDS.Layout:Validate(VDS.LayoutConfig)
if errors and #errors > 0 then
    print("LayoutConfig validation errors:")
    for _, e in ipairs(errors) do print("  - " .. tostring(e)) end
    error("Layout:Validate(VDS.LayoutConfig) reported " .. #errors
        .. " errors -- shipped config must validate clean", 2)
end

-- ===== 2. Sub-table mis-keying audit (HouseTab/Trainers bug class) =====
do
    -- Widget with kind="scrollbox" placed in config.panels: must flag.
    local badCfg1 = {
        window = VDS.LayoutConfig.window,
        panels = {
            myPanel = { kind = "panel", cell = {} },
            oops    = { kind = "scrollbox", ["in"] = "myPanel" },
        },
        sections = {}, widgets = {},
    }
    local errs1 = VDS.Layout:Validate(badCfg1)
    assertTrue(hasMatch(errs1, "scrollbox") or hasMatch(errs1, "has `in`"),
        "mis-keyed widget (kind=scrollbox) in panels must be flagged")

    -- Panel with `in` field: must flag.
    local badCfg2 = {
        window = VDS.LayoutConfig.window,
        panels = {
            myPanel = { kind = "panel", cell = {} },
            orphan  = { ["in"] = "myPanel" },
        },
        sections = {}, widgets = {},
    }
    local errs2 = VDS.Layout:Validate(badCfg2)
    assertTrue(hasMatch(errs2, "has `in`"),
        "panel with `in` field must be flagged")

    -- Section with `kind` field: must flag.
    local badCfg3 = {
        window = VDS.LayoutConfig.window,
        panels = {
            myPanel = { kind = "panel", cell = {} },
        },
        sections = {
            oops = { kind = "label", ["in"] = "myPanel" },
        },
        widgets = {},
    }
    local errs3 = VDS.Layout:Validate(badCfg3)
    assertTrue(hasMatch(errs3, "kind"),
        "mis-keyed widget (kind=label) in sections must be flagged")

    -- Widget spec with `cell` field: must flag.
    local badCfg4 = {
        window = VDS.LayoutConfig.window,
        panels = {
            myPanel = { kind = "panel", cell = {} },
        },
        sections = {},
        widgets = {
            oops = { cell = {} },
        },
    }
    local errs4 = VDS.Layout:Validate(badCfg4)
    assertTrue(hasMatch(errs4, "cell is panel placement"),
        "mis-keyed panel (has cell) in widgets must be flagged")
end

-- ===== 3. Unknown widget kind must be flagged =====
do
    local badCfg = {
        window = VDS.LayoutConfig.window,
        panels = {
            myPanel = { kind = "notARealKind", cell = {} },
        },
        sections = {}, widgets = {},
    }
    local errs = VDS.Layout:Validate(badCfg)
    assertTrue(hasMatch(errs, "notARealKind") or hasMatch(errs, "WidgetType"),
        "unknown widget kind must be flagged")
end

-- ===== 4. Missing window shape must be flagged =====
do
    local badCfg = { panels = {}, sections = {}, widgets = {} }
    local errs = VDS.Layout:Validate(badCfg)
    assertTrue(hasMatch(errs, "window"),
        "missing config.window must be flagged")
end

-- ===== 5. Sanity: shipped LayoutConfig structure =====
assertTrue(VDS.LayoutConfig.window ~= nil, "shipped config has window")
assertTrue(type(VDS.LayoutConfig.window.views) == "table", "window.views is a table")
assertTrue(VDS.LayoutConfig.window.defaultView ~= nil, "defaultView declared")

-- ===== 6. Inventory check: shipped config has reasonable panel/widget counts =====
local panelCount, widgetCount, sectionCount = 0, 0, 0
for _ in pairs(VDS.LayoutConfig.panels or {}) do panelCount = panelCount + 1 end
for _ in pairs(VDS.LayoutConfig.widgets or {}) do widgetCount = widgetCount + 1 end
for _ in pairs(VDS.LayoutConfig.sections or {}) do sectionCount = sectionCount + 1 end
assertTrue(panelCount >= 3,  "expected >= 3 panels, got " .. panelCount)
assertTrue(widgetCount >= 10, "expected >= 10 widgets, got " .. widgetCount)

-- ===== 7. Closed-schema: typo'd field on a widget MUST be flagged =====
do
    local badCfg = {
        window   = VDS.LayoutConfig.window,
        panels   = { fooPanel = { kind = "frame", cell = {} } },
        sections = {},
        widgets  = {
            -- "placholder" is a typo for "placeholder" -- and even
            -- "placeholder" isn't in the label spec; the field shouldn't
            -- exist at all. Validator must catch.
            oops = {
                kind = "label", ["in"] = "fooPanel", order = 10,
                placholder = "x",
            },
        },
    }
    local errs = VDS.Layout:Validate(badCfg)
    assertTrue(hasMatch(errs, "placholder"),
        "typo'd field on widget must be flagged by closed-schema check")
end

-- ===== 8. Retracted pattern (formatFn) MUST be flagged =====
do
    local badCfg = {
        window   = VDS.LayoutConfig.window,
        panels   = { fooPanel = { kind = "frame", cell = {} } },
        sections = {},
        widgets  = {
            -- formatFn is a retracted pattern (ADR-024 corrected) -- the
            -- selector returns the final string, no formatFn closure in
            -- the LayoutConfig. Validator must catch.
            oops = {
                kind = "label", ["in"] = "fooPanel", order = 10,
                formatFn = function(d) return tostring(d) end,
            },
        },
    }
    local errs = VDS.Layout:Validate(badCfg)
    assertTrue(hasMatch(errs, "formatFn"),
        "formatFn on widget (retracted pattern) must be flagged by closed-schema check")
end

-- ===== 9. Every registered widget kind MUST declare specFields =====
do
    local missing = {}
    for kind, def in pairs(VDS.WidgetTypes._registry) do
        if def.specFields == nil then
            missing[#missing + 1] = kind
        end
    end
    assertTrue(#missing == 0,
        "every WidgetType must declare specFields (even if empty). Missing: "
        .. table.concat(missing, ", "))
end

print(string.format(
    "test_layoutconfig_smoke.lua passed (shipped config clean: %d panels, %d sections, %d widgets; 4 mis-keying patterns flagged; closed-schema active)",
    panelCount, sectionCount, widgetCount))
