-- Consent Required API
-- Any Lua script can access this API by adding this line:
-- local Api = require "com.github.cintique.ConsentRequired.Api"
local Environment = require("ConsentRequiredExtended.Util.Environment")
local Barotrauma = require("ConsentRequiredExtended.Util.Barotrauma")

local _ENV = Environment.PrepareEnvironment(_ENV)

-- Table of identifiers (strings) of items that when used
-- as a treatment on an NPC from a different team,
-- causes that NPC (and their allies) to become hostile
-- towards the player.
local affectedItems = {}

---Adds an item (by identifier string) to `affectedItems`.
---@param identifier string
function AddAffectedItem(identifier)
	table.insert(affectedItems, identifier)
end

LuaUserData.MakeFieldAccessible(Descriptors["Barotrauma.AbandonedOutpostMission"], "requireRescue")

-- Character type doesn't have tags we can assign a custom "rescuetarget" tag to
-- So instead we just hold characters which need rescue in a table and compare their entity IDs
-- This table is only resfreshed on roundstart
local rescuetargets = {}

---Returns a boolean indicating whether a given item is affected or not.
---@param identifier string The identifier of the item that we are testing.
---@return boolean isAffected True if the item is affected, false otherwise.
function IsItemAffected(identifier)
	for _, item in pairs(affectedItems) do
		if item == identifier or HF.StartsWith(identifier, item) then
			return true
		end
	end
	return false
end

local ADD_ATTACKER_DAMAGE = 130 -- Heelge: this used to max out negative rep gain, now only around 4 negative rep, any less negative rep is too forgiving.

---@param aiChar Barotrauma_Character The AI character to be made hostile.
---@param instigator Barotrauma_Character The character to be the target of the AI's wrath.
function makeHostile(aiChar, instigator)
	aiChar.AIController.OnAttacked(instigator, Barotrauma.AttackResult.NewAttackResultFromDamage(ADD_ATTACKER_DAMAGE))
	aiChar.AddAttacker(instigator, ADD_ATTACKER_DAMAGE)
end

---@param char1 Barotrauma_Character Character one.
---@param char2 Barotrauma_Character Character two.
---@return boolean charactersAreOnSameTeam True if characters one & two are on the same team, false otherwise.
function isOnSameTeam(char1, char2)
	local team1 = char1.TeamID
	local team2 = char2.TeamID
	return team1 == team2
end

---Updates current rescue targets list, separate so we dont cycle thru all missions every time we apply item to chacter. Use IsRescueTarget(target) after this.
function UpdateRescueTargets()
	rescuetargets = {}
	for mission in Game.GameSession.Missions do
		if LuaUserData.IsTargetType(mission.Prefab.MissionClass, "Barotrauma.AbandonedOutpostMission") then
			for character in mission.requireRescue do
				rescuetargets[character.ID] = character
				--table.insert(rescuetargets, character)
			end
		end
	end
	-- print('rescue targets =')
	-- for char in rescuetargets do print(char.Name) end
end

---@param target Barotrauma_Character The character we want to confirm as being rescued
---@return boolean consent True if target is rescue mission target, false otherwise
function IsRescueTarget(target)
	-- for char in rescuetargets do
	--     if target.ID == char.ID then return true end
	-- end
	if rescuetargets[target.ID] ~= nil then
		return true
	end
	return false
end

---@param user Barotrauma_Character The character who desires consent.
---@param target Barotrauma_Character The character who gives consent
---@return boolean consent True if consent is given, false otherwise.
function hasConsent(user, target)
	return isOnSameTeam(user, target) or target.IsEscorted or IsRescueTarget(target) -- No longer needs to be shared.
end

---@param aiChar Barotrauma_Character The (AI but not necessarily) character whose sight is being tested.
---@param target Barotrauma_Character The character to be seen.
---@return boolean aiCanSeeTarget True if the AI can see the target character.
function canAiSeeTarget(aiChar, target)
	-- I'll just use what Barotrauma uses for witness line of sight
	local aiVisibleHulls = aiChar.GetVisibleHulls()
	local targetCurrentHull = target.CurrentHull
	for _, visibleHull in pairs(aiVisibleHulls) do
		if targetCurrentHull == visibleHull then
			return true
		end
	end
	return false
end

---@param user Barotrauma_Character The character of the instigator being witnessed.
---@param victim Barotrauma_Character The character of the victim of the crime being witnessed.
---@return Barotrauma_Character[] Characters that have witnessed the crime.
function getWitnessesToCrime(user, victim)
	local witnesses = {}
	for _, char in pairs(Character.CharacterList) do
		if
			not char.Removed
			and not char.IsUnconscious
			and char.IsBot
			and char.IsHuman
			and isOnSameTeam(char, victim)
		then
			local isWitnessingUser = canAiSeeTarget(char, user)
			if isWitnessingUser then
				table.insert(witnesses, char)
			end
		end
	end
	return witnesses
end

---@param user Barotrauma_Character The character that is applying the affected item.
---@param target Barotrauma_Character The character of the target of the affected item's application.
function onAffectedItemApplied(user, target)
	if not hasConsent(user, target) and target.IsBot and target.IsHuman then
		if not target.IsIncapacitated and target.Stun <= 10 then
			makeHostile(target, user)
		else
			-- Vanilla Barotrauma Human AI doesn't care what you do to their unconscious teammates, even shooting them in the head
			-- Let's fix that for this particular case of mistreatment
			local witnesses = getWitnessesToCrime(user, target)
			for _, witness in pairs(witnesses) do
				makeHostile(witness, user)
			end
		end
	end
end

return Environment.Export(_ENV)
