dofile_once("mods/lewd_noita/files/status_effects/parasite_utils.lua")
dofile_once("mods/lewd_noita/files/text/ln_text.lua")

local LOS_RANGE = 200

-- Returns the nearest enemy with unobstructed line of sight, or nil.
local function find_los_enemy(px, py)
	local nearest = nil
	local nearest_dist = 99999
	for _, e in ipairs(EntityGetInRadiusWithTag(px, py, LOS_RANGE, "enemy") or {}) do
		local ex, ey = EntityGetTransform(e)
		if ex and ey then
			local dist = math.sqrt((ex - px) * (ex - px) + (ey - py) * (ey - py))
			if dist < nearest_dist then
				-- Raytrace from player to enemy; hit == true means something
				-- solid is blocking the view.
				local hit = RaytraceSurfaces(px, py, ex, ey)
				if not hit then
					nearest = e
					nearest_dist = dist
				end
			end
		end
	end
	return nearest
end

function try_compulsion(player_id, parasite_name, stage, config)
	if stage < config.min_stage then return end

	local frame = GameGetFrameNum()
	local active = GlobalsGetValue("LN_COMPULSION_ACTIVE", "0")
	local UI_MSG = "mods/lewd_noita/files/sex_logic/ui_msg.png"

	-- Block if wolf leash is active
	if GlobalsGetValue("LN_WOLF_BOND_ACTIVE", "0") == "1" then return end

	-- Block during birth/sex scenes
	if EntityGetWithName("ln_sex_cooldown") ~= 0 then return end
	local bt = EntityGetWithTag("birth_scene") or {}
	if #bt > 0 then return end

	if active == "1" then
		local source = GlobalsGetValue("LN_COMPULSION_SOURCE", "")
		if source ~= parasite_name then return end

		local end_frame = tonumber(GlobalsGetValue("LN_COMPULSION_END", "0")) or 0
		local start_frame = tonumber(GlobalsGetValue("LN_COMPULSION_START", "0")) or 0

		if frame >= end_frame or (start_frame > 0 and (frame - start_frame) > 60 * 60) then
			release_compulsion(player_id, config)
			return
		end

		if config.messages.during and frame % 120 == 0 then
			SetRandomSeed(frame, player_id + 7777)
			local msg = ln_compulsion_during_msg(parasite_name)
			if msg ~= nil then
				GamePrint(msg)
			else
				local msgs = config.messages.during
				GamePrint(msgs[Random(1, #msgs)])
			end
		end
		return
	end

	-- Cooldown check
	local cooldown_end = tonumber(GlobalsGetValue("LN_COMPULSION_COOLDOWN", "0")) or 0
	if frame < cooldown_end then return end

	-- Require line-of-sight to an enemy before rolling the chance at all
	local px, py = EntityGetTransform(player_id)
	local los_enemy = find_los_enemy(px, py)
	if los_enemy == nil then return end

	-- Chance: 5% per second per tier above min_stage.
	-- tier_above_min = 0 at min_stage (still counts as tier 1 for the 5% baseline)
	-- stage 3 (min=3) -> tier 1 -> 5%; stage 5 -> tier 3 -> 15%
	local tier = (stage - config.min_stage) + 1
	local chance_percent = 5 * tier

	-- This function is called once per parasite script tick (execute_every_n_frame,
	-- typically 60 = 1 second), so this IS effectively a per-second roll.
	SetRandomSeed(frame, player_id + 3333)
	if Random(1, 100) > chance_percent then return end

	-- START COMPULSION
	SetRandomSeed(frame, player_id + 5555)
	local duration = Random(config.duration_min, config.duration_max)

	GlobalsSetValue("LN_COMPULSION_ACTIVE", "1")
	GlobalsSetValue("LN_COMPULSION_START", tostring(frame))
	GlobalsSetValue("LN_COMPULSION_END", tostring(frame + duration))
	GlobalsSetValue("LN_COMPULSION_SOURCE", parasite_name)

	local cc = EntityGetFirstComponent(player_id, "ControlsComponent")
	if cc then
		ComponentSetValue2(cc, "enabled", false)
	end

	local inv_gui = EntityGetFirstComponent(player_id, "InventoryGuiComponent")
	if inv_gui then
		ComponentSetValue2(inv_gui, "mActive", false)
	end

	local puppet = EntityCreateNew("ln_compulsion_puppet")
	EntityAddComponent2(puppet, "LuaComponent", {
		script_source_file = "mods/lewd_noita/files/status_effects/parasite_compulsion_drive.lua",
		execute_every_n_frame = 1,
	})
	EntityAddChild(player_id, puppet)

	if LN_TEXT[parasite_name] and LN_TEXT[parasite_name].compulsion then
		ln_print_compulsion(parasite_name, "onset", UI_MSG)
	elseif config.messages and config.messages.onset then
		GamePrintImportant(config.messages.onset[1], config.messages.onset[2], UI_MSG)
	end
end

function release_compulsion(player_id, config)
	local UI_MSG = "mods/lewd_noita/files/sex_logic/ui_msg.png"
	local parasite_name = GlobalsGetValue("LN_COMPULSION_SOURCE", "")

	for _, child in ipairs(EntityGetAllChildren(player_id) or {}) do
		if EntityGetName(child) == "ln_compulsion_puppet" then
			EntityKill(child)
		end
	end

	local cc = EntityGetFirstComponentIncludingDisabled(player_id, "ControlsComponent")
	if cc then
		ComponentSetValue2(cc, "enabled", true)
		ComponentSetValue2(cc, "mButtonDownLeft", false)
		ComponentSetValue2(cc, "mButtonDownRight", false)
		ComponentSetValue2(cc, "mButtonDownUp", false)
		ComponentSetValue2(cc, "mButtonDownDown", false)
		ComponentSetValue2(cc, "mButtonDownFire", false)
		ComponentSetValue2(cc, "mButtonDownFire2", false)
		ComponentSetValue2(cc, "mButtonDownThrow", false)
		ComponentSetValue2(cc, "mButtonDownKick", false)
		ComponentSetValue2(cc, "mButtonDownEat", false)
		ComponentSetValue2(cc, "mButtonDownFly", false)
	end

	SetRandomSeed(GameGetFrameNum(), player_id + 9999)
	local cooldown = Random(config.cooldown_min, config.cooldown_max)
	GlobalsSetValue("LN_COMPULSION_COOLDOWN", tostring(GameGetFrameNum() + cooldown))
	GlobalsSetValue("LN_COMPULSION_ACTIVE", "0")
	GlobalsSetValue("LN_COMPULSION_SOURCE", "")
	GlobalsSetValue("LN_COMPULSION_START", "0")

	if parasite_name ~= "" and LN_TEXT[parasite_name] and LN_TEXT[parasite_name].compulsion then
		ln_print_compulsion(parasite_name, "release", UI_MSG)
	elseif config.messages and config.messages.release then
		GamePrintImportant(config.messages.release[1], config.messages.release[2], UI_MSG)
	end
end

function force_release_compulsion(player_id, parasite_name)
	if GlobalsGetValue("LN_COMPULSION_SOURCE", "") == parasite_name then
		for _, child in ipairs(EntityGetAllChildren(player_id) or {}) do
			if EntityGetName(child) == "ln_compulsion_puppet" then
				EntityKill(child)
			end
		end
		local cc = EntityGetFirstComponentIncludingDisabled(player_id, "ControlsComponent")
		if cc then
			ComponentSetValue2(cc, "enabled", true)
			ComponentSetValue2(cc, "mButtonDownLeft", false)
			ComponentSetValue2(cc, "mButtonDownRight", false)
			ComponentSetValue2(cc, "mButtonDownUp", false)
			ComponentSetValue2(cc, "mButtonDownFire", false)
			ComponentSetValue2(cc, "mButtonDownFire2", false)
		end
		GlobalsSetValue("LN_COMPULSION_ACTIVE", "0")
		GlobalsSetValue("LN_COMPULSION_SOURCE", "")
		GlobalsSetValue("LN_COMPULSION_START", "0")
	end
end
