-- test_log_smoke.lua
-- ============================================================================
-- The Log surface MUST define all six engine-default levels: Trace, Debug,
-- Info, Warn, Error, Success. ErrorBoundary + Modules:Boot + Layout hooks
-- call Log:Error -- a missing method raises a SECONDARY error that masks
-- the original failure. Missing Success forces migrations into Warn (wrong
-- semantics). See cookbook 16.
--
-- This test guards against regression: a future engineer "trimming" Log.lua
-- back to 4 levels would silently re-introduce the error-masking bug.

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")

---@diagnostic disable: undefined-global
local function assertType(v, expected, label)
    if type(v) ~= expected then
        error(label .. ": expected " .. expected .. ", got " .. type(v), 2)
    end
end
local function assertTrue(v, label) if not v then error(label, 2) end end
local function assertFalse(v, label) if v then error(label .. " (expected false-y, got truthy)", 2) end end
local function assertEq(a, b, label)
    if a ~= b then error(label .. ": expected " .. tostring(b) .. " got " .. tostring(a), 2) end
end

-- ===== All six levels MUST be present on the engine surface =====
assertType(VDS.Log.Trace,   "function", "Log:Trace must exist")
assertType(VDS.Log.Debug,   "function", "Log:Debug must exist")
assertType(VDS.Log.Info,    "function", "Log:Info must exist")
assertType(VDS.Log.Warn,    "function", "Log:Warn must exist")
assertType(VDS.Log.Error,   "function", "Log:Error MUST exist (cookbook 16 -- ErrorBoundary + Modules + Layout all call it; missing = secondary-error mask)")
assertType(VDS.Log.Success, "function", "Log:Success MUST exist (cookbook 16 -- migrations + hard-reset use it)")

-- ===== Plus the control surface =====
assertType(VDS.Log.SetDebug,      "function", "Log:SetDebug must exist")
assertType(VDS.Log.IsDebug,       "function", "Log:IsDebug must exist")
assertType(VDS.Log.IsTraceActive, "function", "Log:IsTraceActive must exist")
assertType(VDS.Log.ToggleTrace,   "function", "Log:ToggleTrace must exist")

-- ===== Gating behaviour: Info / Debug suppress when _debug=false =====
local captured = {}
local origPrint = _G.print
_G.print = function(s) captured[#captured + 1] = s end

VDS.Log:SetDebug(false)
captured = {}
local bufBefore = #VDS.Log._buffer
VDS.Log:Info("test", "should be suppressed")
assertEq(#captured, 0, "Info suppressed when _debug=false")
VDS.Log:Debug("test", "should be suppressed")
assertEq(#captured, 0, "Debug suppressed when _debug=false")
assertEq(#VDS.Log._buffer, bufBefore, "Debug skips ring buffer when _debug=false")

VDS.Log:SetDebug(true)
captured = {}
VDS.Log:Info("test", "should print")
assertEq(#captured, 1, "Info prints when _debug=true")
-- Debug is buffer-only BY DESIGN (visible via /vds log; skips chat to avoid
-- noise -- see VDS_Log.lua). It must land in the ring buffer, never in chat.
bufBefore = #VDS.Log._buffer
VDS.Log:Debug("test", "should buffer, not print")
assertEq(#captured, 1, "Debug never prints to chat (buffer-only by design)")
assertEq(#VDS.Log._buffer, bufBefore + 1, "Debug pushes to ring buffer when _debug=true")

-- ===== Always-on levels: Warn / Error / Success print regardless of debug =====
VDS.Log:SetDebug(false)
captured = {}
VDS.Log:Warn("test", "always visible")
assertEq(#captured, 1, "Warn always prints regardless of debug flag")

captured = {}
VDS.Log:Error("test", "errors are always visible")
assertEq(#captured, 1, "Error always prints regardless of debug flag")
assertTrue(captured[1]:find("ERROR", 1, true) ~= nil,
    "Error output contains 'ERROR' marker (severity must be visually distinct)")

captured = {}
VDS.Log:Success("test", "positive milestone")
assertEq(#captured, 1, "Success always prints regardless of debug flag")

-- ===== Trace gating: per-tag opt-in =====
captured = {}
VDS.Log:Trace("untoggled", "should be suppressed")
assertEq(#captured, 0, "Trace suppressed when tag not toggled")

assertFalse(VDS.Log:IsTraceActive("mytag"), "tag starts inactive")
VDS.Log:ToggleTrace("mytag")
assertTrue(VDS.Log:IsTraceActive("mytag"), "tag active after toggle")

captured = {}
VDS.Log:Trace("mytag", "should print")
assertEq(#captured, 1, "Trace prints when tag is toggled active")

VDS.Log:ToggleTrace("mytag")
assertFalse(VDS.Log:IsTraceActive("mytag"), "tag inactive after second toggle")

-- ===== Color distinctness: each level uses a different color code =====
VDS.Log:SetDebug(true)
local colors = {}
local function capColor(level, fn)
    captured = {}
    fn(VDS.Log, "test", "msg")
    local hex = captured[1] and captured[1]:match("|c(ff%x%x%x%x%x%x)")
    colors[level] = hex
end
capColor("Warn",    VDS.Log.Warn)
capColor("Error",   VDS.Log.Error)
capColor("Success", VDS.Log.Success)
capColor("Info",    VDS.Log.Info)
-- (Debug omitted: buffer-only, never emits a chat color.)
capColor("Trace",   function(L, t, m) L:ToggleTrace(t); L:Trace(t, m); L:ToggleTrace(t) end)

local seen = {}
for level, hex in pairs(colors) do
    if hex then
        if seen[hex] then
            error(("Color collision: %s and %s both use %s"):format(level, seen[hex], hex), 2)
        end
        seen[hex] = level
    end
end

_G.print = origPrint
print("test_log_smoke.lua passed (6 levels + gating + trace + distinct colors)")
