private async Task <bool> AreLinesValidAsync(UpdateLinePreferencesViewModel model, CancellationToken cancellationToken)
        {
            if (model.FavoriteLines != null)
            {
                ITflService            service = _tflServiceFactory.CreateService();
                ICollection <LineInfo> lines   = await service.GetLinesAsync(cancellationToken);

                IList <string> validLines = lines.Select((p) => p.Id).ToList();

                return(model.FavoriteLines.All((p) => validLines.Contains(p)));
            }

            return(true);
        }
        public async Task <IActionResult> UpdateLinePreferences(
            [Bind(nameof(UpdateLinePreferencesViewModel.ETag), nameof(UpdateLinePreferencesViewModel.FavoriteLines))]
            UpdateLinePreferencesViewModel model,
            CancellationToken cancellationToken)
        {
            if (model == null || string.IsNullOrWhiteSpace(model.ETag))
            {
                return(BadRequest());
            }

            var user = await GetCurrentUserAsync();

            if (user == null)
            {
                _logger.LogError("Failed to get user to update line preferences.");

                return(View("Error"));
            }

            bool?updated = null;

            // Do not bother updating the preferences if they are they same
            bool hasModelBeenUpdated =
                model.FavoriteLines == null ||
                !model.FavoriteLines.SequenceEqual(user.FavoriteLines);

            if (hasModelBeenUpdated)
            {
                if (!await AreLinesValidAsync(model, cancellationToken))
                {
                    return(BadRequest());
                }

                _logger.LogTrace("Updating line preferences for user Id {UserId}.", user.Id);

                var existingLines = user.FavoriteLines;
                var newLines      = user.FavoriteLines = (model.FavoriteLines ?? Array.Empty <string>())
                                                         .OrderBy((p) => p, StringComparer.Ordinal)
                                                         .ToArray();

                // Override the ETag with the one in the model to ensure write consistency
                user.ETag = model.ETag;

                var result = await _userManager.UpdateAsync(user);

                if (result.Succeeded)
                {
                    _telemetry.TrackLinePreferencesUpdated(user.Id, existingLines, newLines);

                    _logger.LogInformation("Updated line preferences for user Id {UserId}.", user.Id);
                }
                else
                {
                    _logger.LogWarning(
                        "Failed to update line preferences for user '{UserId}' as it would cause a write conflict. ETag: {ETag}.",
                        user.Id,
                        model.ETag);
                }

                updated = result.Succeeded;
            }

            return(RedirectToRoute(SiteRoutes.Home, new { UpdateSuccess = updated }));
        }