-- Waypoints smoke test (Phase 10b).
--
-- The Waypoints module sits at the UI/Blizzard boundary -- it calls
-- C_Map.SetUserWaypoint, C_SuperTrack, TomTom, and WorldMapFrame APIs
-- that can't be exercised fully in a headless test. This test covers
-- the PURE-LUA surface: ZonePctToMap coordinate math, the early-return
-- guards (combat lockdown, no_map, no_tomtom), and the InitMapPins
-- idempotency contract.
--
-- The headless wow_mock stubs in/out C_Map / InCombatLockdown / etc.;
-- callers that ever land on the actual Blizzard APIs are exercised by
-- /reload in-game testing.

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_TableUtils.lua")
dofile("../Core/HDGR_Memo.lua")
dofile("../Core/HDGR_BlizzardEvents.lua")
dofile("../Core/HDGR_Environment.lua")
dofile("../Core/HDGR_WidgetTypes.lua")
dofile("../Core/HDGR_TooltipEngine.lua")
dofile("../Core/HDGR_Modules.lua")
dofile("../Core/HDGR_Middleware.lua")
dofile("../Core/HDGR_StoreFurnishings.lua")
dofile("../Core/HDGR_Store.lua")
-- Config layer: reducer routes CONFIG_SET via HDG.Config:_GetSourceForScope.
dofile("../Core/HDGR_ConfigSchema.lua")
dofile("../Core/HDGR_Config.lua")
-- Selectors: Store unconditionally calls HDG.Selectors:InvalidateMemos on every
-- _RawDispatch (no-guards policy). Test must load Selectors so the registry exists.
dofile("../UI/HDGR_Selectors.lua")
-- Theme stack: Waypoints:Set now routes user feedback through Waypoints:PrintResult
-- -> HDG.Theme:GetTextStateColorToken (a single chat line per set, or one
-- consolidated line per :RunBatch), so the feedback path needs Palette + Theme.
dofile("../Core/HDGR_Locale.lua")
dofile("../UI/HDGR_Palette.lua")
dofile("../UI/HDGR_Theme.lua")
HDGR_VendorDB = {}  -- stub: deprecated DB archived; test does not assert on its content
dofile("../Modules/HDGR_StaticData.lua")

-- Waypoints.lua does a module-top mixin install (HDGR_VendorPinMixin =
-- CreateFromMixins(MapCanvasPinMixin)) at file-load time. wow_mock
-- doesn't carry these Blizzard globals, so stub before dofile.
_G.CreateFromMixins = _G.CreateFromMixins or function(...) return {} end
_G.MapCanvasPinMixin = _G.MapCanvasPinMixin or {}
_G.MapCanvasDataProviderMixin = _G.MapCanvasDataProviderMixin or {}
_G.RunNextFrame = _G.RunNextFrame or function(fn) fn() end
_G.UiMapPoint = _G.UiMapPoint or { CreateFromCoordinates = function() return {} end }
_G.GameTooltip_Hide = _G.GameTooltip_Hide or function() end
_G.OpenWorldMap = _G.OpenWorldMap or function() end
_G.C_Map = _G.C_Map or {}
_G.C_Map.GetMapInfo            = _G.C_Map.GetMapInfo            or function(id) return id and id > 0 and {} or nil end
_G.C_Map.GetBestMapForUnit     = _G.C_Map.GetBestMapForUnit     or function() return 27 end
_G.C_Map.CanSetUserWaypointOnMap = _G.C_Map.CanSetUserWaypointOnMap or function() return true end
_G.C_Map.SetUserWaypoint       = _G.C_Map.SetUserWaypoint       or function() end
_G.C_Map.ClearUserWaypoint     = _G.C_Map.ClearUserWaypoint     or function() end
_G.C_SuperTrack = _G.C_SuperTrack or { SetSuperTrackedUserWaypoint = function() end }
_G.UnitFactionGroup = _G.UnitFactionGroup or function() return "Alliance" end
_G.InCombatLockdown = _G.InCombatLockdown or function() return false end

dofile("../Modules/HDGR_Waypoints.lua")

local function assertEq(a, b, label)
    if a ~= b then error(label .. ": expected " .. tostring(b) .. ", got " .. tostring(a), 2) end
end
local function assertTrue(v, label) if not v then error(label, 2) end end

HDG_DB = {}
HDG.Store:LoadFromSavedVariables()
HDG.Config:Initialize()

-- ===== ZonePctToMap coordinate conversion =====
-- 0..100 pct -> 0..1 map coords. Rejects mapID=0, out-of-bounds pct,
-- and (0,0) which the heuristic treats as "no coords".
local R = HDG.Waypoints   -- ZonePctToMap canonical home (VendorResolver shim retired 2026-06-05)
local m, x, y = R.ZonePctToMap(0, 50, 50)
assertEq(m, nil, "mapID=0 -> nil (invalid)")

m, x, y = R.ZonePctToMap(27, 0, 0)
assertEq(m, nil, "(0,0) -> nil (no-coords sentinel)")

m, x, y = R.ZonePctToMap(27, 200, 50)
assertEq(m, nil, "pctX > 105 -> nil (out-of-bounds)")

m, x, y = R.ZonePctToMap(27, 54.4, 50.8)
assertEq(m, 27,    "valid input echoes uiMapID")
assertEq(math.floor(x * 1000 + 0.5), 544, "pctX 54.4 -> mapX 0.544")
assertEq(math.floor(y * 1000 + 0.5), 508, "pctY 50.8 -> mapY 0.508")

-- ===== Waypoint preference + ShouldUseTomTom =====
-- The Waypoints module reads state.account.config.waypointProvider.
-- Default is "auto" (use TomTom if installed). Values: "auto"|"tomtom"|"blizzard".
-- The headless wow_mock has no TomTom global. Behaviour:
--   "auto"     -> TomTom absent, falls through to Blizzard native (ok=true)
--   "tomtom"   -> TomTom absent: logs a warn once, falls back to Blizzard (ok=true)
--   "blizzard" -> always Blizzard regardless of TomTom (ok=true)
-- (Pass B 2026-05-26: "tomtom" mode no longer fails the call -- it warns and
--  falls back. Previous "native" key renamed to "blizzard".)
HDG.Store:Dispatch({ type = HDG.Constants.ACTIONS.CONFIG_SET,
    payload = { key = "waypointProvider", value = "tomtom" } })
local okT, providerT = HDG.Waypoints:Set(27, 54.4, 50.8, "Test")
assertEq(okT, true,       "tomtom mode with TomTom missing falls back -> ok=true")
assertEq(providerT, "native", "tomtom fallback reports native provider")

-- "blizzard" mode bypasses TomTom entirely. wow_mock should let the
-- native path return ok=true, "native".
HDG.Store:Dispatch({ type = HDG.Constants.ACTIONS.CONFIG_SET,
    payload = { key = "waypointProvider", value = "blizzard" } })
local okN, providerN = HDG.Waypoints:Set(27, 54.4, 50.8, "Test")
assertEq(okN, true,    "blizzard mode succeeds")
assertEq(providerN, "native", "blizzard mode reports native provider")

-- ===== InCombatLockdown guard =====
-- Force combat lockdown via the wow_mock and verify the facade
-- early-returns. The vendor-pin OnMouseDown also has this guard
-- (Phase 10a review fix); not invokable from this layer.
_G.InCombatLockdown = function() return true end
local okC, providerC = HDG.Waypoints:Set(27, 54.4, 50.8, "Test")
assertEq(okC, false,   "combat lockdown -> ok=false")
assertEq(providerC, "combat", "combat lockdown reports combat provider")
_G.InCombatLockdown = function() return false end   -- restore

-- ===== AddMapPin + ClearAllMapPins =====
-- pendingPins is an internal table; we can't probe it directly, but we
-- can verify the public API doesn't crash when called repeatedly.
HDG.Waypoints:AddMapPin(27, 0.5, 0.5, "Pin A")
HDG.Waypoints:AddMapPin(27, 0.6, 0.6, "Pin B")
HDG.Waypoints:AddMapPin(48, 0.3, 0.3, "Pin C")
HDG.Waypoints:ClearAllMapPins()
-- No assertion; the test passes if neither call raised.

-- ===== InitMapPins idempotency =====
-- Calling InitMapPins multiple times should be a no-op after the first
-- successful call. wow_mock provides a WorldMapFrame stub; the function
-- early-returns on missing WorldMapFrame.
HDG.Waypoints:InitMapPins()
HDG.Waypoints:InitMapPins()   -- second call should be a no-op
HDG.Waypoints:InitMapPins()   -- and again

print("test_waypoints_smoke.lua passed")
