static async Task RunOneTimeAsync(CLIOptions options)
        {
            ProblemSolver solver = new ProblemSolver(options.LogProgress);

            using (var apiaryClient = new ApiaryHttpClient())
            {
                Console.WriteLine("Getting problem ...");
                var problemDefinition = await apiaryClient.GetProblemDefinitionAsync();

                Console.WriteLine($"Solving {problemDefinition.Id}");

                Stopwatch sw   = Stopwatch.StartNew();
                string    path = solver.Solve(problemDefinition);
                sw.Stop();
                Console.WriteLine($"Solved [{sw.ElapsedMilliseconds}ms]");
                Console.WriteLine("Posting solution");
                var problemSolution = new ProblemSolutionDto
                {
                    Path = path
                };
                var response = await apiaryClient.PutSolution(problemDefinition, problemSolution);

                Console.WriteLine($"Valid: {(response.Valid ? "yes" : "no")}");
                Console.WriteLine($"In time: {(response.InTime ? "yes" : "no")}");
                if (!string.IsNullOrEmpty(response.Message))
                {
                    Console.WriteLine($"Message: {response.Message}");
                }
            }
        }
        static async Task RunMineAsync(CLIOptions options)
        {
            var solver = new ProblemSolver(options.LogProgress);

            using (var apiaryClient = new ApiaryHttpClient(options.SaveApiCalls))
                using (var solutionService = options.SaveSolutions ? new ProblemSolutionService() : null)
                {
                    int       fails             = 0;
                    const int MaxFailInRowCount = 3;
                    DateTime  time = DateTime.MinValue;
                    while (true)
                    {
                        time = await ThrottleAsync(time, ApiaryHttpClient.RequiredDelay);

                        var problemDefinition = await apiaryClient.GetProblemDefinitionAsync();

                        Console.WriteLine($"Solving {problemDefinition.Id}");
                        Stopwatch sw   = Stopwatch.StartNew();
                        string    path = solver.Solve(problemDefinition);
                        sw.Stop();
                        Console.WriteLine($"Solved [{sw.ElapsedMilliseconds}ms]");
                        time = await ThrottleAsync(time, ApiaryHttpClient.RequiredDelay);

                        var problemSolution = new ProblemSolutionDto
                        {
                            Path = path
                        };
                        var response = await apiaryClient.PutSolution(problemDefinition, problemSolution);

                        if (!response.Valid || !response.InTime)
                        {
                            var ex = new Exception($"Invalid solution for problem={problemDefinition.Id}");
                            ex.Data[nameof(ProblemSolutionDto)]   = problemSolution;
                            ex.Data[nameof(ProblemDefinitionDto)] = problemDefinition;
                            throw ex;
                        }

                        if (options.SaveSolutions)
                        {
                            var added = await solutionService?.RegisterSolutionAsync(problemDefinition.Id, path);

                            if (!added)
                            {
                                fails++;
                            }
                            else
                            {
                                fails = 0;
                            }

                            if (fails == MaxFailInRowCount)
                            {
                                break;
                            }
                        }
                    }
                }
        }
        static Task RunAsync(CLIOptions options)
        {
            switch (options.RunType)
            {
            case RunType.OneTime:
                return(RunOneTimeAsync(options));

            case RunType.Mine:
                return(RunMineAsync(options));

            default:
                throw new NotImplementedException();
            }
        }
        static async Task Main(string[] args)
        {
            var parser = Parser.Default;

            CLIOptions options = null;
            var        result  = parser.ParseArguments <CLIOptions>(args)
                                 .WithParsed(opt => options = opt)
                                 .WithNotParsed(_ => Environment.Exit(1));

            try
            {
                await RunAsync(options);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex);
                Environment.Exit(1);
            }
        }