/// <summary>
        /// Runs application.
        /// </summary>
        /// <param name="args">CLI arguments.</param>
        /// <param name="cancellationToken">Cancellation token</param>
        public async Task <int> RunAsync(string[] args, CancellationToken cancellationToken = default)
        {
            await Task.Yield();

            // process CLI args
            var arguments = Cli.Parse <TArgs>(args);

            if (arguments.HelpInvoked)
            {
                return(0);
            }
            if (arguments.ShowVersion)
            {
                Console.WriteLine($"App version: {ApplicationHelper.GetAssemblyVersion()}");
                return(CuriosityExitCodes.Success);
            }

            // basic app setup: culture, content root directory
            var customContentRootDirectory = String.Empty;

            if (EnvironmentHelper.IsDevelopmentEnvironment())
            {
                customContentRootDirectory = Directory.GetCurrentDirectory();
            }

            ApplicationHelper.ChangeCurrentDirectoryForDevelopment();

            // get configuration
            var configurationProvider = new YamlConfigurationProvider <TConfiguration>(arguments.ConfigurationDirectory, IsConfigFileOptional, args);
            var configuration         = configurationProvider.GetConfiguration();

            ApplicationHelper.SetDefaultCulture(configuration.Culture);

            // configure logs
            var loggingConfiguration = new CuriosityNLogConfigurator(configuration.Log.LogConfigurationPath, LoadLoggingConfiguration);

            loggingConfiguration.WithLogOutputDirectory(configuration.Log.LogOutputDirectory);

            loggingConfiguration.WithAppName(configuration.AppName);
            // ReSharper disable once SuspiciousTypeConversion.Global
            if (configuration is IConfigurationWithMailLogger configurationWithMailLogger)
            {
                loggingConfiguration.WithMail(configurationWithMailLogger.LoggerMail);
            }
            loggingConfiguration.Configure();

            var logger = LogManager.GetCurrentClassLogger();

            logger.Info($"Using configurations from directory \"{configurationProvider.PathToConfigurationFiles}\"'");

            // print configuration
            var configPrinter = new ConfigurationPrinter();
            var configLog     = configPrinter.GetLog(configuration);

            logger.Info($"Starting app with configuration: \n{configLog ?? "<no config>"}");

            // validate configuration
            var errors = configuration.Validate();

            if (errors.Count == 0)
            {
                logger.Info("Configuration is valid.");
            }
            else
            {
                var errorsBuilder = new StringBuilder();
                foreach (var validationError in errors)
                {
                    errorsBuilder.AppendLine($"- {validationError.FieldName}: {validationError.Error}");
                }

                logger.Error($"Configuration is invalid. Errors: {Environment.NewLine}{errorsBuilder}");

                return(CuriosityExitCodes.IncorrectConfiguration);
            }

            try
            {
                var startEmailLogger = LogManager.GetLogger("appStartEmailLog");
                startEmailLogger.Info($"{configuration.AppName} is started");

                return(await RunInternalAsync(args, arguments, configuration, configurationProvider, customContentRootDirectory, cancellationToken));
            }
            catch (Exception e) when(e is OperationCanceledException || e is TaskCanceledException)
            {
                logger.Warn(e, $"{configuration.AppName} was cancelled.");

                return(CuriosityExitCodes.Cancellation);
            }
            catch (Exception e)
            {
                logger.Fatal(e, $"Critical error on {configuration.AppName} work");

                return(CuriosityExitCodes.UnhandledException);
            }
            finally
            {
                var startEmailLogger = LogManager.GetLogger("appStopEmailLog");
                startEmailLogger.Info($"{configuration.AppName} is stopped");
                // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
                LogManager.Shutdown();
            }
        }