using Backend.Common; using Backend.Contracts; using Backend.Data; using Microsoft.EntityFrameworkCore; namespace Backend.Endpoints; public static partial class PublicEndpoints { private static async Task GetOverview(AwardsDbContext db) { var siteSettings = await db.SiteSettings.AsNoTracking().FirstOrDefaultAsync(item => item.Id == 1); var season = await db.Seasons .AsNoTracking() .Include(item => item.Categories.OrderBy(category => category.SortOrder)) .ThenInclude(category => category.Candidates) .OrderByDescending(item => item.Year) .FirstOrDefaultAsync(item => item.IsCurrent); if (season is null) { return Results.NotFound(); } if (siteSettings is null) { return Results.Problem("Site settings are missing."); } var today = DateOnly.FromDateTime(DateTime.UtcNow); var showactApplicationsOpenNow = ShowactApplicationSchedule.IsOpenNow(siteSettings, today); var canExposeCurrentSeasonWinners = CanExposeCurrentSeasonWinners(season.CurrentPhase); var winnerPreviewRows = await db.Results .AsNoTracking() .Include(result => result.Season) .Include(result => result.Candidate) .Where(result => result.Season.Year < season.Year || canExposeCurrentSeasonWinners && result.Season.Year == season.Year) .OrderByDescending(result => result.Season.Year) .ThenBy(result => result.CategoryName) .Take(8) .Select(result => new { Year = result.Season.Year, result.CategoryName, WinnerName = result.Candidate.DisplayName, WinnerSlug = result.Candidate.ChannelSlug, WinnerPlatform = result.Candidate.Platform, ClipUrl = result.Candidate.ClipCompilationUrl, ClipTitle = result.Candidate.ClipCompilationTitle, ClipPlatform = result.Candidate.ClipCompilationPlatform, ClipEmbedStatus = result.Candidate.ClipEmbedStatus, }) .ToArrayAsync(); var winnerPreviewItems = winnerPreviewRows .Select(result => new WinnerPreviewDto( result.Year, result.CategoryName, result.WinnerName, result.WinnerSlug, result.WinnerPlatform, SeasonMappings.BuildProfileUrl(result.WinnerPlatform, result.WinnerSlug), result.ClipUrl, result.ClipTitle, result.ClipPlatform, result.ClipEmbedStatus)) .ToArray(); var archiveYearRows = await db.Results .AsNoTracking() .Where(result => result.Season.Year < season.Year || canExposeCurrentSeasonWinners && result.Season.Year == season.Year) .GroupBy(result => result.Season.Year) .Select(group => new { Year = group.Key, WinnerCount = group.Count(), }) .OrderByDescending(item => item.Year) .ToArrayAsync(); var archiveYears = archiveYearRows .Select(item => new ArchiveYearDto(item.Year, item.WinnerCount)) .ToArray(); var phaseKey = SeasonMappings.NormalizePhaseKey(season.CurrentPhase); var publicCategories = season.Categories .Where(category => ShouldExposePublicCategory(phaseKey, category.Candidates.Count)) .ToArray(); var featuredCategories = publicCategories .GroupBy(category => category.GroupName.Trim(), StringComparer.OrdinalIgnoreCase) .Select(group => { var ordered = group .OrderBy(category => category.SortOrder) .ThenBy(category => category.Name, StringComparer.OrdinalIgnoreCase) .ToArray(); var first = ordered[0]; var maxNomineesPerUser = ordered .Select(category => category.MaxNomineesPerUser) .Where(value => value > 0) .DefaultIfEmpty(3) .Max(); return new FeaturedCategoryDto( first.Id, first.GroupName, first.GroupName, first.Description, maxNomineesPerUser); }) .OrderBy(category => publicCategories .Where(item => string.Equals(item.GroupName, category.GroupName, StringComparison.OrdinalIgnoreCase)) .Min(item => item.SortOrder)) .ThenBy(category => category.GroupName, StringComparer.OrdinalIgnoreCase) .ToArray(); var response = new OverviewResponse( season.Id, season.Year, season.Name, season.ShowDate, season.ShowStartsAt, SeasonMappings.NormalizeSeasonStreamUrl(season.ShowStreamUrl), season.CurrentPhase, season.IsCommunityOnly, "Twitch", new[] { new TimelineItem("nomination", "Nominierung", season.NominationStartsAt, season.NominationEndsAt, SeasonMappings.ResolveTimelineState("nomination", phaseKey)), new TimelineItem("voting", "Voting", season.VotingStartsAt, season.VotingEndsAt, SeasonMappings.ResolveTimelineState("voting", phaseKey)), new TimelineItem("preparation", "Aufbereitung", season.ReviewStartsAt, season.ReviewEndsAt, SeasonMappings.ResolveTimelineState("preparation", phaseKey)), new TimelineItem("show", "Award Show", season.ShowDate, season.ShowDate, SeasonMappings.ResolveTimelineState("show", phaseKey)), }, featuredCategories, winnerPreviewItems, archiveYears, new PublicSiteContentDto( siteSettings.HostDisplayName, siteSettings.HostTagline, siteSettings.NewsletterUrl, siteSettings.ShareXUrl, siteSettings.ShareDiscordUrl, siteSettings.PrivacyEmail, siteSettings.PrivacyPolicyContent, SeasonMappings.ReadSocialLinks(siteSettings), SeasonMappings.BuildFooterLinks(siteSettings)), new PublicFeatureFlagsDto( siteSettings.ClipSubmissionsEnabled, siteSettings.ClipReviewEnabled, string.IsNullOrWhiteSpace(siteSettings.ClipSubmissionDisabledMessage) ? "Clip-Einreichungen sind aktuell geschlossen." : siteSettings.ClipSubmissionDisabledMessage, showactApplicationsOpenNow, siteSettings.ShowactApplicationStartsAt, siteSettings.ShowactApplicationEndsAt, string.IsNullOrWhiteSpace(siteSettings.ShowactApplicationDisabledMessage) ? "Showact-Bewerbungen sind aktuell geschlossen." : siteSettings.ShowactApplicationDisabledMessage, siteSettings.SponsorsVisible, siteSettings.ShowactFormSchemaJson ?? "[]"), SeasonMappings.ReadFaqItems(siteSettings)); return Results.Ok(response); } }