-- test_coverage_map.lua
-- ============================================================================
-- Coverage map — surfaces selectors / actions / widget kinds without tests.
--
-- After all engine + domain files load, this test walks the registries and
-- checks which entries are referenced in any test_*.lua file. The gaps are
-- printed; the test fails if any registered entry has no test reference.
--
-- The check is heuristic (substring match on test file contents). False
-- positives are possible if test files only reference the entry in a comment.
-- Tighten by adding tests that exercise the entry, not by adjusting the
-- heuristic.

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("../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_BindingEngine.lua")

-- ============================================================================
-- Read every sibling test file (except this one) into one big string for
-- substring search. Cheap O(N*M) — fine for <100 test files.
-- ============================================================================

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 out = {}
    for _, fname in ipairs(files) do
        local f = io.open(fname, "r")
        if f then out[#out + 1] = f:read("*a"); f:close() end
    end
    return table.concat(out, "\n")
end

local allTests = readAllTestFiles()

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

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

local actions = {}
for name, value in pairs(VDS.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(VDS.WidgetTypes._registry) do widgetKinds[#widgetKinds + 1] = kind end
table.sort(widgetKinds)

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

local missingSelectors = {}
for _, name in ipairs(selectors) do
    -- Quoted-string match catches `"hello.greeting"` references in test code.
    if not allTests:find('"' .. name .. '"', 1, true) then
        missingSelectors[#missingSelectors + 1] = name
    end
end

local missingActions = {}
for _, a in ipairs(actions) do
    -- Match either `A.NAME` form or the literal `"VALUE"` string-form dispatch.
    if not (allTests:find("%." .. a.name .. "%f[%W]") or allTests:find('"' .. a.value .. '"', 1, true)) then
        missingActions[#missingActions + 1] = a.name
    end
end

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

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

print(string.format("Coverage map: %d selectors / %d actions / %d widget kinds registered",
    #selectors, #actions, #widgetKinds))

local function report(label, gaps)
    if #gaps == 0 then
        print(string.format("  [OK] %s coverage: all referenced in tests", label))
        return false
    end
    print(string.format("  [GAP] %s without test coverage (%d):", label, #gaps))
    for _, name in ipairs(gaps) do print("    - " .. name) 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
print("test_coverage_map.lua passed")
