-- HousingDecorGuide/Core/HDG_ProjectsIDs.lua
-- Pure helpers for Projects module ID minting and path key parsing.
-- No WoW API calls — testable in vanilla Lua.

-- Support both addon-loader varargs and standalone require
local _, privateDB = ...
local M = {}

local VALID_FACTIONS = { alliance = true, horde = true }

function M.makeHouseID(faction)
    if type(faction) ~= "string" or faction == "" then return nil end
    local lower = faction:lower()
    if not VALID_FACTIONS[lower] then return nil end
    return "house:" .. lower
end

function M.parseHouseID(key)
    if type(key) ~= "string" then return nil end
    local faction = key:match("^house:(%w+)$")
    if not faction or not VALID_FACTIONS[faction] then return nil end
    return faction
end

local SHORT_UUID_PATTERN = "^[0-9a-f]+$"

function M.makeFloorID(houseID, floor)
    if type(houseID) ~= "string" then return nil end
    if type(floor) ~= "number" then return nil end
    if floor < 1 or floor ~= math.floor(floor) then return nil end
    if not M.parseHouseID(houseID) then return nil end
    return houseID .. ":f" .. tostring(floor)
end

function M.makeRoomID(floorID, shortUUID)
    if type(floorID) ~= "string" or type(shortUUID) ~= "string" then return nil end
    if shortUUID == "" or not shortUUID:match(SHORT_UUID_PATTERN) then return nil end
    return floorID .. ":r" .. shortUUID
end

function M.makeCrateID(roomID, shortUUID)
    if type(roomID) ~= "string" or type(shortUUID) ~= "string" then return nil end
    if shortUUID == "" or not shortUUID:match(SHORT_UUID_PATTERN) then return nil end
    return roomID .. ":c" .. shortUUID
end

function M.parsePath(key)
    if type(key) ~= "string" then return nil end
    -- Try fullest match first (crate), narrow downward
    local faction, floorStr, roomShort, crateShort =
        key:match("^house:(%w+):f(%d+):r([0-9a-f]+):c([0-9a-f]+)$")
    if faction and VALID_FACTIONS[faction] then
        return {
            kind = "crate",
            faction = faction,
            floor = tonumber(floorStr),
            roomShortID = roomShort,
            crateShortID = crateShort,
            houseID = "house:" .. faction,
            floorID = "house:" .. faction .. ":f" .. floorStr,
            roomID = "house:" .. faction .. ":f" .. floorStr .. ":r" .. roomShort,
            crateID = key,
        }
    end

    faction, floorStr, roomShort = key:match("^house:(%w+):f(%d+):r([0-9a-f]+)$")
    if faction and VALID_FACTIONS[faction] then
        return {
            kind = "room",
            faction = faction,
            floor = tonumber(floorStr),
            roomShortID = roomShort,
            houseID = "house:" .. faction,
            floorID = "house:" .. faction .. ":f" .. floorStr,
            roomID = key,
        }
    end

    faction, floorStr = key:match("^house:(%w+):f(%d+)$")
    if faction and VALID_FACTIONS[faction] then
        return {
            kind = "floor",
            faction = faction,
            floor = tonumber(floorStr),
            houseID = "house:" .. faction,
            floorID = key,
        }
    end

    faction = key:match("^house:(%w+)$")
    if faction and VALID_FACTIONS[faction] then
        return { kind = "house", faction = faction, houseID = key }
    end

    return nil
end

-- 4-char hex UUID is sufficient for room/crate within a single house (~16k space).
-- Collisions are detected at mint-time and re-rolled; see Store.MintRoomID.

local function _hexChar()
    return string.format("%x", math.random(0, 15))
end

function M.shortUUID(len)
    len = len or 4
    if type(len) ~= "number" or len < 1 then return nil end
    local out = {}
    for i = 1, len do out[i] = _hexChar() end
    return table.concat(out)
end

local NAMESPACE_PATTERN = "^[a-z]+$"
local NAMESPACE_DEFAULT_LEN = 8

function M.namespacedID(prefix, len)
    if type(prefix) ~= "string" or prefix == "" then return nil end
    if not prefix:match(NAMESPACE_PATTERN) then return nil end
    return prefix .. "_" .. M.shortUUID(len or NAMESPACE_DEFAULT_LEN)
end

-- Expose to addon namespace (when loaded by WoW)
if privateDB then
    privateDB.HDG_ProjectsIDs = M
end

-- Expose to test harness (when loaded by dofile/require in tests)
return M
