using GameList.Contracts; using GameList.Data; using GameList.Domain; using Microsoft.EntityFrameworkCore; namespace GameList.Endpoints; internal sealed class VoteWorkflowService(AppDbContext db) { public async Task GetMineAsync(Guid playerId) { var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, playerId); if (phase != Phase.Vote) return EndpointHelpers.PhaseMismatch(Phase.Vote, phase); var votes = await db.Votes .AsNoTracking() .Where(v => v.PlayerId == playerId) .Select(v => new { v.SuggestionId, v.Score }) .ToListAsync(); return Results.Ok(votes); } public async Task UpsertAsync(Guid playerId, int suggestionId, int score) { if (score is < 0 or > 10) return EndpointHelpers.BadRequestError("Score must be between 0 and 10."); var playerState = await db.Players .AsNoTracking() .Where(p => p.Id == playerId) .Select(p => new { p.VotesFinal, p.DisplayName }) .FirstAsync(); if (playerState.VotesFinal) return EndpointHelpers.BadRequestError("Votes are finalized. Unfinalize before changing scores."); var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, playerId); if (phase != Phase.Vote) return EndpointHelpers.PhaseMismatch(Phase.Vote, phase); if (string.IsNullOrWhiteSpace(playerState.DisplayName)) return EndpointHelpers.BadRequestError("Set a display name before voting."); var linkMap = await db.Suggestions .AsNoTracking() .Select(s => new { s.Id, s.ParentSuggestionId }) .ToListAsync(); var rootIndex = EndpointHelpers.BuildLinkRoots(linkMap.Select(s => (s.Id, s.ParentSuggestionId))); if (!rootIndex.ContainsKey(suggestionId)) return EndpointHelpers.BadRequestError("Suggestion not found."); var linkedIds = EndpointHelpers.LinkedIdsFor(suggestionId, rootIndex); if (linkedIds.Count == 0) linkedIds.Add(suggestionId); var existingVotes = await db.Votes .Where(v => v.PlayerId == playerId && linkedIds.Contains(v.SuggestionId)) .ToListAsync(); foreach (var linkedSuggestionId in linkedIds) { var vote = existingVotes.FirstOrDefault(v => v.SuggestionId == linkedSuggestionId); if (vote == null) { db.Votes.Add(new Vote { PlayerId = playerId, SuggestionId = linkedSuggestionId, Score = score }); } else { vote.Score = score; } } await db.SaveChangesAsync(); return Results.Ok(new VoteUpsertResponse(linkedIds, score)); } public async Task SetFinalizeAsync(Guid playerId, bool final) { var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, playerId); if (phase != Phase.Vote) return EndpointHelpers.PhaseMismatch(Phase.Vote, phase); var player = await db.Players.FirstAsync(p => p.Id == playerId); player.VotesFinal = final; await db.SaveChangesAsync(); return Results.Ok(new VoteFinalizeResponse(player.VotesFinal)); } }