-- Projects ID grammar: make/parse/shortUUID/namespacedID (pure).
dofile("wow_mock.lua")
dofile("../Core/HDGR_ProjectsIDs.lua")

local IDs = HDG.Projects.IDs

local function assertEq(a, b, m) if a ~= b then error((m or "") .. ": expected " .. tostring(b) .. ", got " .. tostring(a), 2) end end
local function assertNil(a, m) if a ~= nil then error((m or "") .. ": expected nil, got " .. tostring(a), 2) end end
local function assertTrue(c, m) if not c then error(m or "assert", 2) end end

-- House -- keyed by an opaque alphanumeric TOKEN now (plot digest), not faction.
assertEq(IDs.makeHouseID("a1b2c3d4"), "house:a1b2c3d4", "makeHouseID wraps a token")
assertEq(IDs.makeHouseID("alliance"), "house:alliance", "makeHouseID accepts any %w token")
assertNil(IDs.makeHouseID(""), "makeHouseID rejects empty")
assertNil(IDs.makeHouseID("has space"), "makeHouseID rejects non-alphanumeric")
assertEq(IDs.parseHouseID("house:a1b2c3d4"), "a1b2c3d4", "parseHouseID returns the token")
assertNil(IDs.parseHouseID("house:a:b"), "parseHouseID rejects multi-segment")

-- hashToken: deterministic, grammar-safe (8 hex), distinct inputs -> distinct tokens.
local tok = IDs.hashToken("Neighborhood-1-2-3:5")
assertEq(tok, IDs.hashToken("Neighborhood-1-2-3:5"), "hashToken deterministic")
assertTrue(tok:match("^[0-9a-f]+$") ~= nil, "hashToken is hex")
assertEq(#tok, 8, "hashToken is 8 chars")
assertTrue(tok ~= IDs.hashToken("Neighborhood-1-2-3:6"), "hashToken distinguishes plot")
assertEq(IDs.makeHouseID(tok), "house:" .. tok, "hashToken feeds makeHouseID")

-- Floor / room / crate
local h = IDs.makeHouseID("alliance")
local f = IDs.makeFloorID(h, 1)
assertEq(f, "house:alliance:f1", "makeFloorID")
assertNil(IDs.makeFloorID(h, 0), "floor must be >= 1")
assertNil(IDs.makeFloorID(h, 1.5), "floor must be integer")
local r = IDs.makeRoomID(f, "7a3f")
assertEq(r, "house:alliance:f1:r7a3f", "makeRoomID")
assertNil(IDs.makeRoomID(f, "XYZ"), "room uuid must be hex")
local c = IDs.makeCrateID(r, "8b2e")
assertEq(c, "house:alliance:f1:r7a3f:c8b2e", "makeCrateID")

-- parsePath narrows correctly (crate -> room -> floor -> house)
local pc = IDs.parsePath(c)
assertEq(pc.kind, "crate", "parse crate kind")
assertEq(pc.roomID, r, "parse crate -> roomID rollup")
assertEq(pc.floorID, f, "parse crate -> floorID rollup")
assertEq(pc.houseID, h, "parse crate -> houseID rollup")
assertEq(pc.floor, 1, "parse crate floor num")
local pr = IDs.parsePath(r)
assertEq(pr.kind, "room", "parse room kind")
assertEq(pr.roomShortID, "7a3f", "parse room short id")
local pf = IDs.parsePath(f)
assertEq(pf.kind, "floor", "parse floor kind")
assertEq(pf.floor, 1, "parse floor num")
local ph = IDs.parsePath(h)
assertEq(ph.kind, "house", "parse house kind")
assertEq(ph.houseID, h, "parse house -> houseID (no faction field)")
assertNil(IDs.parsePath("garbage"), "parsePath rejects non-grammar")

-- shortUUID + namespacedID
local u = IDs.shortUUID(4)
assertEq(#u, 4, "shortUUID length")
assertTrue(u:match("^[0-9a-f]+$"), "shortUUID is hex")
local lib = IDs.namespacedID("lib")
assertTrue(lib:match("^lib_[0-9a-f]+$"), "namespacedID lib_<hex>")
assertNil(IDs.namespacedID("Lib"), "namespacedID rejects non-lowercase prefix")

print("test_projects_ids.lua passed")
