-- VDS_WidgetTypes
-- ============================================================================
-- Widget-type registry. Mirrors HDGR_WidgetTypes.lua's deferred-queue +
-- Flush() + closed-schema-validation pattern.
-- Lifecycle:
--   1. File-load: Register(kind, def) enqueues (order-independent across files).
--   2. Flush() (ADDON_LOADED): validates against KNOWN_FIELDS + per-field shapes,
--      seals the registry. Errors loud (raises).
--   3. Post-Flush: Get / TryGet serve from _registry.

VDS = VDS or {}
VDS.WidgetTypes = VDS.WidgetTypes or {
    _queue    = {},
    _registry = {},
    _flushed  = false,
}

local W = VDS.WidgetTypes

-- Closed contract: unknown keys raise at Flush().
local KNOWN_FIELDS = {
    build        = true,    -- (parent, spec) -> widget. Required.
    skin         = true,    -- string Skinner role for Theme:Register.
    dispatch     = true,    -- { fields = {...}, push = function }.
    requiresFont = true,    -- (spec) -> bool. Validator predicate.
    specFields   = true,    -- { "fieldName", ... } closed-schema allow-list.
}

function W:Register(kind, def)
    if self._flushed then
        error(("VDS.WidgetTypes:Register(%q) called after Flush()"):format(tostring(kind)), 2)
    end
    if type(kind) ~= "string" or kind == "" then
        error("VDS.WidgetTypes:Register: kind must be a non-empty string", 2)
    end
    if type(def) ~= "table" then
        error(("VDS.WidgetTypes:Register(%q): def must be a table"):format(kind), 2)
    end
    self._queue[#self._queue + 1] = { kind = kind, def = def }
end

function W:Get(kind)
    local def = self._registry[kind]
    if not def then error("WidgetTypes:Get: unknown kind " .. tostring(kind), 2) end
    return def
end

function W:TryGet(kind) return self._registry[kind] end

local function validateOne(kind, def, errors)
    local function err(msg) errors[#errors + 1] = ("[%s] %s"):format(kind, msg) end

    if type(def.build) ~= "function" then
        err("missing required field `build` (must be a function)")
    end
    for k in pairs(def) do
        if not KNOWN_FIELDS[k] then
            err(("unknown field %q (not in KNOWN_FIELDS)"):format(k))
        end
    end
    if def.skin ~= nil and type(def.skin) ~= "string" then
        err("`skin` must be a string Skinner role")
    end
    if def.dispatch ~= nil then
        if type(def.dispatch) ~= "table"
           or type(def.dispatch.fields) ~= "table"
           or type(def.dispatch.push) ~= "function" then
            err("`dispatch` must be { fields = { ... }, push = function }")
        end
    end
    if def.requiresFont ~= nil and type(def.requiresFont) ~= "function" then
        err("`requiresFont` must be a function (spec) -> bool")
    end
    if def.specFields ~= nil then
        if type(def.specFields) ~= "table" then
            err("`specFields` must be a list of field-name strings")
        else
            for i, name in ipairs(def.specFields) do
                if type(name) ~= "string" then
                    err(("`specFields[%d]` must be a string, got %s"):format(i, type(name)))
                end
            end
        end
    end
end

function W:Flush()
    if self._flushed then return end
    self._flushed = true

    for _, entry in ipairs(self._queue) do -- Pass 1: insert (catches duplicates)
        if self._registry[entry.kind] then
            error(("VDS.WidgetTypes: duplicate registration for kind %q"):format(entry.kind), 2)
        end
        self._registry[entry.kind] = entry.def
    end

    local errors = {} -- Pass 2: validate
    for kind, def in pairs(self._registry) do
        validateOne(kind, def, errors)
    end
    if #errors > 0 then
        error("VDS.WidgetTypes:Flush validation failures:\n  " .. table.concat(errors, "\n  "), 2)
    end
end
