-- HousingDecorGuide/tests/test_projects_ids.lua
---@diagnostic disable: undefined-global
-- ^ Standalone Lua test file — `package` / `require` / `dofile` are valid here even though
-- the project's WoW-context .luarc.json disables them.
package.path = "./?.lua;../Core/?.lua;" .. package.path
math.randomseed(os.time())

-- Load the module under test (it returns its public table when loaded standalone)
local IDs = dofile("../Core/HDG_ProjectsIDs.lua")

local t = require("test_helpers")

t.describe("HDG_ProjectsIDs.makeHouseID", function()
    t.it("returns 'house:alliance' for 'alliance'", function()
        t.assert_eq(IDs.makeHouseID("alliance"), "house:alliance")
    end)
    t.it("returns 'house:horde' for 'horde'", function()
        t.assert_eq(IDs.makeHouseID("horde"), "house:horde")
    end)
    t.it("normalizes case", function()
        t.assert_eq(IDs.makeHouseID("Alliance"), "house:alliance")
        t.assert_eq(IDs.makeHouseID("HORDE"), "house:horde")
    end)
    t.it("rejects invalid factions", function()
        t.assert_nil(IDs.makeHouseID("neutral"))
        t.assert_nil(IDs.makeHouseID(""))
        t.assert_nil(IDs.makeHouseID(nil))
    end)
end)

t.describe("HDG_ProjectsIDs.parseHouseID", function()
    t.it("extracts faction from house:alliance", function()
        t.assert_eq(IDs.parseHouseID("house:alliance"), "alliance")
    end)
    t.it("extracts faction from house:horde", function()
        t.assert_eq(IDs.parseHouseID("house:horde"), "horde")
    end)
    t.it("returns nil for non-house keys", function()
        t.assert_nil(IDs.parseHouseID("garbage"))
        t.assert_nil(IDs.parseHouseID("house:alliance:f1:r7a3f"))    -- has more segments
    end)
end)

t.describe("HDG_ProjectsIDs.makeFloorID", function()
    t.it("builds 'house:alliance:f1'", function()
        t.assert_eq(IDs.makeFloorID("house:alliance", 1), "house:alliance:f1")
    end)
    t.it("rejects invalid floor numbers", function()
        t.assert_nil(IDs.makeFloorID("house:alliance", 0))
        t.assert_nil(IDs.makeFloorID("house:alliance", -1))
        t.assert_nil(IDs.makeFloorID("house:alliance", 1.5))
        t.assert_nil(IDs.makeFloorID("house:alliance", "1"))
    end)
end)

t.describe("HDG_ProjectsIDs.makeRoomID", function()
    t.it("builds 'house:alliance:f1:r7a3f'", function()
        t.assert_eq(IDs.makeRoomID("house:alliance:f1", "7a3f"), "house:alliance:f1:r7a3f")
    end)
    t.it("rejects bad short_uuid", function()
        t.assert_nil(IDs.makeRoomID("house:alliance:f1", ""))
        t.assert_nil(IDs.makeRoomID("house:alliance:f1", "BAD!"))    -- non-hex
    end)
end)

t.describe("HDG_ProjectsIDs.makeCrateID", function()
    t.it("builds '<roomID>:c<short_uuid>'", function()
        t.assert_eq(IDs.makeCrateID("house:alliance:f1:r7a3f", "8b2e"),
                    "house:alliance:f1:r7a3f:c8b2e")
    end)
end)

t.describe("HDG_ProjectsIDs.parsePath", function()
    t.it("parses a full crate path", function()
        local p = IDs.parsePath("house:alliance:f1:r7a3f:c8b2e")
        t.assert_eq(p.faction, "alliance")
        t.assert_eq(p.floor, 1)
        t.assert_eq(p.roomShortID, "7a3f")
        t.assert_eq(p.crateShortID, "8b2e")
        t.assert_eq(p.houseID, "house:alliance")
        t.assert_eq(p.floorID, "house:alliance:f1")
        t.assert_eq(p.roomID, "house:alliance:f1:r7a3f")
        t.assert_eq(p.crateID, "house:alliance:f1:r7a3f:c8b2e")
        t.assert_eq(p.kind, "crate")
    end)
    t.it("parses a room path", function()
        local p = IDs.parsePath("house:alliance:f1:r7a3f")
        t.assert_eq(p.kind, "room")
        t.assert_eq(p.roomShortID, "7a3f")
        t.assert_nil(p.crateShortID)
    end)
    t.it("parses a floor path", function()
        local p = IDs.parsePath("house:horde:f2")
        t.assert_eq(p.kind, "floor")
        t.assert_eq(p.floor, 2)
        t.assert_eq(p.faction, "horde")
    end)
    t.it("parses a house path", function()
        local p = IDs.parsePath("house:alliance")
        t.assert_eq(p.kind, "house")
        t.assert_eq(p.faction, "alliance")
    end)
    t.it("returns nil for garbage", function()
        t.assert_nil(IDs.parsePath("foo:bar"))
        t.assert_nil(IDs.parsePath(""))
    end)
    t.it("rejects uppercase / non-hex room or crate short IDs (must round-trip with make*)", function()
        -- The maker rejects these, so the parser must too — otherwise we accept keys
        -- that no one in this module could have produced.
        t.assert_nil(IDs.parsePath("house:alliance:f1:rBAD_ID"))
        t.assert_nil(IDs.parsePath("house:alliance:f1:r7a3f:cBADHEX"))
        t.assert_nil(IDs.parsePath("house:alliance:f1:rABCDEF"))   -- uppercase hex
        t.assert_nil(IDs.parsePath("house:alliance:f1:r_underscore"))
    end)
end)

t.describe("HDG_ProjectsIDs.shortUUID", function()
    t.it("returns a 4-char hex string by default", function()
        local id = IDs.shortUUID()
        t.assert_eq(type(id), "string")
        t.assert_eq(#id, 4)
        t.assert_truthy(id:match("^[0-9a-f]+$"))
    end)
    t.it("respects a length argument", function()
        t.assert_eq(#IDs.shortUUID(8), 8)
        t.assert_eq(#IDs.shortUUID(12), 12)
    end)
    t.it("produces different IDs on successive calls (probabilistic)", function()
        local seen = {}
        for _ = 1, 200 do seen[IDs.shortUUID(8)] = true end
        local count = 0; for _ in pairs(seen) do count = count + 1 end
        t.assert_truthy(count > 150, "expected mostly unique 8-char IDs across 200 calls, got " .. count)
    end)
    t.it("rejects invalid length arguments", function()
        t.assert_nil(IDs.shortUUID("bad"))
        t.assert_nil(IDs.shortUUID(0))
        t.assert_nil(IDs.shortUUID(-1))
    end)
end)

t.describe("HDG_ProjectsIDs.namespacedID", function()
    t.it("makes lib_xxxx", function()
        local id = IDs.namespacedID("lib")
        t.assert_truthy(id:match("^lib_[0-9a-f]+$"))
    end)
    t.it("makes ship_xxxx", function()
        t.assert_truthy(IDs.namespacedID("ship"):match("^ship_[0-9a-f]+$"))
    end)
    t.it("makes plan_xxxx", function()
        t.assert_truthy(IDs.namespacedID("plan"):match("^plan_[0-9a-f]+$"))
    end)
    t.it("makes orphan_xxxx", function()
        t.assert_truthy(IDs.namespacedID("orphan"):match("^orphan_[0-9a-f]+$"))
    end)
    t.it("rejects invalid prefixes", function()
        t.assert_nil(IDs.namespacedID(""))
        t.assert_nil(IDs.namespacedID("BAD"))     -- uppercase not allowed
        t.assert_nil(IDs.namespacedID("a-b"))     -- hyphen not allowed
    end)
end)

t.run_all()
