-- luacheck: globals Character MyModGlobal Timer CLIENT
if not CLIENT then return end

local utils = require("Cyka.utils")

---@param inventory Barotrauma.ItemInventory
---@param predicate fun(slot: InventorySlot): boolean
---@return InventorySlot[], string?
local function findSlotsThat(inventory, predicate)
	local slots = {}
	for i, slot in ipairs(inventory.slots) do
		local inventorySlot = {
			slot = slot,
			inventory = inventory,
			slotIndex = i - 1
		}
		if predicate(inventorySlot) then
			slots[#slots + 1] = inventorySlot
		end
	end
	return slots
end

---@param slot InventorySlot
local function tryUnloadSlot(slot)
	---@type Barotrauma.Item
	local item = slot.slot.items[1]
	if not item then
		MyModGlobal.debugPrint("No item in slot")
		return
	end
	local inventory = item.OwnInventory
	if not inventory then
		MyModGlobal.debugPrint("Item has no own inventory")
		return
	end

	local toUnload = {}
	local toUnloadByPrefab = {}
	local inventorySlots = inventory.slots
	for _, inventorySlot in ipairs(inventorySlots) do
		for _, inventoryItem in ipairs(inventorySlot.items) do
			toUnload[#toUnload + 1] = inventoryItem
			-- This will only serve as O(1) lookup
			toUnloadByPrefab[inventoryItem.Prefab] = true
		end
	end

	-- Where can we put our toUnload items?
	local nearbySlots = findSlotsThat(slot.inventory, function(islot)
		local isEmpty = #islot.slot.items == 0
		if isEmpty then return true end

		for _, prefab in ipairs(toUnloadByPrefab) do
			local canAccept = islot.inventory.CanBePutInSlot(prefab, islot.slotIndex)
			if canAccept then return true end
		end
		return false
	end)
	-- print("Before sorting:")
	-- dump(nearbySlots)

	-- Some inventories don't have slots per row, like the player inventory
	local slotsPerRow = 900
	local ok, err = pcall(function()
		slotsPerRow = slot.inventory.slotsPerRow
	end)
	if not ok then
		MyModGlobal.debugPrint(string.format("Error getting slots per row: %s", err))
	end

	local getGridPos = function(slotIndex)
		local x = slotIndex % slotsPerRow
		local y = math.floor(slotIndex / slotsPerRow)
		return x, y
	end

	-- We are offsetting here by 1 because the backend uses 0-indexed slots
	-- And the lua uses 1-indexed slots
	-- We are trying to match the backend behavior for sorting
	local slotx, sloty = getGridPos(slot.slotIndex - 1)
	-- print(string.format("Slot position %d: %d, %d", slot.slotIndex, slotx, sloty))
	table.sort(nearbySlots, function(a, b)
		local ax, ay = getGridPos(a.slotIndex)
		local bx, by = getGridPos(b.slotIndex)

		local distA = math.max(math.abs(ax - slotx), math.abs(ay - sloty))
		local distB = math.max(math.abs(bx - slotx), math.abs(by - sloty))

		if distA == distB then
			return a.slotIndex < b.slotIndex
		end
		return distA < distB
	end)
	-- print(string.format("Current slot: %d at (%d, %d)", slot.slotIndex, slotx, sloty))

	for _, iitem in ipairs(toUnload) do
		for _, nearbySlot in ipairs(nearbySlots) do
			local canAccept = nearbySlot.inventory.CanBePutInSlot(iitem.Prefab, nearbySlot.slotIndex)
			if canAccept then
				local moved = nearbySlot.inventory.TryPutItem(iitem, nearbySlot.slotIndex, true, false,
				nil)
				-- print(string.format("Moved item %s to slot %d", iitem.Name, nearbySlot.slotIndex))
				if moved then break end
			end
		end
	end
end

local function tryUnloadCursorItem()
	local slots, err = utils.getSlotsUnderCursor()
	if err then
		-- MyModGlobal.debugPrint(string.format("Error getting inventory slot: %s", err))
		return
	end

	if not slots or #slots == 0 then
		-- MyModGlobal.debugPrint("No items in slot")
		return
	end

	for _, slot in ipairs(slots) do
		tryUnloadSlot(slot)
	end
end

return {
	tryUnloadCursorItem = tryUnloadCursorItem,
}
