local MAJOR, MINOR = 'LibProcessable', 54
assert(LibStub, MAJOR .. ' requires LibStub')

local lib, oldMinor = LibStub:NewLibrary(MAJOR, MINOR)
if(not lib) then
	return
end

local CLASSIC = select(4, GetBuildInfo()) < 90000

local LE_ITEM_QUALITY_UNCOMMON = LE_ITEM_QUALITY_UNCOMMON or Enum.ItemQuality.Uncommon
local LE_ITEM_QUALITY_EPIC = LE_ITEM_QUALITY_EPIC or Enum.ItemQuality.Epic
local LE_ITEM_CLASS_ARMOR = LE_ITEM_CLASS_ARMOR or 4
local LE_ITEM_CLASS_WEAPON = LE_ITEM_CLASS_WEAPON or 2
local LE_ITEM_CLASS_GEM = LE_ITEM_CLASS_GEM or 3
local LE_ITEM_ARMOR_COSMETIC = LE_ITEM_ARMOR_COSMETIC or 5
local LE_ITEM_SUBCLASS_ARTIFACT = 11 -- no existing constant for this one
local LE_ITEM_EQUIPLOC_SHIRT = Enum and Enum.InventoryType and Enum.InventoryType.IndexBodyType or 4

local professions = {}
--[[ LibProcessable:IsMillable(_item[, ignoreMortar]_)
Returns whether the player can mill the given item.

**Arguments:**
* `item`: item ID or link
* `ignoreMortar`: whether the [Draenic Mortar](http://www.wowhead.com/item=114942) should be ignored or not _(boolean, optional)_

**Return values:**
* `isMillable`: Whether or not the player can mill the given item _(boolean)_
--]]
function lib:IsMillable(itemID, ignoreMortar)
	if(type(itemID) == 'string') then
		assert(string.match(itemID, 'item:(%d+):') or tonumber(itemID), 'item must be an item ID or item Link')
		itemID = (tonumber(itemID)) or (GetItemInfoFromHyperlink(itemID))
	end

	if(self:HasProfession(773)) then -- Inscription
		-- any herb can be milled at level 1
		return self.herbs[itemID]
	elseif(not ignoreMortar and GetItemCount(114942) > 0) then
		-- Draenic Mortar can mill Draenor herbs without a profession
		return itemID >= 109124 and itemID <= 109130, true
	end
end

--[[ LibProcessable:IsProspectable(_item_)
Returns whether the player can prospect the given item.

**Arguments:**
* `item`: item ID or link

**Return values:**
* `isProspectable`: Whether or not the player can prospect the given item _(boolean)_

**Notes**:
* This does not check if the player has the required skills to use the profession items
   * Only Outland and Pandaria ores have skill level requirements
--]]
function lib:IsProspectable(itemID)
	if(type(itemID) == 'string') then
		assert(string.match(itemID, 'item:(%d+):') or tonumber(itemID), 'item must be an item ID or item Link')
		itemID = (tonumber(itemID)) or (GetItemInfoFromHyperlink(itemID))
	end

	if(self:HasProfession(755)) then -- Jewelcrafting
		-- TODO: consider required skill for classic prospecting?
		return not not self.ores[itemID]
	end
end

--[[ LibProcessable:IsDisenchantable(_item_)
Returns whether the player can disenchant the given item.

**Arguments:**
* `item`: item ID or link

**Return values:**
* `isDisenchantable`: Whether or not the player can disenchant the given item _(boolean)_

**Notes**:
* Many items that are not disenchantable will still return as `true`
   * These items are hard to keep track of since they're not flagged in any known database, and thus hard to keep track of
--]]
function lib:IsDisenchantable(item)
	local itemID = item
	if(type(itemID) == 'string') then
		assert(string.match(itemID, 'item:(%d+):') or tonumber(itemID), 'item must be an item ID or item Link')
		itemID = (tonumber(itemID)) or (GetItemInfoFromHyperlink(itemID))
	end

	if(self:HasProfession(333)) then -- Enchanting
		if(self.enchantingItems[itemID]) then
			-- special items that can be disenchanted
			return true
		else
			local _, _, quality, _, _, _, _, _, _, _, _, class, subClass = GetItemInfo(item)
			return quality and ((quality >= LE_ITEM_QUALITY_UNCOMMON and quality <= LE_ITEM_QUALITY_EPIC)
				and C_Item.GetItemInventoryTypeByID(itemID) ~= LE_ITEM_EQUIPLOC_SHIRT
				and (class == LE_ITEM_CLASS_WEAPON
					or (class == LE_ITEM_CLASS_ARMOR and subClass ~= LE_ITEM_ARMOR_COSMETIC)
					or (class == LE_ITEM_CLASS_GEM and subClass == LE_ITEM_SUBCLASS_ARTIFACT)))
		end
	end
end

-- https://wowhead.com/items?filter=107:99;0:2;lockpick:0
local function GetBlacksmithingPick(pickLevel)
	if(CLASSIC) then
		if(pickLevel <= 25 and GetItemCount(15869) > 0) then
			return 15869, nil, 100 -- Silver Skeleton Key
		end
		if(pickLevel <= 125 and GetItemCount(15870) > 0) then
			return 15870, nil, 150 -- Golden Skeleton Key
		end
		if(pickLevel <= 200 and GetItemCount(15871) > 0) then
			return 15871, nil, 200 -- Truesilver Skeleton Key
		end
		if(pickLevel <= 300 and GetItemCount(15872) > 0) then
			return 15872, nil, 275 -- Arcanite Skeleton Key
		end
	else
		if(pickLevel <= 15 and GetItemCount(15869) > 0) then
			return 15869, 590, 100 -- Silver Skeleton Key
		end
		if(pickLevel <= 15 and GetItemCount(15870) > 0) then
			return 15870, 590, 150 -- Golden Skeleton Key
		end
		if(pickLevel <= 20 and GetItemCount(15871) > 0) then
			return 15871, 590, 200 -- Truesilver Skeleton Key
		end
		if(pickLevel <= 30 and GetItemCount(15872) > 0) then
			return 15872, 590, 275 -- Arcanite Skeleton Key
		end
		if(pickLevel <= 30 and GetItemCount(43854) > 0) then
			return 43854, 577, 1 -- Cobalt Skeleton Key
		end
		if(pickLevel <= 30 and GetItemCount(43853) > 0) then
			return 43853, 577, 55 -- Titanium Skeleton Key
		end
		if(pickLevel <= 35 and GetItemCount(55053) > 0) then
			return 55053, 569, 25 -- Obsidium Skeleton Key
		end
		if(pickLevel <= 35 and GetItemCount(82960) > 0) then
			return 82960, 553, 1 -- Ghostly Skeleton Key
		end
		if(pickLevel <= 50 and GetItemCount(159826) > 0) then
			return 159826, 542, 1 -- Monelite Skeleton Key
		end
		if(pickLevel <= 50 and GetItemCount(171441) > 0) then
			return 171441, 1311, 1 -- Laestrite Skeleton Key
		end
	end
end

-- https://wowhead.com/items?filter=107:99;0:7;lockpick:0
local function GetJewelcraftingPick(pickLevel)
	if(pickLevel <= 550 and GetItemCount(130250) > 0) then
		-- TODO: this item has not been updated for 9.0, so it has incorrect pick level
		return 130250, 464, 1 -- Jeweled Lockpick
	end
end

-- https://wowhead.com/items?filter=107:99;0:15;lockpick:0
local function GetInscriptionPick(pickLevel)
	if(pickLevel <= 50 and GetItemCount(159825) > 0) then
		return 159825, 759, 1 -- Scroll of Unlocking
	elseif(pickLevel <= 625 and GetItemCount(173065) > 0) then
		-- TODO: this item has not been updated for 9.0, so it has incorrect pick level
		return 173065, 1406, 1 -- Writ of Grave Robbing
	end
end

-- https://wowhead.com/items?filter=107:99;0:5;lockpick:0
local function GetEngineeringPick(pickLevel)
	if(pickLevel <= 35 and GetItemCount(60853) > 0) then
		return 60853, 715, 1 -- Volatile Seaforium Blastpack
	elseif(pickLevel <= 35 and GetItemCount(77532) > 0) then
		return 77532, 713, 1 -- Locksmith's Powderkeg
	end
end

--[[ LibProcessable:IsOpenable(_item_)
Returns whether the player can open the given item with a class/racial ability.

**Arguments:**
* `item`: item ID or link

**Return values:**
* `isOpenable`: Whether or not the player can open the given item _(boolean)_
* `spellID`:    SpellID of the spell that can be used to open the given item _(number)_
--]]
function lib:IsOpenable(itemID)
	if(type(itemID) == 'string') then
		assert(string.match(itemID, 'item:(%d+):') or tonumber(itemID), 'item must be an item ID or item Link')
		itemID = (tonumber(itemID)) or (GetItemInfoFromHyperlink(itemID))
	end

	local spellID = (IsSpellKnown(1804) and 1804) or -- Pick Lock, Rogue ability
	                (IsSpellKnown(312890) and 312890) -- Skeleton Pinkie, Mechagnome racial ability
	if(spellID) then
		local pickLevel = lib.containers[itemID]
		return pickLevel and pickLevel <= (UnitLevel('player') * (CLASSIC and 5 or 1)), spellID
	end
end

--[[ LibProcessable:IsOpenableProfession(_item_)
Returns the profession data if the given item can be opened by a profession item that the player
posesses.

**Arguments:**
* `item`: item ID or link

**Return values:**
* `skillRequired`:        The skill required in the profession category _(number)_
* `professionID`:         The profession ID _(number)_
* `professionCategoryID`: The profession category ID associated with the unlocking item _(number)_
* `professionItem`:       The itemID for the unlocking item _(number)_

**Notes:**
* The method will return `nil` instead of a category ID for Classic clients, as there are no profession categories there
* This does not check if the player has the required skills to use the profession items
--]]
function lib:IsOpenableProfession(itemID)
	if(type(itemID) == 'string') then
		assert(string.match(itemID, 'item:(%d+):') or tonumber(itemID), 'item must be an item ID or item Link')
		itemID = (tonumber(itemID)) or (GetItemInfoFromHyperlink(itemID))
	end

	local pickLevel = lib.containers[itemID]
	if(not pickLevel) then
		return
	end

	if(self:HasProfession(164)) then -- Blacksmithing
		local itemID, categoryID, skillLevelRequired = GetBlacksmithingPick(pickLevel)
		if(itemID) then
			return skillLevelRequired, 164, categoryID, itemID
		end
	end

	if(self:HasProfession(755)) then -- Jewelcrafting
		local itemID, categoryID, skillLevelRequired = GetJewelcraftingPick(pickLevel)
		if(itemID) then
			return skillLevelRequired, 755, categoryID, itemID
		end
	end

	if(self:HasProfession(773)) then -- Inscription
		local itemID, categoryID, skillLevelRequired = GetInscriptionPick(pickLevel)
		if(itemID) then
			return skillLevelRequired, 773, categoryID, itemID
		end
	end

	if(self:HasProfession(202)) then -- Engineering
		local itemID, categoryID, skillLevelRequired = GetEngineeringPick(pickLevel)
		if(itemID) then
			return skillLevelRequired, 202, categoryID, itemID
		end
	end
end

--[[ LibProcessable:HasProfession(_professionID_)
Returns whether the player has the given profession.

Here's a table with the profession ID for each profession.

| Profession Name | Profession ID |
|-----------------|:--------------|
| Alchemy         | 171           |
| Blacksmithing   | 164           |
| Enchanting      | 333           |
| Engineering     | 202           |
| Herbalism       | 182           |
| Inscription     | 773           |
| Jewelcrafting   | 755           |
| Leatherworking  | 165           |
| Mining          | 186           |
| Skinning        | 393           |
| Tailoring       | 197           |

**Arguments:**
* `professionID`: The profession ID

**Return values:**
* `hasProfession`: Whether or not the player has the profession _(boolean)_
--]]
function lib:HasProfession(professionID)
	return not not professions[professionID]
end

--[[ LibProcessable:GetProfessionCategories(_professionID_)
Returns data of all category IDs for a given (valid) profession, indexed by the expansion level index.

**Arguments:**
* `professionID`: The profession ID _(number)_

**Return values:**
* `categories`: Profession categories _(table)_
--]]
function lib:GetProfessionCategories(professionID)
	local professionCategories = lib.professionCategories[professionID]
	return professionCategories and CopyTable(professionCategories)
end

local classicIDs = {
	[(GetSpellInfo(2259))]  = 171, -- Alchemy
	[(GetSpellInfo(2018))]  = 164, -- Blacksmithing
	[(GetSpellInfo(7411))]  = 333, -- Enchanting
	[(GetSpellInfo(4036))]  = 202, -- Engineering
	[(GetSpellInfo(9134))]  = 182, -- Herbalism (spell from gloves with +5 herbalism)
	[(GetSpellInfo(2108))]  = 165, -- Leatherworking
	[(GetSpellInfo(2575))]  = 186, -- Mining
	[(GetSpellInfo(8613))]  = 393, -- Skinning
	[(GetSpellInfo(3908))]  = 197, -- Tailoring
	[(GetSpellInfo(25229)) or 0] = 755, -- Jewelcrafting
}

local Handler = CreateFrame('Frame')
Handler:RegisterEvent('SKILL_LINES_CHANGED')
Handler:SetScript('OnEvent', function(self, event, ...)
	table.wipe(professions)

	if(CLASSIC) then
		-- all professions are spells in the first spellbook tab
		local _, _, offset, numSpells = GetSpellTabInfo(1)
		for index = offset + 1, offset + numSpells do
			-- iterate through all the spells to find the professions
			local professionID = classicIDs[(GetSpellBookItemName(index, BOOKTYPE_SPELL))]
			if(professionID) then
				professions[professionID] = true
			end
		end
	else
		local first, second = GetProfessions()
		if(first) then
			local _, _, _, _, _, _, professionID = GetProfessionInfo(first)
			professions[professionID] = true
		end

		if(second) then
			local _, _, _, _, _, _, professionID = GetProfessionInfo(second)
			professions[professionID] = true
		end
	end
end)

--[[ LibProcessable.ores
Table of all ores that can be prospected by a jewelcrafter.

See [LibProcessable:IsProspectable()](LibProcessable#libprocessableisprospectableitem).

**Notes**:
* Some items contains a table instead of a boolean
   * Outland and Pandaria ores have skill level requirements, the tables hold that information
* This table has different content based on the game version (retail vs classic)
   * In classic the values represent the required jewelcrafting skill to prospect
--]]
if CLASSIC then
	lib.ores = {
		-- https://tbc.wowhead.com/spell=31252/prospecting#comments
		[2770]  = 1,   -- Copper Ore
		[2771]  = 50,  -- Tin Ore
		[2772]  = 125, -- Iron Ore
		[3858]  = 175, -- Mithril Ore
		[10620] = 250, -- Thorium Ore
		[23424] = 275, -- Fel Iron Ore
		[23425] = 325, -- Adamantite Ore
	}
else
	lib.ores = {
		-- http://www.wowhead.com/spell=31252/prospecting#prospected-from:0+1+17-20
		[2770] = true, -- Copper Ore
		[2771] = true, -- Tin Ore
		[2772] = true, -- Iron Ore
		[3858] = true, -- Mithril Ore
		[10620] = true, -- Thorium Ore
		[23424] = {815, 1}, -- Fel Iron Ore
		[23425] = {815, 25}, -- Adamantite Ore
		[36909] = true, -- Cobalt Ore
		[36910] = true, -- Titanium Ore
		[36912] = true, -- Saronite Ore
		[52183] = true, -- Pyrite Ore
		[52185] = true, -- Elementium Ore
		[53038] = true, -- Obsidium Ore
		[72092] = {809, 1}, -- Ghost Iron Ore
		[72093] = {809, 25}, -- Kyparite
		[72094] = {809, 75}, -- Black Trillium Ore
		[72103] = {809, 75}, -- White Trillium Ore
		[123918] = true, -- Leystone Ore
		[123919] = true, -- Felslate
		[151564] = true, -- Empyrium
		[152579] = true, -- Storm Silver Ore
		[152512] = true, -- Monelite Ore
		[152513] = true, -- Platinum Ore
		[155830] = true, -- Runic Core, BfA Jewelcrafting Quest
		[168185] = true, -- Osmenite Ore
		[171828] = true, -- Laestrite
		[171829] = true, -- Solenium
		[171830] = true, -- Oxxein
		[171831] = true, -- Phaedrum
		[171832] = true, -- Sinvyr
		[171833] = true, -- Elethium
		[187700] = true, -- Progenium Ore
	}
end

--[[ LibProcessable.herbs
Table of all herbs that can be milled by a scribe.

See [LibProcessable:IsMillable()](LibProcessable#libprocessableismillableitem-ignoremortar).
--]]
lib.herbs = {
	-- http://www.wowhead.com/spell=51005/milling#milled-from:0+1+17-20
	[765] = true, -- Silverleaf
	[785] = true, -- Mageroyal
	[2447] = true, -- Peacebloom
	[2449] = true, -- Earthroot
	[2450] = true, -- Briarthorn
	[2452] = true, -- Swiftthistle
	[2453] = true, -- Bruiseweed
	[3355] = true, -- Wild Steelbloom
	[3356] = true, -- Kingsblood
	[3357] = true, -- Liferoot
	[3358] = true, -- Khadgar's Whisker
	[3369] = true, -- Grave Moss
	[3818] = true, -- Fadeleaf
	[3819] = true, -- Dragon's Teeth
	[3820] = true, -- Stranglekelp
	[3821] = true, -- Goldthorn
	[4625] = true, -- Firebloom
	[8831] = true, -- Purple Lotus
	[8836] = true, -- Arthas' Tears
	[8838] = true, -- Sungrass
	[8839] = true, -- Blindweed
	[8845] = true, -- Ghost Mushroom
	[8846] = true, -- Gromsblood
	[13463] = true, -- Dreamfoil
	[13464] = true, -- Golden Sansam
	[13465] = true, -- Mountain Silversage
	[13466] = true, -- Sorrowmoss
	[13467] = true, -- Icecap
	[22785] = true, -- Felweed
	[22786] = true, -- Dreaming Glory
	[22787] = true, -- Ragveil
	[22789] = true, -- Terocone
	[22790] = true, -- Ancient Lichen
	[22791] = true, -- Netherbloom
	[22792] = true, -- Nightmare Vine
	[22793] = true, -- Mana Thistle
	[36901] = true, -- Goldclover
	[36903] = true, -- Adder's Tongue
	[36904] = true, -- Tiger Lily
	[36905] = true, -- Lichbloom
	[36906] = true, -- Icethorn
	[36907] = true, -- Talandra's Rose
	[37921] = true, -- Deadnettle
	[39969] = true, -- Fire Seed
	[39970] = true, -- Fire Leaf
	[52983] = true, -- Cinderbloom
	[52984] = true, -- Stormvine
	[52985] = true, -- Azshara's Veil
	[52986] = true, -- Heartblossom
	[52987] = true, -- Twilight Jasmine
	[52988] = true, -- Whiptail
	[72234] = true, -- Green Tea Leaf
	[72235] = true, -- Silkweed
	[72237] = true, -- Rain Poppy
	[79010] = true, -- Snow Lily
	[79011] = true, -- Fool's Cap
	[89639] = true, -- Desecrated Herb
	[109124] = true, -- Frostweed
	[109125] = true, -- Fireweed
	[109126] = true, -- Gorgrond Flytrap
	[109127] = true, -- Starflower
	[109128] = true, -- Nagrand Arrowbloom
	[109129] = true, -- Talador Orchid
	[124101] = true, -- Aethril
	[124102] = true, -- Dreamleaf
	[124103] = true, -- Foxflower
	[124104] = true, -- Fjarnskaggl
	[124105] = true, -- Starlight Rose
	[124106] = true, -- Felwort
	[128304] = true, -- Yseralline Seed
	[151565] = true, -- Astral Glory
	[152511] = true, -- Sea Stalk
	[152509] = true, -- Siren's Pollen
	[152508] = true, -- Winter's Kiss
	[152507] = true, -- Akunda's Bite
	[152506] = true, -- Star Moss
	[152505] = true, -- Riverbud
	[152510] = true, -- Anchor Weed
	[168487] = true, -- Zin'anthid
	[168583] = true, -- Widowbloom
	[168586] = true, -- Rising Glory
	[168589] = true, -- Marrowroot
	[169701] = true, -- Deathblossom
	[170554] = true, -- Vigil's Torch
	[171315] = true, -- Nightshade
	[187699] = true, -- First Flower
}

--[[ LibProcessable.enchantingItems
Table of special items used in Enchanting quests.

See [LibProcessable:IsDisenchantable()](LibProcessable#libprocessableisdisenchantableitem).
--]]
lib.enchantingItems = {
	-- Legion enchanting quest line
	[137195] = true, -- Highmountain Armor
	[137221] = true, -- Enchanted Raven Sigil
	[137286] = true, -- Fel-Crusted Rune

	-- Shadowlands profession world quests
	[182021] = true, -- Antique Kyrian Javelin
	[182043] = true, -- Antique Necromancer's Staff
	[182067] = true, -- Antique Duelist's Rapier
	[181991] = true, -- Antique Stalker's Bow
}

--[[ LibProcessable.containers
Table of all items that can be opened with a Rogue's _Pick Lock_ ability, or with profession keys.

The value is the required skill to open the item.

See [LibProcessable:IsOpenable()](LibProcessable#libprocessableisopenableitem) and
[LibProcessable:IsOpenableProfession()](LibProcessable#libprocessableisopenableprofessionitem).

**Notes**:
* This table has different content based on the game version (retail vs classic).
--]]
if(CLASSIC) then
	lib.containers = {
		-- https://classic.wowhead.com/items?filter=10:195;1:2;:0
		[4632]  = 1,    -- Ornate Bronze Lockbox
		[6354]  = 1,    -- Small Locked Chest
		[6712]  = 1,    -- Practice Lock
		[7209]  = 1,    -- Tazan's Satchel
		[16882] = 1,    -- Battered Junkbox
		[4633]  = 25,   -- Heavy Bronze Lockbox
		[4634]  = 70,   -- Iron Lockbox
		[5046]  = 70,   -- Locked Gift
		[6355]  = 70,   -- Sturdy Locked Chest
		[7869]  = 70,   -- Lucius's Lockbox
		[16883] = 70,   -- Worn Junkbox
		[4636]  = 125,  -- Strong Iron Lockbox
		[4637]  = 175,  -- Steel Lockbox
		[13875] = 175,  -- Ironbound Locked Chest
		[16884] = 175,  -- Sturdy Junkbox
		[4638]  = 225,  -- Reinforced Steel Lockbox
		[5758]  = 225,  -- Mithril Lockbox
		[5759]  = 225,  -- Thorium Lockbox
		[5760]  = 225,  -- Eternium Lockbox
		[13918] = 250,  -- Reinforced Locked Chest
		[16885] = 250,  -- Heavy Junkbox
		[12033] = 275,  -- Thaurissan Family Jewels
		[24282] = 5000, -- Rogue's Diary (lockpick requirement sourced from comments, unverified)
		[29569] = 300,  -- Strong Junkbox
		[31952] = 325,  -- Khorium Lockbox
	}
else
	lib.containers = {
		-- https://www.wowhead.com/items?filter=10:195;1:2;:0
		[7209]   = 0,  -- Tazan's Satchel
		[4632]   = 15, -- Ornate Bronze Lockbox
		[4633]   = 15, -- Heavy Bronze Lockbox
		[4634]   = 15, -- Iron Lockbox
		[4636]   = 15, -- Strong Iron Lockbox
		[4637]   = 15, -- Steel Lockbox
		[4638]   = 15, -- Reinforced Steel Lockbox
		[5758]   = 15, -- Mithril Lockbox
		[5759]   = 15, -- Thorium Lockbox
		[5760]   = 15, -- Eternium Lockbox
		[6354]   = 15, -- Small Locked Chest
		[6355]   = 15, -- Sturdy Locked Chest
		[12033]  = 15, -- Thaurissan Family Jewels
		[13875]  = 15, -- Ironbound Locked Chest
		[13918]  = 15, -- Reinforced Locked Chest
		[16882]  = 15, -- Battered Junkbox
		[16883]  = 15, -- Worn Junkbox
		[16884]  = 15, -- Sturdy Junkbox
		[16885]  = 15, -- Heavy Junkbox
		[106895] = 15, -- Iron-Bound Junkbox
		[29569]  = 30, -- Strong Junkbox
		[31952]  = 30, -- Khorium Lockbox
		[43575]  = 30, -- Reinforced Junkbox
		[43622]  = 30, -- Froststeel Lockbox
		[43624]  = 30, -- Titanium Lockbox
		[45986]  = 30, -- Tiny Titanium Lockbox
		[63349]  = 30, -- Flame-Scarred Junkbox
		[68729]  = 30, -- Elementium Lockbox
		[88165]  = 35, -- Vine-Cracked Junkbox
		[88567]  = 35, -- Ghost Iron Lockbox
		[116920] = 40, -- True Steel Lockbox
		[121331] = 45, -- Leystone Lockbox
		[169475] = 50, -- Barnacled Lockbox
		[179311] = 50, -- Synvir Lockbox
		[180522] = 50, -- Phaedrum Lockbox
		[180532] = 50, -- Oxxein Lockbox
		[180533] = 50, -- Solenium Lockbox
		[186161] = 50, -- Stygian Lockbox
		[186160] = 50, -- Locked Artifact Case
		[188787] = 50, -- Locked Broker Luggage
	}
end

--[[ LibProcessable.professionCategories
Table of all professionIDs and their respective categories, indexed by expansion ID.

See [LibProcessable:GetProfessionCategories()](LibProcessable#libprocessablegetprofessioncategoriesprofessionid).
--]]
lib.professionCategories = {
	[171] = { -- Alchemy
		604, -- Classic
		602, -- Outland
		600, -- Northrend
		598, -- Cataclysm
		596, -- Pandaria
		332, -- Draenor
		433, -- Legion
		592, -- Zandalari/Kul Tiran
		1294, -- Shadowlands
	},
	[164] = { -- Blacksmithing
		590, -- Classic
		584, -- Outland
		577, -- Northrend
		569, -- Cataclysm
		553, -- Pandaria
		389, -- Draenor
		426, -- Legion
		542, -- Zandalari/Kul Tiran
		1311, -- Shadowlands
	},
	[333] = { -- Enchanting
		667, -- Classic
		665, -- Outland
		663, -- Northrend
		661, -- Cataclysm
		656, -- Pandaria
		348, -- Draenor
		443, -- Legion
		647, -- Zandalari/Kul Tiran
		1364, -- Shadowlands
	},
	[202] = { -- Engineering
		419, -- Classic
		719, -- Outland
		717, -- Northrend
		715, -- Cataclysm
		713, -- Pandaria
		347, -- Draenor
		469, -- Legion
		709, -- Zandalari/Kul Tiran
		1381, -- Shadowlands
	},
	[182] = { -- Herbalism
		1044, -- Classic
		1042, -- Outland
		1040, -- Northrend
		1038, -- Cataclysm
		1036, -- Pandaria
		1034, -- Draenor
		456, -- Legion
		1029, -- Zandalari/Kul Tiran
		1441, -- Shadowlands
	},
	[773] = { -- Inscription
		415, -- Classic
		769, -- Outland
		767, -- Northrend
		765, -- Cataclysm
		763, -- Pandaria
		410, -- Draenor
		450, -- Legion
		759, -- Zandalari/Kul Tiran
		1406, -- Shadowlands
	},
	[755] = { -- Jewelcrafting
		372, -- Classic
		815, -- Outland
		813, -- Northrend
		811, -- Cataclysm
		809, -- Pandaria
		373, -- Draenor
		464, -- Legion
		805, -- Zandalari/Kul Tiran
		1418, -- Shadowlands
	},
	[165] = { -- Leatherworking
		379, -- Classic
		882, -- Outland
		880, -- Northrend
		878, -- Cataclysm
		876, -- Pandaria
		380, -- Draenor
		460, -- Legion
		871, -- Zandalari/Kul Tiran
		1334, -- Shadowlands
	},
	[186] = { -- Mining
		1078, -- Classic
		1076, -- Outland
		1074, -- Northrend
		1072, -- Cataclysm
		1070, -- Pandaria
		1068, -- Draenor
		425, -- Legion
		1065, -- Zandalari/Kul Tiran
		1320, -- Shadowlands
	},
	[393] = { -- Skinning
		1060, -- Classic
		1058, -- Outland
		1056, -- Northrend
		1054, -- Cataclysm
		1042, -- Pandaria
		1050, -- Draenor
		459, -- Legion
		1046, -- Zandalari/Kul Tiran
		1331, -- Shadowlands
	},
	[197] = { -- Tailoring
		362, -- Classic
		956, -- Outland
		954, -- Northrend
		952, -- Cataclysm
		950, -- Pandaria
		369, -- Draenor
		430, -- Legion
		942, -- Zandalari/Kul Tiran
		1395, -- Shadowlands
	},
}
