-- @author DoomTero 2026
-- @copyright 2026 DoomTero - ZomboRut original work. ref:a9cb18c7
-- @license Proprietary. Not licensed for reuse, redistribution or derivative works without written permission from DoomTero. Ask - permission is obtainable. See ZomboRut/LICENSE.txt

require "TimedActions/ISBaseTimedAction"

ISSubdueZombieAction = ISBaseTimedAction:derive("ISSubdueZombieAction")

ISSubdueZombieAction.ITEM_ROPE      = "Base.Rope"
ISSubdueZombieAction.ITEM_DUCT_TAPE = "Base.DuctTape"

ISSubdueZombieAction.BELT_COUNT = 2

function ISSubdueZombieAction.isBelt(item)
	if item == nil or item.getBodyLocation == nil then return false end
	local loc = item:getBodyLocation()
	if loc == nil then return false end
	local id = string.lower(tostring(loc))
	return (id:match("([^:]+)$") or id) == "belt"
end

function ISSubdueZombieAction.findBinding(character)
	local inv = character and character:getInventory()
	if inv == nil then return nil end

	local rope = inv:getFirstTypeRecurse(ISSubdueZombieAction.ITEM_ROPE)
	if rope then return { kind = "rope", items = { rope } } end

	local picked = {}
	for _ = 1, ISSubdueZombieAction.BELT_COUNT do
		local found = inv:getFirstEvalRecurse(function(item)
			if not ISSubdueZombieAction.isBelt(item) then return false end
			for i = 1, #picked do if picked[i] == item then return false end end
			return true
		end)
		if not found then return nil end
		picked[#picked + 1] = found
	end
	return { kind = "belts", items = picked }
end

local SUBDUE_DURATION_TICKS = 3 * 60

function ISSubdueZombieAction:isValid()
	if not self.target or not self.target:isAlive() then return false end
	if self.target:getModData().zrSubdued then return false end

	local downed = self.target:isKnockedDown()
		or self.target:isOnFloor()
		or self.target:isCrawling()
	if not downed then return false end

	local inv = self.character:getInventory()
	if not inv then return false end
	if not ISSubdueZombieAction.findBinding(self.character) then return false end
	if not inv:getFirstTypeRecurse(ISSubdueZombieAction.ITEM_DUCT_TAPE) then return false end

	return true
end

function ISSubdueZombieAction:waitToStart()
	self.character:faceThisObject(self.target)
	return self.character:shouldBeTurning()
end

function ISSubdueZombieAction:update()
	self.character:faceThisObject(self.target)
end

function ISSubdueZombieAction:start()
	self:setActionAnim("Loot")
	self:setAnimVariable("LootPosition", "Low")
end

function ISSubdueZombieAction:stop()
	ISBaseTimedAction.stop(self)
end

local function consumeBindingItem(character, inv, item)
	if item == nil then return end
	pcall(function()
		if character.isEquippedClothing and character:isEquippedClothing(item)
			and character.removeWornItem
		then
			character:removeWornItem(item)
		end
		local cont = item.getContainer and item:getContainer()
		if cont and cont.Remove then cont:Remove(item)
		elseif inv and inv.Remove then inv:Remove(item) end
	end)
end

function ISSubdueZombieAction:perform()
	local inv = self.character:getInventory()
	local binding = ISSubdueZombieAction.findBinding(self.character)
	local tape = inv and inv:getFirstTypeRecurse(ISSubdueZombieAction.ITEM_DUCT_TAPE)

	local spent = { kind = binding and binding.kind or "rope", types = {} }
	if binding then
		for i = 1, #binding.items do
			local item = binding.items[i]
			spent.types[#spent.types + 1] = item:getFullType()
			consumeBindingItem(self.character, inv, item)
		end
	end
	pcall(function()
		if tape and tape.Use then tape:Use() end
	end)

	self.target:getModData().zrSubdued = true
	if ZR_Subdued and ZR_Subdued.enforce then
		ZR_Subdued.enforce(self.target)
	end
	if ZR_Subdued and ZR_Subdued.register then
		ZR_Subdued.register(self.target, spent)
	end

	ISBaseTimedAction.perform(self)
end

function ISSubdueZombieAction:new(character, target)
	local o = ISBaseTimedAction.new(self, character)
	o.character    = character
	o.target       = target
	o.stopOnWalk   = true
	o.stopOnRun    = true
	o.maxTime      = SUBDUE_DURATION_TICKS
	o.loopedAction = false
	return o
end
