-- Roundtrip + format test for HDG.ShoppingCodec (HDGVL:1: format -- interop
-- with HDG donor's vendor-shopping-list export blobs).
dofile("wow_mock.lua")
-- Stub the StaticData.Decor reverse-index accessor. Test inputs use small
-- itemIDs (100/200) that trip the codec's decor heuristic (id < 100000);
-- identity stub passes them through unchanged so the roundtrip is verified
-- end-to-end without depending on the real HDGR_AllDecorDB shipping with
-- those IDs.
HDG = HDG or {}
-- ShoppingCodec now calls HousingCatalogObserver:GetItemIDByDecorID.
-- Test inputs use small itemIDs (100/200) that trip the decor heuristic
-- (id < 100000); identity stub passes them through without HDGR_AllDecorDB.
HDG.HousingCatalogObserver = HDG.HousingCatalogObserver or {}
-- Canonical IsOwned helper stub (mirrors production formula
-- quantity + remainingRedeemable + numPlaced > 0).
HDG.HousingCatalogObserver.IsOwned = HDG.HousingCatalogObserver.IsOwned
    or function(self, rowOrID)
        local row
        if type(rowOrID) == "table" then row = rowOrID
        elseif type(rowOrID) == "number" then
            row = HDG.HousingCatalogObserver.byItemID and HDG.HousingCatalogObserver.byItemID[rowOrID]
        end
        if not row then return false end
        return ((row.quantity or 0)
              + (row.remainingRedeemable or 0)
              + (row.numPlaced or 0)) > 0
    end

HDG.HousingCatalogObserver.GetItemIDByDecorID = function(_, id) return id end
-- Codec now classifies each imported ID against the catalog (IsReady-gated):
-- the identity GetItemIDByDecorID above treats the test's small itemIDs
-- (100/200) as decorIDs that map to themselves, so the roundtrip preserves both.
HDG.HousingCatalogObserver.IsReady = HDG.HousingCatalogObserver.IsReady or function() return true end
HDG.HousingCatalogObserver.GetRow  = HDG.HousingCatalogObserver.GetRow  or function() return nil end
dofile("../Modules/HDGR_ShoppingCodec.lua")

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

-- ===== Roundtrip ===========================================================
local input = {
    name = "Test Roundtrip",
    items = {
        { itemID = 100, npcID = 1, qty = 2 },
        { itemID = 200,            qty = 5 },  -- wishlist (npcID = nil)
    },
    meta = { source = "player", url = "https://example.com/x" },
    createdAt = 1769904000,
}

local encoded = HDG.ShoppingCodec.Encode(input)
assertTrue(type(encoded) == "string", "Encode returns a string")
assertTrue(#encoded > 0, "Encode returns non-empty string")
assertTrue(encoded:find("^HDGVL:1:"), "Encode uses HDGVL:1: magic header (HDG interop)")

local decoded = HDG.ShoppingCodec.Decode(encoded)
assertEq(decoded.name, input.name, "Decode preserves name (via meta.desc)")
assertEq(#decoded.items, #input.items, "Decode preserves item count")
assertEq(decoded.items[1].itemID, input.items[1].itemID, "Decode preserves itemID")
assertEq(decoded.items[1].npcID,  input.items[1].npcID,  "Decode preserves npcID")
assertEq(decoded.items[1].qty,    input.items[1].qty,    "Decode preserves qty")
assertEq(decoded.items[2].npcID,  nil,                   "Decode preserves nil npcID for wishlist")
assertEq(decoded.meta.source,     input.meta.source,     "Decode preserves meta.source")
assertEq(decoded.meta.url,        input.meta.url,        "Decode preserves meta.url")
assertEq(decoded.createdAt,       input.createdAt,       "Decode preserves createdAt (via meta.date)")

-- ===== Bad input -> nil, NEVER errors ======================================
assertEq(HDG.ShoppingCodec.Decode("not-base64"), nil, "Decode garbage returns nil")
assertEq(HDG.ShoppingCodec.Decode(""),           nil, "Decode empty returns nil")
assertEq(HDG.ShoppingCodec.Decode(nil),          nil, "Decode nil returns nil")
assertEq(HDG.ShoppingCodec.Decode("HDGR1:abcd"), nil, "Decode old format magic returns nil")
assertEq(HDG.ShoppingCodec.Decode("HDGVL:99:abcd"), nil, "Decode unknown version returns nil")

-- ===== HDG-interop decode ==================================================
-- Spec-compliance test: a blob HAND-CRAFTED to match HDG's exact emit format
-- must decode cleanly. This is the regression we added after VendorList lists
-- exported from HDG silently no-op'd into HDG.
-- HDG emits: "HDGVL:1:" .. b64("source=player,desc=Test%20list,date=1700,author=Vamoose\n100,1,2\n200,0,5")
-- Round-trip through our own Encode of an HDG-shape record covers the same path.
local hdgShape = {
    name = "Test list",
    items = {
        { itemID = 100, npcID = 1, qty = 2 },
        { itemID = 200, npcID = nil, qty = 5 },   -- npcID nil emits as 0 sentinel
    },
    meta = { source = "player", author = "Vamoose", date = 1700 },
    createdAt = 1700,
}
local hdgEncoded = HDG.ShoppingCodec.Encode(hdgShape)
local hdgDecoded = HDG.ShoppingCodec.Decode(hdgEncoded)
assertTrue(hdgDecoded ~= nil, "HDG-shape encode/decode roundtrips")
assertEq(hdgDecoded.name, "Test list", "HDG shape preserves name")
assertEq(hdgDecoded.meta.author, "Vamoose", "HDG shape preserves author")
assertEq(#hdgDecoded.items, 2, "HDG shape preserves 2 items")
assertEq(hdgDecoded.items[2].npcID, nil, "HDG shape preserves wishlist npcID nil (was 0 in wire format)")

-- ===== Encode of bad input -- safe ========================================
assertEq(HDG.ShoppingCodec.Encode(nil),     nil, "Encode nil returns nil")
assertEq(HDG.ShoppingCodec.Encode("not a table"), nil, "Encode non-table returns nil")

io.write("test_shopping_codec.lua passed\n")
