using Backend.Contracts; using Backend.Common; using Backend.Data; using Backend.Domain; using Backend.Services; using Microsoft.EntityFrameworkCore; namespace Backend.Endpoints; public static partial class AdminSeasonManagementEndpoints { private static async Task SetResult( HttpContext context, int seasonId, SetAwardResultRequest request, AwardsDbContext db, IAdminAuditService adminAuditService) { var session = AdminEndpointConventions.CurrentSession(context); var category = await db.Categories .AsNoTracking() .FirstOrDefaultAsync(item => item.Id == request.CategoryId && item.SeasonId == seasonId); if (category is null) { return Results.BadRequest(new { message = "The selected category does not exist in this season." }); } var candidate = await db.Candidates .AsNoTracking() .FirstOrDefaultAsync(item => item.Id == request.CandidateId && item.SeasonId == seasonId && item.CategoryId == request.CategoryId); if (candidate is null) { return Results.BadRequest(new { message = "The selected candidate does not belong to the selected category." }); } var workflowRules = await LoadWorkflowRulesAsync(db, context.RequestAborted); var winnerRequiresClipRule = WorkflowRuleSettings.Find(workflowRules, WorkflowRuleSettings.WinnerRequiresClip); if (WorkflowRuleSettings.ShouldBlock(winnerRequiresClipRule) && string.IsNullOrWhiteSpace(candidate.ClipCompilationUrl)) { return CreateWorkflowRuleError( "Dieser Kandidat hat noch keinen gepflegten Clip-Link. Bitte zuerst die Clip-Compilation am Kandidaten hinterlegen oder die Workflow-Regel umstellen."); } var winnerPlacementsRule = WorkflowRuleSettings.Find(workflowRules, WorkflowRuleSettings.MaxWinnerPlacements); if (WorkflowRuleSettings.ShouldBlock(winnerPlacementsRule)) { var candidateIdentityKey = WorkflowRuleSettings.CandidateIdentityKey(candidate); var existingWinnerIdentities = await db.Results .AsNoTracking() .Include(item => item.Candidate) .Where(item => item.SeasonId == seasonId && item.CategoryId != request.CategoryId) .Select(item => new { item.CategoryId, item.Candidate.DisplayName, item.Candidate.ChannelSlug, }) .ToArrayAsync(context.RequestAborted); var existingWinnerCount = existingWinnerIdentities.Count(item => string.Equals(WorkflowRuleSettings.CandidateIdentityKey(item.DisplayName, item.ChannelSlug), candidateIdentityKey, StringComparison.Ordinal)); if (existingWinnerCount >= winnerPlacementsRule.Limit) { return CreateWorkflowRuleError( $"Diese Person hat bereits {existingWinnerCount} Gewinnerplatz(e). Limit: {winnerPlacementsRule.Limit}."); } } var existingResult = await db.Results.FirstOrDefaultAsync(item => item.SeasonId == seasonId && item.CategoryId == request.CategoryId); if (existingResult is null) { existingResult = new AwardResult { SeasonId = seasonId, CategoryId = request.CategoryId, CandidateId = request.CandidateId, CategoryName = category.Name, }; db.Results.Add(existingResult); } else { existingResult.CandidateId = request.CandidateId; existingResult.CategoryName = category.Name; } adminAuditService.AddEntry( session.TwitchUserId, "result.set", "result", $"{seasonId}:{request.CategoryId}", $"Gewinner für {category.Name} wurde gesetzt.", new { seasonId, categoryId = request.CategoryId, candidateId = request.CandidateId, candidateName = candidate.DisplayName, }, RequestMetadataReader.Read(context)); await db.SaveChangesAsync(context.RequestAborted); return Results.Ok(new { saved = true, resultId = existingResult.Id, seasonId, categoryId = request.CategoryId, candidateId = request.CandidateId, }); } private static async Task DeleteResult( HttpContext context, int resultId, AwardsDbContext db, IAdminAuditService adminAuditService) { var session = AdminEndpointConventions.CurrentSession(context); var result = await db.Results .Include(item => item.Category) .Include(item => item.Candidate) .FirstOrDefaultAsync(item => item.Id == resultId); if (result is null) { return Results.NotFound(); } db.Results.Remove(result); adminAuditService.AddEntry( session.TwitchUserId, "result.delete", "result", result.Id.ToString(), $"Gewinner für {result.Category.Name} wurde entfernt.", new { result.SeasonId, result.CategoryId, result.CandidateId, candidateName = result.Candidate.DisplayName, }, RequestMetadataReader.Read(context)); await db.SaveChangesAsync(context.RequestAborted); return Results.Ok(new { deleted = true, resultId }); } }