--[=[
	@private

	@param onFulfilled function?
	@param onRejected function?
	@param promise2 Promise<T>? -- May be nil. If it is, then we have the option to return self
	@return Promise
]=]

function Promise:_executeThen(onFulfilled, onRejected, promise2)
	if self._fulfilled then
		if type(onFulfilled) == "function" then
			-- If either onFulfilled or onRejected returns a value x, run
			-- the Promise Resolution Procedure [[Resolve]](promise2, x).
			if promise2 then
				promise2:Resolve(onFulfilled(unpack(self._fulfilled, 1, self._valuesLength)))
				return promise2
			else
				local results = table.pack(onFulfilled(unpack(self._fulfilled, 1, self._valuesLength)))
				if results.n == 0 then
					return _emptyFulfilledPromise
				elseif results.n == 1 and Promise.isPromise(results[1]) then
					return results[1]
				else
					local promise = Promise.new()
					-- Technically undefined behavior from A+, but we'll resolve to nil like ES6 promises
					promise:Resolve(table.unpack(results, 1, results.n))
					return promise
				end
			end
		else
			-- If onFulfilled is not a function, it must be ignored.
			-- If onFulfilled is not a function and promise1 is fulfilled,
			-- promise2 must be fulfilled with the same value as promise1.
			if promise2 then
				promise2:_fulfill(self._fulfilled, self._valuesLength)
				return promise2
			else
				return self
			end
		end
	elseif self._rejected then
		if type(onRejected) == "function" then
			-- If either onFulfilled or onRejected returns a value x, run
			-- the Promise Resolution Procedure [[Resolve]](promise2, x).
			if promise2 then
				promise2:Resolve(onRejected(unpack(self._rejected, 1, self._valuesLength)))
				return promise2
			else
				local results = table.pack(onRejected(unpack(self._rejected, 1, self._valuesLength)))
				if results.n == 0 then
					return _emptyFulfilledPromise
				elseif results.n == 1 and Promise.isPromise(results[1]) then
					return results[1]
				else
					local promise = Promise.new()
					-- Technically undefined behavior from A+, but we'll resolve to nil like ES6 promises
					promise:Resolve(table.unpack(results, 1, results.n))
					return promise
				end
			end
		else
			-- If onRejected is not a function, it must be ignored.
			-- If onRejected is not a function and promise1 is rejected, promise2 must be
			-- rejected with the same reason as promise1.
			if promise2 then
				promise2:_reject(self._rejected, self._valuesLength)

				return promise2
			else
				return self
			end
		end
	else
		error("Internal error: still pending")
	end
end
