private async Task <List <AchievementOverviewViewModel> > GetCollectionAchievementOverviewsAsync(string userId, IList <Milestone> collectionMilestones)
        {
            List <AchievementOverviewViewModel> achievementOverviews       = new List <AchievementOverviewViewModel>();
            List <AchievementStatusViewModel>   currentAchievementStatuses = new List <AchievementStatusViewModel>();
            AchievementOverviewViewModel        currentAchievementOverView = new AchievementOverviewViewModel();

            // get progress towards all valid milestone types at once so we don't have to keep querying for it
            var collectionMilestoneTypeIds = collectionMilestones
                                             .Select(x => x.MilestoneTypeId)
                                             .Distinct()
                                             .ToList();
            var achievementProgress = await GetUserProgressTowardsAchievementsAsync(userId, collectionMilestoneTypeIds);

            int       currentMilestoneTypeId  = 0;
            int       previousMilestoneTypeId = 0;
            Milestone previousMilestone       = null;

            foreach (var milestone in collectionMilestones)
            {
                previousMilestoneTypeId = currentMilestoneTypeId;
                currentMilestoneTypeId  = milestone.MilestoneTypeId;

                // we hit a new milestone type, create a new collection
                if (currentMilestoneTypeId != previousMilestoneTypeId)
                {
                    // save off the current group before starting over but not if this is the first item
                    if (previousMilestoneTypeId > 0)
                    {
                        currentAchievementOverView.AchievementStatuses    = currentAchievementStatuses;
                        currentAchievementOverView.AchievementTypeName    = previousMilestone.MilestoneType.Name;
                        currentAchievementOverView.AchievementDescription = previousMilestone.MilestoneType.Description;
                        achievementOverviews.Add(currentAchievementOverView);
                    }

                    // start over for the next group
                    currentAchievementOverView = new AchievementOverviewViewModel();
                    currentAchievementStatuses = new List <AchievementStatusViewModel>();
                }

                int amountAchieved = achievementProgress[milestone.MilestoneTypeId];

                currentAchievementStatuses.Add(new AchievementStatusViewModel()
                {
                    RequiredCount       = milestone.AmountRequired,
                    AchievedCount       = amountAchieved <= milestone.AmountRequired ? amountAchieved : milestone.AmountRequired,
                    AchievementIconPath = milestone.GetImagePath()
                });

                previousMilestone = milestone;
            }

            currentAchievementOverView.AchievementStatuses    = currentAchievementStatuses;
            currentAchievementOverView.AchievementTypeName    = previousMilestone.MilestoneType.Name;
            currentAchievementOverView.AchievementDescription = previousMilestone.MilestoneType.Description;
            achievementOverviews.Add(currentAchievementOverView);

            return(achievementOverviews);
        }
        private async Task <IReadOnlyCollection <AchievementOverviewViewModel> > GetSoloAchievementOverviewsAsync(string userId, IEnumerable <Milestone> soloMilestones)
        {
            var soloMilestoneIds = soloMilestones.Select(x => x.Id).ToList();

            var completedMilestones = await(from achievements in db.MilestoneAchievements
                                            where achievements.UserId == userId
                                            where soloMilestoneIds.Contains(achievements.MilestoneId)
                                            select achievements)
                                      .ToDictionaryAsync(x => x.MilestoneId, x => x.Milestone);

            List <AchievementOverviewViewModel> achievementOverviews = new List <AchievementOverviewViewModel>();

            foreach (var soloMilestone in soloMilestones)
            {
                var achievementOverview = new AchievementOverviewViewModel()
                {
                    AchievementTypeName    = soloMilestone.MilestoneType.Name,
                    AchievementDescription = soloMilestone.MilestoneType.Description,
                    AchievementStatuses    = new List <AchievementStatusViewModel>()
                };

                int achievedCount = HasUserCompletedAchievement(completedMilestones, soloMilestone);

                achievementOverview.AchievementStatuses.Add(new AchievementStatusViewModel()
                {
                    AchievedCount       = achievedCount,
                    RequiredCount       = soloMilestone.AmountRequired,
                    AchievementIconPath = soloMilestone.IconFileName
                });

                achievementOverviews.Add(achievementOverview);
            }

            foreach (var achievementOverview in achievementOverviews)
            {
                // this will always only be a single item for solo achievements
                foreach (var achievementStatus in achievementOverview.AchievementStatuses)
                {
                    achievementStatus.AchievementIconPath = MilestoneExtensions.GetImagePath(achievementStatus.AchievementIconPath);
                }
            }

            return(achievementOverviews.AsReadOnly());
        }