--[[
	This function fixes a problem where when the Camera's CameraType is set back
	to Enum.CameraType.Custom, it snaps back to the last distance before we
	animated it.

	As such, if the user is zoomed far out, calling `focusCharacter()` will zoom
	in, and then once the animation finishes the camera snaps back to the
	distance it was at before.

	To work around this, we use this function to force the camera's zoom
	distance to the distance that the camera will be from the character when the
	animation completes.
]]

local function _setZoomDistanceHack(player: Player, distance: number, callback: () -> ())
	local min = player.CameraMinZoomDistance
	local max = player.CameraMaxZoomDistance

	player.CameraMinZoomDistance = distance
	player.CameraMaxZoomDistance = distance

	callback()

	player.CameraMinZoomDistance = min
	player.CameraMaxZoomDistance = max
end

local function focusCharacter(player: Player, camera: Camera, offset: number?): RBXScriptSignal
	offset = if offset ~= nil then offset else 10
	-- selene: allow(incorrect_standard_library_use)
	assert(offset)

	local character = player.Character
	local completed = Instance.new("BindableEvent")

	if character and character.PrimaryPart then
		local cameraType = camera.CameraType
		local motor = Otter.createSingleMotor(0)
		local direction = character:GetPrimaryPartCFrame().LookVector
		local goalPosition = camera.Focus.Position + (Vector3.new(offset, 0, offset) * direction)
		local goal = CFrame.lookAt(goalPosition, camera.Focus.Position)

		motor:onStep(function(alpha: number)
			camera.CFrame = camera.CFrame:Lerp(goal, alpha)
		end)

		motor:onComplete(function()
			completed:Fire()

			_setZoomDistanceHack(player, offset, function()
				camera.CameraType = cameraType

				-- Wait a frame to ensure the Camera has time to update based on the
				-- current max zoom distance before we leave this scope and reset it
				task.wait()
			end)
		end)

		camera.CameraType = Enum.CameraType.Scriptable
		motor:setGoal(Otter.spring(1, constants.Springs.Slowpoke))
	end

	return completed.Event
end

return focusCharacter
