-- test_coverage_map.lua
-- ============================================================================
-- Coverage map for HDG — surfaces selectors / actions / widget kinds that
-- aren't referenced (by quoted-string name) in any sibling test_*.lua file.
-- Heuristic — false positives possible if a name only appears in a comment.
-- The right fix is adding a real test that exercises the entry; do not
-- adjust the heuristic to suppress the check.
--
-- Wildcard credit: a test file that contains `Selectors:GetRegistry()`
-- (or `Selectors._registry`) is considered to exercise every registered
-- selector. Same for `WidgetTypes._registry` and widget kinds. This
-- matches the test_first_paint.lua pattern.

local _realPrint = print
local function out(s) _realPrint(s) end

dofile("wow_mock.lua")
dofile("../Core/HDGR_SchemeConstants.lua")
dofile("../Core/HDGR_Constants.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_Store.lua")
dofile("../Core/HDGR_WidgetTypes.lua")
dofile("../Core/HDGR_Expansion.lua")
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/HDGR_Theme.lua")
dofile("../UI/HDGR_Selectors.lua")
dofile("../UI/HDGR_CategoryNav.lua")
dofile("../UI/HDGR_Selectors_Status.lua")
dofile("../UI/HDGR_Selectors_Decor.lua")
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")

if HDG.WidgetTypes and HDG.WidgetTypes.Flush then HDG.WidgetTypes:Flush() end

-- ============================================================================
-- Read every sibling test file (except this one) into one corpus.
-- ============================================================================

local function readAllTestFiles()
    local p = io.popen('ls test_*.lua 2>/dev/null')
    if not p then return "" end
    local files = {}
    for line in p:lines() do
        if line ~= "test_coverage_map.lua" then files[#files + 1] = line end
    end
    p:close()
    local parts = {}
    for _, fname in ipairs(files) do
        local f = io.open(fname, "r")
        if f then parts[#parts + 1] = f:read("*a"); f:close() end
    end
    return table.concat(parts, "\n")
end

local allTests = readAllTestFiles()

-- Iteration of the registry is sufficient signal — a test that iterates the
-- registry is exercising all entries.
local selectorWildcard = allTests:find("Selectors._registry", 1, true)
                      or allTests:find("Selectors:GetRegistry", 1, true)
local widgetWildcard   = allTests:find("WidgetTypes._registry", 1, true)
                      or allTests:find("WidgetTypes:GetRegistry", 1, true)
local actionWildcard   = allTests:find("Constants.ACTIONS", 1, true) ~= nil

-- ============================================================================
-- Enumerate registries
-- ============================================================================

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

local actions = {}
for name, value in pairs(HDG.Constants.ACTIONS) do
    actions[#actions + 1] = { name = name, value = value }
end
table.sort(actions, function(a, b) return a.name < b.name end)

local widgetKinds = {}
for kind in pairs(HDG.WidgetTypes._registry or {}) do widgetKinds[#widgetKinds + 1] = kind end
table.sort(widgetKinds)

-- ============================================================================
-- Coverage check
-- ============================================================================

local missingSelectors = {}
if not selectorWildcard then
    for _, name in ipairs(selectors) do
        if not allTests:find('"' .. name .. '"', 1, true) then
            missingSelectors[#missingSelectors + 1] = name
        end
    end
end

local missingActions = {}
if not actionWildcard then
    for _, a in ipairs(actions) do
        if not (allTests:find("%." .. a.name .. "%f[%W]") or allTests:find('"' .. a.value .. '"', 1, true)) then
            missingActions[#missingActions + 1] = a.name
        end
    end
end

local missingKinds = {}
if not widgetWildcard then
    for _, kind in ipairs(widgetKinds) do
        if not allTests:find('"' .. kind .. '"', 1, true) then
            missingKinds[#missingKinds + 1] = kind
        end
    end
end

-- ============================================================================
-- Report
-- ============================================================================

out(string.format("HDG coverage: %d selectors / %d actions / %d widget kinds registered",
    #selectors, #actions, #widgetKinds))
if selectorWildcard then out("  selectors: wildcard credit (registry iteration in test suite)") end
if widgetWildcard   then out("  widget kinds: wildcard credit (registry iteration in test suite)") end
if actionWildcard   then out("  actions: wildcard credit (ACTIONS iteration in test suite)") end

local function report(label, gaps)
    if #gaps == 0 then
        out(string.format("  [OK] %s coverage: complete", label))
        return false
    end
    out(string.format("  [GAP] %s without test coverage (%d):", label, #gaps))
    for i = 1, math.min(#gaps, 30) do out("    - " .. gaps[i]) end
    if #gaps > 30 then out(string.format("    ... and %d more", #gaps - 30)) end
    return true
end

local anyGap = false
anyGap = report("selectors",    missingSelectors) or anyGap
anyGap = report("actions",      missingActions)   or anyGap
anyGap = report("widget kinds", missingKinds)     or anyGap

if anyGap then
    error("test_coverage_map: registered entries missing test references — fix by adding tests, not by suppressing the check", 0)
end
out("test_coverage_map.lua passed")
