-- test_action_meta_exhaustiveness.lua
-- Priority 3: Action meta exhaustiveness as a CI-executable test.
--
-- Invariant 4.2: every value in HDG.Constants.ACTIONS must have a key
-- in BuildActionMeta()'s output, and that key must carry an `invalidates`
-- field. The boot validator already catches this at runtime; this test
-- makes the same contract runnable in the test suite so regressions fail
-- in CI rather than only at first game load.
--
-- We reproduce the validator logic inline (no reliance on Init.lua which
-- pulls in many deps we don't need here). The test fails if any action is
-- missing meta OR if any meta row is missing `invalidates`.

dofile("wow_mock.lua")
dofile("../Core/HDGR_SchemeConstants.lua")
dofile("../Core/HDGR_Constants.lua")
dofile("../Core/HDGR_Actions.lua")
dofile("../Core/HDGR_Resolvers.lua")
dofile("../Core/HDGR_Format.lua")
dofile("../Core/HDGR_Paths.lua")
dofile("../Core/HDGR_Log.lua")
dofile("../Core/HDGR_StoreFurnishings.lua")
dofile("../Core/HDGR_Store.lua")
-- Load BlizzardEvents BEFORE Init.lua because Init.lua calls
-- HDG.BlizzardEvents:_internalSubscribe at file level (line 645).
-- Without it, dofile("HDGR_Init.lua") crashes immediately.
dofile("../Core/HDGR_BlizzardEvents.lua")
dofile("../Core/HDGR_Environment.lua")
dofile("../Core/HDGR_Modules.lua")
-- Theme is in UI/, not Core/.
dofile("../UI/HDGR_Theme.lua")
-- Locale: HDG:OnInitialize calls HDG.Locale:Initialize unconditionally
-- (no-guards policy), so the test harness must provide it.
dofile("../Core/HDGR_Locale.lua")
-- Now load Init.lua. At file level it only registers ADDON_LOADED /
-- PLAYER_LOGIN / PLAYER_LOGOUT handlers; OnInitialize is NOT called yet.
dofile("../Core/HDGR_Init.lua")

-- Manually boot the minimum needed so BuildActionMeta can run.
-- HDG:OnInitialize() calls Theme:Initialize() then BuildActionMeta()
-- then stashes the result on HDG.Store._actionMeta BEFORE calling
-- Modules:Boot(). We pcall the whole OnInitialize; even if it errors
-- on a missing UI dep later, the meta is already stashed.
HDG_DB = {}
HDG.Store:LoadFromSavedVariables()
pcall(function() HDG:OnInitialize() end)

local actionMeta = HDG.Store._actionMeta
if type(actionMeta) ~= "table" then
    error("HDG.Store._actionMeta was not set by OnInitialize -- test setup failed", 2)
end

-- Exhaustiveness check (mirrors ValidateActionMetaExhaustiveness in Init.lua).
local A = HDG.Constants.ACTIONS
local missingMeta, missingInvalidates = {}, {}

for name, value in pairs(A) do
    local row = actionMeta[value]
    if row == nil then
        missingMeta[#missingMeta + 1] = name
    else
        local inv = row.invalidates
        local invType = type(inv)
        local valid = invType == "table"
                   or invType == "function"
                   or (invType == "string" and inv == "*")
        if not valid then
            missingInvalidates[#missingInvalidates + 1] = name
        end
    end
end

if #missingMeta > 0 then
    table.sort(missingMeta)
    error("Action meta missing for actions:\n  " .. table.concat(missingMeta, "\n  ")
        .. "\nFix: add entries to BuildActionMeta() in HDGR_Init.lua", 2)
end

if #missingInvalidates > 0 then
    table.sort(missingInvalidates)
    error("Action meta missing `invalidates` field for:\n  "
        .. table.concat(missingInvalidates, "\n  ")
        .. "\nFix: every meta row needs invalidates = {paths} | fn | \"*\"", 2)
end

-- Confirm total count looks reasonable (sanity: not an empty ACTIONS table).
local actionCount = 0
for _ in pairs(A) do actionCount = actionCount + 1 end
if actionCount < 20 then
    error("ACTIONS table has only " .. actionCount .. " entries -- suspiciously small; "
        .. "check HDGR_Constants.lua loaded correctly", 2)
end

-- Confirm every meta value in actionMeta corresponds to a known ACTIONS value
-- (inverse check: no orphaned meta rows).
local knownValues = {}
for _, v in pairs(A) do knownValues[v] = true end
local orphaned = {}
for actionValue in pairs(actionMeta) do
    if not knownValues[actionValue] then
        orphaned[#orphaned + 1] = actionValue
    end
end
if #orphaned > 0 then
    table.sort(orphaned)
    error("Orphaned meta rows (in BuildActionMeta but NOT in ACTIONS table):\n  "
        .. table.concat(orphaned, "\n  ")
        .. "\nFix: remove from BuildActionMeta or add to ACTIONS", 2)
end

print(string.format(
    "test_action_meta_exhaustiveness.lua passed (%d actions, all have meta + invalidates)",
    actionCount))
