KoogleApp/src/Koogle.Application/Services/GamePersistenceService.cs

177 lines
5.3 KiB
C#

using Koogle.Application.DTOs;
using Koogle.Application.Interfaces;
using Koogle.Domain.Entities;
using Koogle.Domain.Enums;
using KoogleApp.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Koogle.Application.Services;
/// <summary>
/// Service for persisting and recovering game state.
/// </summary>
public class GamePersistenceService : IGamePersistenceService
{
private readonly IDbContextFactory<AppDbContext> _contextFactory;
private readonly ILogger<GamePersistenceService> _logger;
/// <summary>
/// Initializes a new instance of the GamePersistenceService class.
/// </summary>
public GamePersistenceService(
IDbContextFactory<AppDbContext> contextFactory,
ILogger<GamePersistenceService> logger)
{
_contextFactory = contextFactory;
_logger = logger;
}
/// <inheritdoc />
public async Task<Guid> CreateGameAsync(CreateGameDto dto)
{
await using var context = await _contextFactory.CreateDbContextAsync();
var game = new Game
{
Id = Guid.NewGuid(),
DayId = dto.DayId,
ClubId = dto.ClubId,
GameType = dto.GameType,
GameData = dto.InitialGameStateJson,
Status = GameStatus.Active,
StartedAt = DateTime.UtcNow,
CreatedAt = DateTime.UtcNow
};
// Add game participants
foreach (var playerId in dto.PlayerIds)
{
game.GamePersons.Add(new GamePerson
{
GameId = game.Id,
PersonId = playerId,
ClubId = dto.ClubId
});
}
context.Games.Add(game);
await context.SaveChangesAsync();
_logger.LogInformation(
"Game created: {GameId}, Type: {GameType}, Day: {DayId}, Players: {PlayerCount}",
game.Id, dto.GameType, dto.DayId, dto.PlayerIds.Length);
return game.Id;
}
/// <inheritdoc />
public async Task<bool> SaveGameStateAsync(Guid gameId, string gameStateJson)
{
await using var context = await _contextFactory.CreateDbContextAsync();
var game = await context.Games.FindAsync(gameId);
if (game == null)
{
_logger.LogWarning("Game not found: {GameId}", gameId);
return false;
}
game.GameData = gameStateJson;
game.ModifiedAt = DateTime.UtcNow;
try
{
await context.SaveChangesAsync();
_logger.LogDebug("Game state saved: {GameId}", gameId);
return true;
}
catch (DbUpdateConcurrencyException ex)
{
_logger.LogWarning(ex, "Concurrency conflict saving game: {GameId}", gameId);
return false;
}
}
/// <inheritdoc />
public async Task<ActiveGameDto?> LoadActiveGameAsync(Guid dayId)
{
await using var context = await _contextFactory.CreateDbContextAsync();
var game = await context.Games
.Include(g => g.GamePersons)
.Where(g => g.DayId == dayId && g.Status == GameStatus.Active && !g.IsDeleted)
.FirstOrDefaultAsync();
if (game == null)
{
_logger.LogDebug("No active game found for day: {DayId}", dayId);
return null;
}
_logger.LogInformation(
"Active game loaded: {GameId}, Type: {GameType}",
game.Id, game.GameType);
return new ActiveGameDto
{
Id = game.Id,
DayId = game.DayId,
GameType = game.GameType,
StartedAt = game.StartedAt,
PlayerIds = game.GamePersons.Select(gp => gp.PersonId).ToArray(),
GameStateJson = game.GameData
};
}
/// <inheritdoc />
public async Task EndGameAsync(Guid gameId, GameStatus status)
{
if (status == GameStatus.Active)
{
throw new ArgumentException("Cannot end game with Active status", nameof(status));
}
await using var context = await _contextFactory.CreateDbContextAsync();
var game = await context.Games.FindAsync(gameId);
if (game == null)
{
_logger.LogWarning("Game not found for ending: {GameId}", gameId);
return;
}
game.Status = status;
game.CompletedAt = DateTime.UtcNow;
game.ModifiedAt = DateTime.UtcNow;
await context.SaveChangesAsync();
_logger.LogInformation(
"Game ended: {GameId}, Status: {Status}",
gameId, status);
}
/// <inheritdoc />
public async Task<IReadOnlyList<GameSummaryDto>> GetCompletedGamesAsync(Guid dayId)
{
await using var context = await _contextFactory.CreateDbContextAsync();
var games = await context.Games
.Include(g => g.GamePersons)
.Where(g => g.DayId == dayId && g.Status != GameStatus.Active && !g.IsDeleted)
.OrderByDescending(g => g.CompletedAt)
.ToListAsync();
return games.Select(g => new GameSummaryDto
{
Id = g.Id,
GameTypeName = g.GameType,
Status = g.Status,
StartedAt = g.StartedAt,
CompletedAt = g.CompletedAt,
ParticipantCount = g.GamePersons.Count
}).ToList();
}
}