using Marathon.Application.Abstractions; using Marathon.Application.Storage; using Marathon.Domain.Entities; using Marathon.Domain.Enums; using Marathon.Domain.ValueObjects; using Microsoft.EntityFrameworkCore; namespace Marathon.Infrastructure.Persistence.Repositories; internal sealed class PlacedBetRepository : IPlacedBetRepository { private readonly MarathonDbContext _db; public PlacedBetRepository(MarathonDbContext db) => _db = db; public async Task GetAsync(Guid key, CancellationToken ct = default) { var idStr = key.ToString(); // AsNoTracking so callers can re-map and UpdateAsync without tripping // EF's "another instance with the same key is already tracked" guard. var entity = await _db.PlacedBets.AsNoTracking() .FirstOrDefaultAsync(b => b.Id == idStr, ct); return entity is null ? null : Mapping.ToDomain(entity); } public async Task> ListAsync(CancellationToken ct = default) { var entities = await _db.PlacedBets.AsNoTracking().ToListAsync(ct); return entities.Select(Mapping.ToDomain).ToList().AsReadOnly(); } public async Task> ListByOutcomeAsync(BetOutcome outcome, CancellationToken ct = default) { var outcomeInt = (int)outcome; var entities = await _db.PlacedBets.AsNoTracking() .Where(b => b.Outcome == outcomeInt) .ToListAsync(ct); return entities.Select(Mapping.ToDomain).ToList().AsReadOnly(); } public async Task> ListByDateRangeAsync(DateRange range, CancellationToken ct = default) { // PlacedAt is stored via SqliteDateText (O-format TEXT) — same lexical-equals- // chronological ordering used across the repositories. var fromStr = SqliteDateText.Key(range.From); var toStr = SqliteDateText.Key(range.To); var entities = await _db.PlacedBets.AsNoTracking() .Where(b => b.PlacedAt.CompareTo(fromStr) >= 0 && b.PlacedAt.CompareTo(toStr) <= 0) .ToListAsync(ct); return entities.Select(Mapping.ToDomain).ToList().AsReadOnly(); } public async Task> ListByEventAsync(EventId eventId, CancellationToken ct = default) { var entities = await _db.PlacedBets.AsNoTracking() .Where(b => b.EventCode == eventId.Value) .ToListAsync(ct); return entities.Select(Mapping.ToDomain).ToList().AsReadOnly(); } public async Task AddAsync(PlacedBet entity, CancellationToken ct = default) { var efEntity = Mapping.ToEntity(entity); await _db.PlacedBets.AddAsync(efEntity, ct); } public Task UpdateAsync(PlacedBet entity, CancellationToken ct = default) { var efEntity = Mapping.ToEntity(entity); _db.PlacedBets.Update(efEntity); return Task.CompletedTask; } public async Task DeleteAsync(Guid key, CancellationToken ct = default) { var idStr = key.ToString(); var entity = await _db.PlacedBets.FirstOrDefaultAsync(b => b.Id == idStr, ct); if (entity is not null) _db.PlacedBets.Remove(entity); } public async Task SaveChangesAsync(CancellationToken ct = default) => await _db.SaveChangesAsync(ct); }