local debug = false
local iconDisplayDuration = 3

---@param container number
---@param slot number
---@return string, string|nil
local function getItemLink(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local link = select(7, GetContainerItemInfo(container, slot))
	if link == nil then return "", string.format("GetContainerItemInfo returned nil for link (arg 7)") end
	return link
end
---@param container number
---@param slot number
---@return string, string|nil
local function getItemQuantity(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local quantity = select(2, GetContainerItemInfo(container, slot))
	if quantity == nil then return "", string.format("GetContainerItemInfo returned nil for quantity (arg 2)") end
	return quantity
end
---@param container number
---@param slot number
---@return string, string|nil
local function getItemName(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local name = select(1, GetItemInfo(getItemLink(container, slot)))
	if name == nil then return "", string.format("GetItemInfo returned nil for name (arg 1)") end
	return name
end
---@param container number
---@param slot number
---@return string, string|nil
local function getItemType(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local type = select(6, GetItemInfo(getItemLink(container, slot)))
	if type == nil then return "", string.format("GetItemInfo returned nil for type (arg 6)") end
	return type
end
---@param container number
---@param slot number
---@return string, string|nil
local function getItemSubtype(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local subtype = select(7, GetItemInfo(getItemLink(container, slot)))
	if subtype == nil then return "", string.format("GetItemInfo returned nil for subtype (arg 7)") end
	return subtype
end
---@param container number
---@param slot number
---@return string, string|nil
local function getItemLevel(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local level = select(4, GetItemInfo(getItemLink(container, slot)))
	if level == nil then return "", string.format("GetItemInfo returned nil for level (arg 4)") end
	return level
end
---@param container number
---@param slot number
---@return string, string|nil
local function getItemValue(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local value = select(11, GetItemInfo(getItemLink(container, slot)))
	if value == nil then return "", string.format("GetItemInfo returned nil for value (arg 11)") end
	return value
end
---@param container number
---@param slot number
---@return string, string|nil
local function getItemQuality(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local quality = select(3, GetItemInfo(getItemLink(container, slot)))
	if quality == nil then return "", string.format("GetItemInfo returned nil for quality (arg 3)") end
	return quality
end
---@param container number
---@param slot number
---@return string, string|nil
local function getItemEquipLocation(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local equipLoc = select(9, GetItemInfo(getItemLink(container, slot)))
	if equipLoc == nil then return "", string.format("GetItemInfo returned nil for equipLoc (arg 9)") end
	return equipLoc
end
---@param container number
---@param slot number
---@return string, string|nil
local function getItemIcon(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local icon = select(10, GetItemInfo(getItemLink(container, slot)))
	if icon == nil then return "", string.format("GetItemInfo returned nil for icon (arg 10)") end
	return icon
end
---@param container number
---@param slot number
---@return string, string|nil
local function getBindType(container, slot)
	if container == nil then return "", string.format("Container is nil") end
	if slot == nil then return "", string.format("Slot is nil") end
	local bindType = select(14, GetItemInfo(getItemLink(container, slot)))
	if bindType == nil then return "", string.format("GetItemInfo returned nil for bindType (arg 14)") end
	return bindType
end

---@class Filter
---@field requires table<string, fun(container:number, slot: number): string|number|boolean> | nil
---@field filter fun(container: number, slot: number, provided: table<string, string|number|boolean>): boolean
Filter = {
	---@param requires table<string, fun(container:number, slot: number): string|number|boolean> | nil
	---@param filter fun(container: number, slot: number, provided: table<string, string|number|boolean>): boolean
	---@return Filter
	new = function(requires, filter)
		local self = setmetatable({}, {
			__index = Filter,
		})
		self.requires = requires
		self.filter = filter
		return self
	end,

	---@param self Filter
	---@param container number
	---@param slot number
	---@return boolean, string|nil
	Run = function(self, container, slot)
		---@type table<string, string|number|boolean>
		local provided = {}
		if self.requires then
			for k, v in pairs(self.requires) do
				local res, err = v(container, slot)
				if err then
					if debug then print(err) end
					return false, err
				end
				provided[k] = res
			end
		end
		local res, err = self.filter(container, slot, provided)
		if err then
			if debug then print(err) end
		end
		return res, nil
	end,
}

local grayFilter = Filter.new({
	["quality"] = getItemQuality,
}, function(container, slot, provided)
	if not aura_env.config.grayFilter then return false end
	if provided.quality == 0 then return true end
	return false
end)
local gearFilter = Filter.new({
	["ilvl"] = getItemLevel,
	["bindType"] = getBindType,
	["type"] = getItemType,
	["equipLoc"] = getItemEquipLocation,
}, function(container, slot, provided)
	if not aura_env.config.gearFilter then return false end
	local ilvlThreshold = aura_env.config.gearFilterIlvlFilterThreshold
	local sellBoe = aura_env.config.gearFilterSellBoe

	if
		provided.type == "Armor"
		or provided.type == "Weapon"
		or provided.equipLoc == "INVTYPE_FINGER"
		or provided.equipLoc == "INVTYPE_TRINKET"
		or provided.equipLoc == "INVTYPE_CLOAK"
		or provided.equipLoc == "INVTYPE_NECK"
	then
		if provided.ilvl < ilvlThreshold and (provided.bindType == 1 or sellBoe) then return true end
	end
	return false
end)

---@param container number
---@param slot number
---@return nil, string|nil
local function doSell(container, slot)
	local itemIcon, err = getItemIcon(container, slot)
	if err then return nil, string.format("Error getting item icon: %s", err) end
	local itemName, err = getItemName(container, slot)
	if err then return nil, string.format("Error getting item name: %s", err) end
	local itemQuantity, err = getItemQuantity(container, slot)
	if err then return nil, string.format("Error getting item quantity: %s", err) end
	local itemQuality, err = getItemQuality(container, slot)
	if err then return nil, string.format("Error getting item quality: %s", err) end

	local nameWithColor = string.format("%s[%s]\124r", aura_env.qualityColors[itemQuality + 1], itemName)
	local nameWithQuantity = string.format("%s x%s", nameWithColor, itemQuantity)

	aura_env.allstates[#aura_env.allstates + 1] = {
		show = true,
		changed = true,
		index = GetTime(),
		resort = true,

		icon = itemIcon,
		name = nameWithQuantity,
		amount = itemQuantity,

		progressType = "timed",
		expirationTime = GetTime() + iconDisplayDuration,
		duration = iconDisplayDuration,
		autoHide = true,
	}

	UseContainerItem(container, slot)
	return nil, nil
end

---@type table<Filter>
local filters = {
	grayFilter,
	gearFilter,
}

---@class FilterService
aura_env.FilterService = {
	---@param container number
	---@param slot number
	---@return nil, nil
	Run = function(container, slot)
		local itemLink, err = getItemLink(container, slot)
		if err then return nil, string.format("Err: slot empty (%d-%d) - %s", container, slot, err) end
		for k, filter in pairs(filters) do
			local res, err = filter:Run(container, slot)
			if err then
				if debug then print(err) end
			else
				if res then
					doSell(container, slot)
					return
				end
			end
		end
	end,
}

aura_env.qualityColors = {
	"\124cff9d9d9d", -- Poor
	"\124cffffffff", -- Common
	"\124cff1eff00", -- Uncommon
	"\124cff0070dd", -- Rare
	"\124cffa335ee", -- Epic
	"\124cffff8000", -- Legendary
	"\124cffe6cc80", -- Artifact
	"\124cff00ccff", -- Heirloom
}
