-- luacheck: globals Character MyModGlobal Timer CLIENT
-- luacheck: max line length 420
if not CLIENT then return end

local utils = require("Cyka.utils")
local dump = require("Cyka.dump")

-- Some items allow multiple items to be loaded
-- Such as welders and cutters
-- But we don't really want to disqualify those programmatically
-- Because it is not so obvious what item we REALLY want to load
-- So we will just hardcode tools and their whitelisted magazines
---@type table<string, table<string, number>>
local LOAD_MAP = require("Cyka.quickreload_loadmap")

---@param inventory Barotrauma.ItemInventory
---@return InventorySlot[]
local function getSlots(inventory)
	local slots = {}
	local inventorySlots = inventory.slots
	for i, inventorySlot in ipairs(inventorySlots) do
		slots[#slots + 1] = {
			inventory = inventory,
			slotIndex = i - 1,
			slot = inventorySlot
		}
	end
	return slots
end

---@param slots InventorySlot[]
---@return table<InventorySlot, Barotrauma.Item[]>
local function getItemsPerSlot(slots)
	-- How many items can we move to what slot
	-- We don't yet know what can fit into what slot
	---@type table<InventorySlot, Barotrauma.Item[]>
	local movableBySlot = {}
	-- Get all the items and then we will sort them by condition and shit
	utils.enqueueAllPlayerItems({}, function(ititem, itemRef)
		-- We don't want to take oxygen out of our diving suit to load our plasma cutter
		-- Most loadable items have 1 capacity
		-- But some have 2 or 3 (coil speargun)
		if itemRef and itemRef.inventory and itemRef.inventory.Capacity < 4 then
			-- MyModGlobal.debugPrint(string.format("Skipping small inventory %s", tostring(itemRef.inventory)))
			return false
		end
		-- MyModGlobal.debugPrint("Checking item:")
		-- dump(slots)
		-- MyModGlobal.debugPrint(ititem.Prefab.Identifier.Value)
		for _, inventorySlot in ipairs(slots) do
			local canMove = inventorySlot.inventory.CanBePutInSlot(ititem, inventorySlot.slotIndex)
			-- MyModGlobal.debugPrint(string.format("Can move to slot %d: %s", inventorySlot.slotIndex, tostring(canMove)))
			if canMove then
				movableBySlot[inventorySlot] = movableBySlot[inventorySlot] or {}
				movableBySlot[inventorySlot][#movableBySlot[inventorySlot] + 1] = ititem
				return true
			end
		end
		return false
	end, true)
	return movableBySlot
end

---@param movableBySlot table<InventorySlot, Barotrauma.Item[]>
---@return table<InventorySlot, table<Barotrauma.ItemPrefab, boolean>>
local function getPermissibleItemsPerSlot(movableBySlot)
	-- The point of this exercise is to eliminate slots that can have
	-- Multiple items
	-- What do we put into those? Any? All?
	-- What if those slots belong to a container?
	-- Are we reloading 30 slot containers?
	---@type table<InventorySlot, table<Barotrauma.ItemPrefab, boolean>>
	local permissibleItemsPerSlot = {}
	for inventorySlot, items in pairs(movableBySlot) do
		for _, ititem in ipairs(items) do
			local thisone = tostring(ititem.Prefab.Identifier.Value)
			permissibleItemsPerSlot[inventorySlot] = permissibleItemsPerSlot[inventorySlot] or {}
			permissibleItemsPerSlot[inventorySlot][thisone] = true
		end
	end
	return permissibleItemsPerSlot
end

---@param movableBySlot table<InventorySlot, Barotrauma.Item[]>
local function printPermissibleItems(movableBySlot)
	local permissibleItemsPerSlot = getPermissibleItemsPerSlot(movableBySlot)
	MyModGlobal.debugPrint("Can load:")
	for _, loadableItems in pairs(permissibleItemsPerSlot) do
		for loadableItem, _ in pairs(loadableItems) do
			MyModGlobal.debugPrint("    " .. loadableItem)
		end
	end
end


---@param slot InventorySlot
---@param preferMinCondition boolean
local function tryReloadSlot(slot, preferMinCondition)
	---@type Barotrauma.Item
	local item = slot.slot.items[1]
	if not item then
		MyModGlobal.debugPrint("No item in slot")
		return
	end
	MyModGlobal.debugPrint(string.format("Reloading item %s", item.Prefab.Identifier.Value))
	local inventory = item.OwnInventory
	if not inventory then
		MyModGlobal.debugPrint("Item has no own inventory")
		return
	end

	---@type InventorySlot[]
	local slots = getSlots(inventory)
	if #slots == 0 then
		MyModGlobal.debugPrint("No slots")
		return
	end
	-- MyModGlobal.debugPrint("Slots:")
	-- dump(slots)

	---@type table<InventorySlot, Barotrauma.Item[]>
	local movableBySlot = getItemsPerSlot(slots)
	-- MyModGlobal.debugPrint("Movable by slot:")
	-- dump(movableBySlot)

	local permissibleItems = LOAD_MAP[tostring(item.Prefab.Identifier.Value)]
	if not permissibleItems then
		MyModGlobal.debugPrint("No permissible items for " .. tostring(item.Prefab.Identifier.Value))
		printPermissibleItems(movableBySlot)
		return
	end

	-- Sort items by condition (asc or desc) but also
	-- Make sure items with 0 condition are at the end
	-- We don't really want to load those
	for _, items in pairs(movableBySlot) do
		table.sort(items, function(a, b)
			if a.Condition == 0 and b.Condition ~= 0 then
				return false
			elseif a.Condition ~= 0 and b.Condition == 0 then
				return true
			elseif preferMinCondition then
				return a.Condition < b.Condition
			else
				return a.Condition > b.Condition
			end
		end)
	end
	-- dump(movableBySlot)

	local numMoved = 0
	for inventorySlot, items in pairs(movableBySlot) do
		for _, ititem in ipairs(items) do
			local permissible = permissibleItems[tostring(ititem.Prefab.Identifier.Value)]
			if permissible then
				-- We loaded as many as we have been allowed to
				-- And we do this check up front because an item may already
				-- Be partially loaded
				local nowHave = #inventorySlot.slot.items
				if nowHave >= permissible then
					-- MyModGlobal.debugPrint(string.format(
					-- "Finished processing item: %s. Current slot has reached the permissible limit of %d items.",
					-- 	tostring(ititem.Prefab.Identifier.Value), permissible))
					break
				end

				local moved = inventorySlot.inventory.TryPutItem(ititem, inventorySlot.slotIndex, false, true, nil)
				-- When the slot is full no more will be able to be moved
				-- And tat that point we're done with that slot
				if not moved then break end
				numMoved = numMoved + 1

				-- else
				-- 	MyModGlobal.debugPrint(string.format("Not permissible: %s", tostring(ititem.Prefab.Identifier.Value)))
			end
		end
	end
	MyModGlobal.debugPrint(string.format("Moved %d items to load %s", numMoved, tostring(item.Prefab.Identifier.Value)))
end

---@param preferMinCondition boolean Prefer items with lowest condition
local function tryReloadCursorItem(preferMinCondition)
	local slots, err = utils.getSlotsUnderCursor()
	if err then
		MyModGlobal.debugPrint(string.format("Error getting slots under cursor: %s", err))
		return
	end

	for _, slot in ipairs(slots) do
		tryReloadSlot(slot, preferMinCondition)
	end
end

return {
	tryReloadCursorItem = tryReloadCursorItem,
}
