-- Background loader for ItemDB binary (v3):
-- - Global salvage table per rarity
-- - Per-item description (serverId)
-- - Per-item sell gold (max across NPCs)
-- - Per-item weaponType + ammoType + slotType enums (u8 each)

local DEBUG = false
if isLocalEnv and isLocalEnv() then DEBUG = true end

local function dlog(fmt, ...)
    if not DEBUG then return end
    local ok, msg = pcall(string.format, fmt, ...)
    g_logger.info('[itemdb] ' .. (ok and msg or fmt))
end

local function ilog(fmt, ...)
    local ok, msg = pcall(string.format, fmt, ...)
    g_logger.info('[itemdb] ' .. (ok and msg or fmt))
end

local API_PATH = '/resources/itemdb/itemdb.bin'

local DESC = {} -- serverId -> string
local GOLD = {} -- serverId -> gold (u32)
local SALV = {} -- rarity -> { {item_id, count}, ... }
local WEAPON = {} -- serverId -> weaponType enum (u8)
local AMMO = {} -- serverId -> ammoType enum (u8)
local SLOT = {} -- serverId -> slotType enum (u8)

local function parseBaseUrl()
    if Services and type(Services.apiBase) == 'string' and Services.apiBase ~= '' then
        return Services.apiBase
    end
    local updater = (Services and Services.updater) or ''
    if type(updater) ~= 'string' then updater = '' end
    local proto, host = updater:match('^(https?://)([^/]+)/')
    if proto and host then return proto .. host end
    return 'https://localhost'
end

-- ItemDB v3 only: 'RIDB' (4) + u8 version=3
--          + u8 nRarities
--            repeat nRarities: u8 nameLen + name + u8 nItems + (u16 itemId, u16 count)*nItems
--          + u32 count
--            repeat count: u16 serverId, u8 weaponType, u8 ammoType, u8 slotType, u16 descLen, bytes, u32 gold
-- No backward compatibility: version must be 3.
local function parseItemDB(payload)
    if not payload or #payload < 5 then return nil, 'short' end
    if payload:sub(1, 4) ~= 'RIDB' then return nil, 'magic' end
    local function u8(i) return payload:byte(i) end
    local function u16le(i)
        local b1, b2 = payload:byte(i, i + 1); return b1 + b2 * 256
    end
    local function u32le(i)
        local b1, b2, b3, b4 = payload:byte(i, i + 3); return b1 + b2 * 256 + b3 * 65536 + b4 * 16777216
    end
    local ver = u8(5); if ver ~= 3 then return nil, 'version' end
    local i = 6
    local salv = {}
    local nr = u8(i); i = i + 1
    for _ = 1, nr do
        local nl = u8(i); i = i + 1
        local name = payload:sub(i, i + nl - 1); i = i + nl
        local ni = u8(i); i = i + 1
        local arr = {}
        for __ = 1, ni do
            local iid = u16le(i); i = i + 2
            local ct  = u16le(i); i = i + 2
            table.insert(arr, { item_id = iid, count = ct })
        end
        salv[name] = arr
    end
    if i + 3 > #payload then return nil, 'entries_short' end
    local count = u32le(i); i = i + 4
    local desc = {}; local gold = {}; local weapon = {}; local ammo = {}; local slot = {}
    for _ = 1, count do
        if i + 1 > #payload then break end
        local sid = u16le(i); i = i + 2
        local w, a, st = 0, 0, 0
        if i + 2 > #payload then break end
        w = u8(i); i = i + 1
        a = u8(i); i = i + 1
        st = u8(i); i = i + 1
        local dl = u16le(i); i = i + 2
        if i + dl + 3 > #payload then break end
        local d = (dl > 0) and payload:sub(i, i + dl - 1) or ''
        i = i + dl
        local g = u32le(i); i = i + 4
        if sid > 0 then
            if d ~= '' then desc[sid] = d end
            if g and g > 0 then gold[sid] = g end
            weapon[sid] = w
            ammo[sid] = a
            slot[sid] = st
        end
    end
    return { salvage = salv, desc = desc, gold = gold, weapon = weapon, ammo = ammo, slot = slot, version = ver }
end

local function tryLoadLocal()
    if not (g_app and g_app.getBuildArch and g_app.getBuildArch() == 'WASM32') then
        return false
    end
    local path = '/resources/itemdb/itemdb.bin'
    if not g_resources.fileExists(path) then return false end
    local ok, data = pcall(g_resources.readFileContents, path)
    if not ok or not data or data == '' then return false end
    local obj, err = parseItemDB(data)
    if type(obj) ~= 'table' then
        ilog('local itemdb parse failed (%s)', tostring(err))
        return false
    end
    SALV = obj.salvage or {}
    DESC = obj.desc or {}
    GOLD = obj.gold or {}
    WEAPON = obj.weapon or {}
    AMMO = obj.ammo or {}
    SLOT = obj.slot or {}
    ilog('ItemDB loaded from %s: %d items, %d salvage entries', path,
        (function()
            local c = 0
            for _ in pairs(DESC) do c = c + 1 end
            return c
        end)(),
        (function()
            local c = 0
            for _ in pairs(SALV) do c = c + 1 end
            return c
        end)())
    return true
end

local function fetchAsync()
    if tryLoadLocal() then return end
    local url = parseBaseUrl() .. API_PATH
    ilog('fetching ItemDB: %s', url)
    HTTP.get(url, function(data, err)
        if err or not data or data == '' then
            ilog('download error: %s', tostring(err))
            return
        end
        local obj, perr = parseItemDB(data)
        if type(obj) ~= 'table' then
            ilog('ItemDB parse failed: %s', tostring(perr))
            return
        end
        SALV = obj.salvage or {}
        DESC = obj.desc or {}
    GOLD = obj.gold or {}
    WEAPON = obj.weapon or {}
    AMMO = obj.ammo or {}
    SLOT = obj.slot or {}
        local descCount = (function()
            local c = 0
            for _ in pairs(DESC) do c = c + 1 end
            return c
        end)()
        local goldCount = (function()
            local c = 0
            for _ in pairs(GOLD) do c = c + 1 end
            return c
        end)()
        ilog('ItemDB loaded (v%d): %d items, %d with sell prices', obj.version or 0, descCount, goldCount)
    end)
end

local function onGameStart()
    fetchAsync()
end

-- Public API
_G.itemdb = _G.itemdb or {}

function _G.itemdb.desc(serverId)
    if not serverId then return nil end
    return DESC[serverId]
end

function _G.itemdb.sellGold(serverId)
    if not serverId then return 0 end
    return GOLD[serverId] or 0
end

function _G.itemdb.weaponType(serverId)
    if not serverId then return 0 end
    return WEAPON[serverId] or 0
end

function _G.itemdb.ammoType(serverId)
    if not serverId then return 0 end
    return AMMO[serverId] or 0
end

function _G.itemdb.slotType(serverId)
    if not serverId then return 0 end
    return SLOT[serverId] or 0
end

function _G.itemdb.salvage(rarity)
    if not rarity then return {} end
    return SALV[rarity] or SALV[rarity:lower()] or {}
end

function init()
    connect(g_game, { onGameStart = onGameStart })
    if g_game.isOnline and g_game.isOnline() then onGameStart() end
end

function terminate()
    disconnect(g_game, { onGameStart = onGameStart })
    DESC = {}; GOLD = {}; SALV = {}; WEAPON = {}; SLOT = {}
end
