---@diagnostic disable-next-line: undefined-field
local __FILE__ = tostring(debugstack(1, 2, 0):match("(.*):2:")) -- Always check line number in regexp and file
local MAJOR, MINOR = "LibItemUpgradeInfo-1.0", 36
local type, tonumber, select, strsplit, GetItemInfoFromHyperlink = type, tonumber, select, strsplit,
	GetItemInfoFromHyperlink
---@diagnostic disable-next-line: deprecated
local unpack, GetDetailedItemLevelInfo = unpack, C_Item.GetDetailedItemLevelInfo
---@class lib
---@diagnostic disable-next-line: assign-type-mismatch
local lib, previous = _G.LibStub:NewLibrary(MAJOR, MINOR)
if not lib then return end
local print = print
if (_G.AlarDbg) then
	_G.AlarMessage('Loaded from' .. __FILE__ .. ' over ' .. previous)
else
	print = function() end
end
--[[
Caching system
1	itemName	String	The name of the item.
2	itemLink	String	The item link of the item.
3	itemRarity	Number	The quality of the item. The value is 0 to 7, which represents Poor to Heirloom. This appears to include gains from upgrades/bonuses.
4	itemLevel	Number	The item level of this item, not including item levels gained from upgrades. There is currently no API to get the item level including upgrades/bonuses.
5	itemMinLevel	Number	The minimum level required to use the item, 0 meaning no level requirement.
6	itemType	String	The type of the item: Armor, Weapon, Quest, Key, etc.
7	itemSubType	String	The sub-type of the item: Enchanting, Cloth, Sword, etc. See itemType.
8	itemStackCount	Number	How many of the item per stack: 20 for Runecloth, 1 for weapon, 100 for Alterac Ram Hide, etc.
9	itemEquipLoc	String	The type of inventory equipment location in which the item may be equipped, or "" if it can't be equippable. The string returned is also the name of a global string variable e.g. if "INVTYPE_WEAPONMAINHAND" is returned, _G["INVTYPE_WEAPONMAINHAND"] will be the localized, displayable name of the location.
10	iconFileDataID	Number	The FileDataID for the icon texture for the item.
11	itemSellPrice	Number	The price, in copper, a vendor is willing to pay for this item, 0 for items that cannot be sold.
12	itemClassID	Number	This is the numerical value that determines the string to display for 'itemType'.
13	itemSubClassID	Number	This is the numerical value that determines the string to display for 'itemSubType'
14	bindType	Number	Item binding type: 0 - none; 1 - on pickup; 2 - on equip; 3 - on use; 4 - quest.
15	expacID	Number
16	itemSetID	Number
17	isCraftingReagent bool
--]]
-- ItemLink Constants
local i_Name = 1
local i_Link = 2
local i_Rarity = 3
local i_Quality = 3
local i_Level = 4
local i_MinLevel = 5
local i_ClassName = 6
local i_SubClassName = 7
local i_StackCount = 8
local i_EquipLoc = 9
local i_TextureId = 10
local i_SellPrice = 11
local i_ClassID = 12
local i_SubClass_ID = 13
local i_unk1 = 14
local i_unk2 = 15
local i_unk3 = 16
local i_unk4 = 17
local LE_ITEM_QUALITY_ARTIFACT = Enum.ItemQuality.Artifact
local LE_ITEM_QUALITY_HEIRLOOM = Enum.ItemQuality.Heirloom
do
	local oGetItemInfo = GetItemInfo
	lib.itemcache = lib.itemcache or
		setmetatable({ miss = 0, tot = 0 }, {
			__index = function(table, key)
				if (not key) then return "" end
				if (key == "miss") then return 0 end
				if (key == "tot") then return 0 end
				local cached = { oGetItemInfo(key) }
				if #cached == 0 then return nil end
				local itemLink = cached[2]
				if not itemLink then return nil end
				local itemID = lib:GetItemID(itemLink)
				local quality = cached[3]
				local cacheIt = true
				if quality == LE_ITEM_QUALITY_ARTIFACT then
					local relic1, relic2, relic3 = select(4, strsplit(':', itemLink))
					if relic1 and relic1 ~= '' and not oGetItemInfo(relic1) then cacheIt = false end
					if relic2 and relic2 ~= '' and not oGetItemInfo(relic2) then cacheIt = false end
					if relic3 and relic3 ~= '' and not oGetItemInfo(relic3) then cacheIt = false end
				end
				cached.englishClass = C_Item.GetItemClassInfo(cached[12])
				cached.englishSubClass = C_Item.GetItemSubClassInfo(cached[12], cached[13])
				if cacheIt then
					rawset(table, key, cached)
				end
				table.miss = table.miss + 1
				return cached
			end

		})
end
local cache = lib.itemcache
local function CachedGetItemInfo(key, index)
	if not key then return nil end
	index = index or 1
	cache.tot = cache.tot + 1
	local cached = cache[key]
	if cached and type(cached) == 'table' then
		return select(index, unpack(cached))
	else
		rawset(cache, key, nil) -- voiding broken cache entry
	end
end

local upgradeTable = {
	[1] = { upgrade = 1, max = 1, ilevel = 8 },
	[373] = { upgrade = 1, max = 3, ilevel = 4 },
	[374] = { upgrade = 2, max = 3, ilevel = 8 },
	[375] = { upgrade = 1, max = 3, ilevel = 4 },
	[376] = { upgrade = 2, max = 3, ilevel = 4 },
	[377] = { upgrade = 3, max = 3, ilevel = 4 },
	[378] = { ilevel = 7 },
	[379] = { upgrade = 1, max = 2, ilevel = 4 },
	[380] = { upgrade = 2, max = 2, ilevel = 4 },
	[445] = { upgrade = 0, max = 2, ilevel = 0 },
	[446] = { upgrade = 1, max = 2, ilevel = 4 },
	[447] = { upgrade = 2, max = 2, ilevel = 8 },
	[451] = { upgrade = 0, max = 1, ilevel = 0 },
	[452] = { upgrade = 1, max = 1, ilevel = 8 },
	[453] = { upgrade = 0, max = 2, ilevel = 0 },
	[454] = { upgrade = 1, max = 2, ilevel = 4 },
	[455] = { upgrade = 2, max = 2, ilevel = 8 },
	[456] = { upgrade = 0, max = 1, ilevel = 0 },
	[457] = { upgrade = 1, max = 1, ilevel = 8 },
	[458] = { upgrade = 0, max = 4, ilevel = 0 },
	[459] = { upgrade = 1, max = 4, ilevel = 4 },
	[460] = { upgrade = 2, max = 4, ilevel = 8 },
	[461] = { upgrade = 3, max = 4, ilevel = 12 },
	[462] = { upgrade = 4, max = 4, ilevel = 16 },
	[465] = { upgrade = 0, max = 2, ilevel = 0 },
	[466] = { upgrade = 1, max = 2, ilevel = 4 },
	[467] = { upgrade = 2, max = 2, ilevel = 8 },
	[468] = { upgrade = 0, max = 4, ilevel = 0 },
	[469] = { upgrade = 1, max = 4, ilevel = 4 },
	[470] = { upgrade = 2, max = 4, ilevel = 8 },
	[471] = { upgrade = 3, max = 4, ilevel = 12 },
	[472] = { upgrade = 4, max = 4, ilevel = 16 },
	[491] = { upgrade = 0, max = 4, ilevel = 0 },
	[492] = { upgrade = 1, max = 4, ilevel = 4 },
	[493] = { upgrade = 2, max = 4, ilevel = 8 },
	[494] = { upgrade = 0, max = 6, ilevel = 0 },
	[495] = { upgrade = 1, max = 6, ilevel = 4 },
	[496] = { upgrade = 2, max = 6, ilevel = 8 },
	[497] = { upgrade = 3, max = 6, ilevel = 12 },
	[498] = { upgrade = 4, max = 6, ilevel = 16 },
	[503] = { upgrade = 3, max = 3, ilevel = 1 },
	[504] = { upgrade = 3, max = 4, ilevel = 12 },
	[505] = { upgrade = 4, max = 4, ilevel = 16 },
	[506] = { upgrade = 5, max = 6, ilevel = 20 },
	[507] = { upgrade = 6, max = 6, ilevel = 24 },
	[529] = { upgrade = 0, max = 2, ilevel = 0 },
	[530] = { upgrade = 1, max = 2, ilevel = 5 },
	[531] = { upgrade = 2, max = 2, ilevel = 10 },
	[535] = { upgrade = 1, max = 3, ilevel = 15 },
	[536] = { upgrade = 2, max = 3, ilevel = 30 },
	[537] = { upgrade = 3, max = 3, ilevel = 45 },
	[538] = { upgrade = 0, max = 3, ilevel = 0 },

}
do
	local stub = { ilevel = 0 }
	setmetatable(upgradeTable, {
		__index = function(t, key)
			return stub
		end
	})
end
-- Tooltip Scanning stuff
---@diagnostic disable-next-line: undefined-field
local itemLevelPattern = _G.ITEM_LEVEL:gsub("%%d", "(%%d+)")
---@diagnostic disable-next-line: undefined-field
local soulboundPattern = _G.ITEM_SOULBOUND
---@diagnostic disable-next-line: undefined-field
local boePattern = _G.ITEM_BIND_ON_EQUIP
---@diagnostic disable-next-line: undefined-field
local bopPattern = _G.ITEM_BIND_ON_PICKUP
---@diagnostic disable-next-line: undefined-field
local boaPattern1 = _G.ITEM_BIND_TO_BNETACCOUNT
---@diagnostic disable-next-line: undefined-field
local boaPattern2 = _G.ITEM_BNETACCOUNTBOUND
local patterns = {
	[soulboundPattern] = "soulbound",
	[boePattern] = "boe",
	[bopPattern] = "bop",
	[boaPattern1] = "boa",
	[boaPattern2] = "boa",
}

local scanningTooltip
local anchor
lib.tipCache = lib.tipCache or setmetatable({}, { __index = function(table, key) return {} end })
local tipCache = lib.tipCache
local emptytable = {}

local function NewScanTip(itemLink, unit, show)
	if type(itemLink) == "string" then
		itemLink = lib:GetItemID(itemLink)
	end
	local key = 'nokey'
	if (unit) then
		local name, realm = UnitName(unit)
		local level = UnitLevel(unit)
		key = format("%s:%s:%d:%d", name, realm or 'local', level, itemLink)
	end
	if key == 'nokey' or type(tipCache[key].ilevel) == "nil" or not tipCache[key].cached then
		local cacheIt = key and true or false
		if not scanningTooltip then
			anchor = CreateFrame("Frame")
			anchor:Hide()
			scanningTooltip = _G.CreateFrame("GameTooltip", "LibItemUpgradeInfoTooltip", nil, "GameTooltipTemplate")
		end
		--scanningTooltip:ClearLines()
		GameTooltip_SetDefaultAnchor(scanningTooltip, MultiBarBottomRightButton12)
		local rc, message
		if (unit == 'player') then -- local rc,message=pcall(scanningTooltip.SetHyperlink,scanningTooltip,itemString)
			rc, message = pcall(scanningTooltip.SetOwnedItemByID, scanningTooltip, itemLink)
		else
			rc, message = pcall(scanningTooltip.SetItemByID, scanningTooltip, itemLink)
		end
		if (not rc) then
			return { empty = message }
		end
		scanningTooltip:Show()

		if show then
			for i = 1, 12 do
				local l, ltext = _G["LibItemUpgradeInfoTooltipTextLeft" .. i], nil
				local r, rtext = _G["LibItemUpgradeInfoTooltipTextRight" .. i], nil
				if l then
					ltext = l:GetText()
					rtext = r:GetText()
					_G.print(i, ltext, ' - ', rtext)
				end
			end
		end
		tipCache[key] = {
			ilevel = nil,
			soulbound = nil,
			bop = nil,
			boe = nil,
			boa = nil,
			cached = cacheIt
		}
		local c = tipCache[key]
		for i = 2, 6 do
			local label, text = _G["LibItemUpgradeInfoTooltipTextLeft" .. i], nil
			if label then text = label:GetText() end
			if text then
				if show then _G.print("|cFFFFFF00" .. text .. "|r") end
				if c.ilevel == nil then c.ilevel = tonumber(text:match(itemLevelPattern)) end
				for pattern, key in pairs(patterns) do
					if type(c[key]) == "nil" then
						if text:find(pattern) then
							if show then _G.print(text, "matched", pattern) end
							c[key] = true
						end
					end
				end
			end
		end

		scanningTooltip:Hide()
	end
	return tipCache[key]
end
local function ScanTip(itemID, unit, show)
	if type(itemID) == "string" then
		itemID = lib:GetItemID(itemID)
	end
	local key = 'nokey'
	if (unit) then
		local name, realm = UnitName(unit)
		local level = UnitLevel(unit)
		key = format("%s:%s:%d:%d", name, realm or 'local', level, itemID)
	end
	if true or key == 'nokey' or type(tipCache[key].ilevel) == "nil" or not tipCache[key].cached then
		local cacheIt = key and true or false
		if not scanningTooltip then
			anchor = CreateFrame("Frame")
			anchor:Hide()
			scanningTooltip = _G.CreateFrame("GameTooltip", "LibItemUpgradeInfoTooltip", nil, "GameTooltipTemplate")
		end
		--scanningTooltip:ClearLines()
		GameTooltip_SetDefaultAnchor(scanningTooltip, MultiBarBottomRightButton12)
		local rc, message
		if (unit == 'player') then -- local rc,message=pcall(scanningTooltip.SetHyperlink,scanningTooltip,itemString)
			rc, message = pcall(scanningTooltip.SetOwnedItemByID, scanningTooltip, itemID)
		else
			rc, message = pcall(scanningTooltip.SetItemByID, scanningTooltip, itemID)
		end
		if (not rc) then
			return { empty = message }
		end
		scanningTooltip:Show()

		if show then
			for i = 1, 12 do
				local l, ltext = _G["LibItemUpgradeInfoTooltipTextLeft" .. i], nil
				local r, rtext = _G["LibItemUpgradeInfoTooltipTextRight" .. i], nil
				if l then
					ltext = l:GetText()
					rtext = r:GetText()
					_G.print(i, ltext, ' - ', rtext)
				end
			end
		end
		tipCache[key] = {
			ilevel = nil,
			soulbound = nil,
			bop = nil,
			boe = nil,
			boa = nil,
			cached = cacheIt
		}
		local c = tipCache[key]
		for i = 2, 6 do
			local label, text = _G["LibItemUpgradeInfoTooltipTextLeft" .. i], nil
			if label then text = label:GetText() end
			if text then
				if show then _G.print("|cFFFFFF00" .. text .. "|r") end
				if c.ilevel == nil then c.ilevel = tonumber(text:match(itemLevelPattern)) end
				for pattern, key in pairs(patterns) do
					if type(c[key]) == "nil" then
						if text:find(pattern) then
							if show then _G.print(text, "matched", pattern) end
							c[key] = true
						end
					end
				end
			end
		end
		if (not show) then
			scanningTooltip:Hide()
		else
			DevTools_Dump(tipCache[key])
		end
	end
	return tipCache[key]
end


-- GetUpgradeID(itemString)
--
-- Arguments:
--   itemString - String - An itemLink or itemString denoting the item
--
-- Returns:
--   Number - The upgrade ID (possibly 0), or nil if the input is invalid or
--            does not contain upgrade info
function lib:GetUpgradeID(itemString)
	if type(itemString) ~= "string" then return end
	local itemString = itemString:match("item[%-?%d:]+") or "" -- Standardize itemlink to itemstring
	local instaid, _, numBonuses, affixes = select(12, strsplit(":", itemString, 15))
	instaid = tonumber(instaid) or 7
	numBonuses = tonumber(numBonuses) or 0
	if instaid > 0 and (instaid - 4) % 8 == 0 then
		return tonumber((select(numBonuses + 1, strsplit(":", affixes or ''))))
	end
end

-- GetCurrentUpgrade(id)
--
-- Returns the current upgrade level of the item, e.g. 1 for a 1/2 item.
--
-- Arguments:
--   id - Number - The upgrade ID of the item (obtained via GetUpgradeID())
--
-- Returns:
--   Number - The current upgrade level of the item. Returns nil if the item
--            cannot be upgraded
function lib:GetCurrentUpgrade(id)
	return upgradeTable[id].upgrade
end

-- GetMaximumUpgrade(id)
--
-- Returns the maximum upgrade level of the item, e.g. 2 for a 1/2 item.
--
-- Arguments:
--   id - Number - The upgrade ID of the item (obtained via GetUpgradeID())
--
-- Returns:
--   Number - The maximum upgrade level of the item. Returns nil if the item
--            cannot be upgraded
function lib:GetMaximumUpgrade(id)
	return upgradeTable[id].max
end

-- GetItemLevelUpgrade(id)
--
-- Returns the item level increase that this upgrade is worth, e.g. 4 for a
-- 1/2 item or 8 for a 2/2 item.
--
-- Arguments:
--   id - Number - The upgrade ID of the item (obtained via GetUpgradeID())
--
-- Returns:
--   Number - The item level increase of the item. Returns 0 if the item
--            cannot be or has not been upgraded
function lib:GetItemLevelUpgrade(id)
	return upgradeTable[id].ilevel
end

-- GetItemUpgradeInfo(itemString)
--
-- Returns the current upgrade level, maximum upgrade level, and item level
-- increase for an item.
--
-- Arguments:
--   itemString - String - An itemLink or itemString denoting the item
--
-- Returns if the item can be upgraded:
--   Number - The current upgrade level of the item
--   Number - The maximum upgrade level of the item
--   Number - The item level increase of the item
-- or if the item cannot be upgraded:
--   nil
--   nil
--   0
-- or if the item is invalid or does not contain upgrade info:
--   nil
function lib:GetItemUpgradeInfo(itemString)
	local id = self:GetUpgradeID(itemString)
	if id then
		local cur = self:GetCurrentUpgrade(id)
		local max = self:GetMaximumUpgrade(id)
		local delta = self:GetItemLevelUpgrade(id)
		return cur, max, delta
	end
	return nil
end

-- GetHeirloomTrueLevel(itemString)
--
-- Returns the true item level for an heirloom (actually, returns the true level for any adapting item)
--
-- Arguments:
--   itemString - String - An itemLink or itemString denoting the item
--
-- Returns:
--   Number, Boolean - The true item level of the item. If the item is not
--                     an heirloom, or an error occurs when trying to scan the
--                     item tooltip, the second return value is false. Otherwise
--                     the second return value is true. If the input is invalid,
--                     (nil, false) is returned.
-- Convert the ITEM_LEVEL constant into a pattern for our use
function lib:GetHeirloomTrueLevel(itemString)
	if type(itemString) ~= "string" then return nil, false end
	local _, itemLink, rarity, itemLevel = CachedGetItemInfo(itemString)
	if (not itemLink) then
		return nil, false
	end
	local rc = ScanTip(itemLink, itemLevel)
	if rc.ilevel then
		return rc.ilevel, true
	end
	return itemLevel, false
end

-- GetUpgradedItemLevel(itemString)
--
-- Returns the true item level of the item, including upgrades and heirlooms.
--
-- Arguments:
--   itemString - String - An itemLink or itemString denoting the item
--   unit - String - Optional The unit to scan the item for
--
-- Returns:
--   Number - The true item level of the item, or nil if the input is invalid
function lib:GetUpgradedItemLevel(itemString, unit)
	-- check for heirlooms first
	local c = ScanTip(itemString, unit)

	return c.ilevel, c
end

-- IsBop(itemString)
--
-- Check an item for  Bind On Pickup.
--
-- Arguments:

--   itemString - String - An itemLink or itemString denoting the item
--
-- Returns:
--   Boolean - True if Bind On Pickup

function lib:IsBop(itemString)
	local rc = ScanTip(itemString)
	return rc.bop
end

-- IsBoe(itemString)
--
-- Check an item for  Bind On Equip.
--
-- Arguments:
--   itemString - String - An itemLink or itemString denoting the item
--
-- Returns:
--   Boolean - True if Bind On Equip

function lib:IsBoe(itemString)
	local rc = ScanTip(itemString)
	return rc.boe
end

-- IsBoa(itemString)
--
-- Check an item for  Bind On Aaccount
--
-- Arguments:
--   itemString - String - An itemLink or itemString denoting the item
--
-- Returns:
--   Boolean - True if Bind On Equip

function lib:IsBoa(itemString)
	local rc = ScanTip(itemString)
	return rc.boa
end

-- IsArtifact(itemString)
--
-- Check an item for  Heirloom
--
-- Arguments:
--   itemString - String - An itemLink or itemString denoting the item
--
-- Returns:
--   Boolean - True if Artifact

function lib:IsArtifact(itemString)
	return CachedGetItemInfo(itemString, i_Quality) == LE_ITEM_QUALITY_ARTIFACT
end

-- GetClassInfoIsHeirloom(itemString)
--
-- Retrieve class and subclass
--
-- Arguments:
--   itemString - String - An itemLink or itemString denoting the item
--
-- Returns:
--   class,subclass


function lib:GetClassInfo(itemString)
	local rc = ScanTip(itemString)
	return rc.class, rc.subclass
end

-- IsHeirloom(itemString)
--
-- Check an item for  Heirloom
--
-- Arguments:
--   itemString - String - An itemLink or itemString denoting the item
--
-- Returns:
--   Boolean - True if Heirloom

function lib:IsHeirloom(itemString)
	return CachedGetItemInfo(itemString, i_Quality) == LE_ITEM_QUALITY_HEIRLOOM
end

---
-- Parses an itemlink and returns itemId without calling API again
-- @param #lib self
-- @param #string itemlink
-- @return #number itemId or 0
function lib:GetItemID(itemlink)
	if (type(itemlink) == "string") then
		local itemid, context = GetItemInfoFromHyperlink(itemlink)
		return tonumber(itemid) or 0
		--return tonumber(itemlink:match("Hitem:(%d+):")) or 0
	else
		return 0
	end
end

---
--
-- Returns a caching version of GetItemInfo. Can be used to override the original one.
-- Adds a second parameter to directly retrieving a specific value
-- (Note: internally uses select so it's actually like calling select(n,GetItemInfo(itemID))
--
-- Arguments:
--   self #lib self
--
-- Returns:
--   #function The new function

--@do-not-package--
local slots = {
	INVSLOT_AMMO     = _G.INVSLOT_AMMO,
	INVSLOT_HEAD     = _G.INVSLOT_HEAD,
	INVSLOT_NECK     = _G.INVSLOT_NECK,
	INVSLOT_SHOULDER = _G.INVSLOT_SHOULDER,
	INVSLOT_BODY     = _G.INVSLOT_BODY,
	INVSLOT_CHEST    = _G.INVSLOT_CHEST,
	INVSLOT_WAIST    = _G.INVSLOT_WAIST,
	INVSLOT_LEGS     = _G.INVSLOT_LEGS,
	INVSLOT_FEET     = _G.INVSLOT_FEET,
	INVSLOT_WRIST    = _G.INVSLOT_WRIST,
	INVSLOT_HAND     = _G.INVSLOT_HAND,
	INVSLOT_FINGER1  = _G.INVSLOT_FINGER1,
	INVSLOT_FINGER2  = _G.INVSLOT_FINGER2,
	INVSLOT_TRINKET1 = _G.INVSLOT_TRINKET1,
	INVSLOT_TRINKET2 = _G.INVSLOT_TRINKET2,
	INVSLOT_BACK     = _G.INVSLOT_BACK,
	INVSLOT_MAINHAND = INVSLOT_MAINHAND,
	INVSLOT_OFFHAND  = _G.INVSLOT_OFFHAND,
	INVSLOT_RANGED   = _G.INVSLOT_RANGED,
	INVSLOT_TABARD   = _G.INVSLOT_TABARD,
}
local INVSLOT_FIRST_EQUIPPED = _G.INVSLOT_FIRST_EQUIPPED;
local INVSLOT_LAST_EQUIPPED = _G.INVSLOT_LAST_EQUIPPED
_G.SLASH_CHECKSLOT1 = "/checkslot"
SlashCmdList['CHECKSLOT'] = function(args, chatframe)
	_G.print(args)
	local slot = strsplit(' ', args)
	if not slot or slot == "" then
		DevTools_Dump(slots)
		return
	end
	local slot = tonumber(slot)
	---@diagnostic disable-next-line: param-type-mismatch
	local itemlink = GetInventoryItemLink("player", slot)
	if itemlink then
		for k, v in pairs(slots) do
			if slot == v then
				_G.print("Item in ", k)
				break
			end
		end
		_G.print(itemlink)
		DevTools_Dump({ GetDetailedItemLevelInfo(itemlink) })
		print(itemlink)
		_G.print(lib:ScanTip(itemlink))
	end
end
--self.itemcache[itemLink]=nil
function lib:GetCachingGetItemInfo()
	return CachedGetItemInfo
end

function lib:GetCacheStats()
	local c = lib.itemcache
	local h = c.tot - c.miss
	local perc = (h > 0) and h / c.tot * 100 or 0
	return c.miss, h, perc
end

function lib:GetCache()
	return lib.itemcache
end

function lib:CleanCache()
	return wipe(lib.itemcache)
end

--[===========[ ]===========]
--[===[ Debug utilities ]===]
--[===========[ ]===========]

local function compareTables(t1, t2)
	local seen = {}
	for k, v1 in pairs(t1) do
		seen[k] = true
		local v2 = rawget(t2, k)
		if not v2 then return false end
		if type(v1) ~= type(v2) then return false end
		if type(v1) == "table" then
			if not compareTables(v1, v2) then return false end
		elseif v1 ~= v2 then
			return false
		end
	end
	for k in pairs(t2) do
		if not seen[k] then return false end
	end
	return true
end

-- prints the table rows in red and green
-- omits the lead { and the trailing }
local function printDiffTable(t1, t2)
	local keys, seen = {}, {}
	for k in pairs(t1) do
		keys[#keys + 1] = k
		seen[k] = true
	end
	for k in pairs(t2) do
		if not seen[k] then
			keys[#keys + 1] = k
		end
	end
	table.sort(keys)
	local function formatTable(t)
		local comps = {}
		for k, v in pairs(t) do
			comps[#comps + 1] = ("%s = %d"):format(k, v)
		end
		return "{ " .. table.concat(comps, ", ") .. " }"
	end
	for _, k in ipairs(keys) do
		local v1, v2 = rawget(t1, k), rawget(t2, k)
		local equal
		if type(v1) == "table" and type(v2) == "table" then
			equal = compareTables(v1, v2)
		else
			equal = v1 == v2
		end
		if not equal then
			if v1 then
				print(("|cffff0000    [%d] = %s,|r"):format(k, formatTable(v1)))
			end
			if v2 then
				print(("|cff00ff00    [%d] = %s,|r"):format(k, formatTable(v2)))
			end
		end
	end
end

-- Scans the first 10000 upgrade IDs
-- Run this with /run LibStub:GetLibrary("LibItemUpgradeInfo-1.0"):_CheckUpgradeTable()
-- If you don't have Aspirant's Staff of Harmony cached it may error out, just try again.
do
	local debugFrame
	local worker
	local newTable
	local debugTooltip
	function lib:_CheckUpgradeTable(itemLink)
		if worker then
			print("|cffff0000LibItemUpgradeInfo-1.0: upgrade check already in progress|r")
			return
		end
		if not debugFrame then
			debugFrame = _G.CreateFrame("frame")
			debugFrame:Hide()
			debugFrame:SetScript("OnUpdate", function()
				local ok, result, count, max = pcall(worker)
				if not ok or result then
					debugFrame:Hide()
					worker = nil
				end
				if not ok then
					print("|cffff0000LibItemUpgradeInfo-1.0 error: " .. result .. "|r")
				elseif result then
					print("LibItemUpgradeInfo-1.0: scan complete")
					if compareTables(upgradeTable, newTable) then
						print("LibItemUpgradeInfo-1.0: |cff00ff00No changes|r")
					else
						print("LibItemUpgradeInfo-1.0: |cffff0000New table:|r {")
						printDiffTable(upgradeTable, newTable)
						print("}")
					end
				else
					print("LibItemUpgradeInfo-1.0: scanning " .. count .. "/" .. max)
				end
			end)
		end
		if not debugTooltip then
			---@class debugTooltip: GameTooltip
			debugTooltip = _G.CreateFrame("GameTooltip", "LibItemUpgradeInfoDebugTooltip", nil, "GameTooltipTemplate")
			debugTooltip:SetOwner(_G.WorldFrame, "ANCHOR_NONE")
		end
		newTable = {}
		--local itemLink = "|cff0070dd|Hitem:89551:0:0:0:0:0:0:0:90:253:0:0:1:0|h[Aspirant's Staff of Harmony]|h|r"
		local itemLink = itemLink or
			"|cff0070dd|Hitem:89551:0:0:0:0:0:0:0:100:253:4:0:0:0|h[Aspirant's Staff of Harmony]|h|r"
		-- Livello è il 9,upgradeid il 14. Al decimo posto, un valore che deve essere 4 o 4+n *8) per far scattare l'uso dell'upgradeid
		local itemLevel = select(4, C_Item.GetItemInfo(itemLink))
		assert(itemLevel, "Can't find item level for itemLink")
		local count, max, batchsize = 0, 10000, 200
		worker = function()
			for i = count, math.min(max, count + batchsize) do
				local link = itemLink:gsub("%d+|h", i .. "|h")
				debugTooltip:ClearLines()
				debugTooltip:SetHyperlink(link)
				local upgrade, max
				local curLevel, maxLevel = _G.LibItemUpgradeInfoDebugTooltipTextLeft3:GetText():match(
					"^Upgrade Level: (%d+)/(%d+)")
				local ilvl = tonumber(_G.LibItemUpgradeInfoDebugTooltipTextLeft2:GetText():match("Item Level (%d+)"))
				if not ilvl then
					ilvl = tonumber(_G.LibItemUpgradeInfoDebugTooltipTextLeft3:GetText():match("Item Level (%d+)"))
				end
				assert(ilvl ~= nil,
					"Can't find ItemLevel in tooltip: " .. _G.LibItemUpgradeInfoDebugTooltipTextLeft2:GetText())
				if curLevel or maxLevel or ilvl ~= itemLevel then
					newTable[i] = { upgrade = tonumber(curLevel), max = tonumber(maxLevel), ilevel = ilvl - itemLevel }
				end
			end
			count = count + batchsize
			return (count > max), count, max
		end
		debugFrame:Show()
	end
end
--@end-do-not-package--

-- vim: set noet sw=4 ts=4:
