using Microsoft.EntityFrameworkCore; using Nexus.Api.Data; using System.Text.Json; namespace Nexus.Api.Repositories; public sealed class ProjectRepository(NexusDbContext db) : IProjectRepository { public Task> GetAllAsync(CancellationToken ct = default) => db.Projects.AsNoTracking().OrderByDescending(x => x.UpdatedAt).ToListAsync(ct); public ValueTask GetByIdAsync(Guid id, CancellationToken ct = default) => db.Projects.FindAsync([id], ct); public async Task AddAsync(Project project, CancellationToken ct = default) { db.Projects.Add(project); db.OutboxEvents.Add(CreateProjectEvent("project.created", project)); await db.SaveChangesAsync(ct); return project; } public async Task UpdateAsync(Project project, CancellationToken ct = default) { project.UpdatedAt = DateTimeOffset.UtcNow; db.OutboxEvents.Add(CreateProjectEvent("project.updated", project)); await db.SaveChangesAsync(ct); } public async Task DeleteAsync(Project project, CancellationToken ct = default) { db.OutboxEvents.Add(CreateProjectEvent("project.deleted", project)); db.Projects.Remove(project); await db.SaveChangesAsync(ct); } public Task HasTasksAsync(Guid projectId, CancellationToken ct = default) => db.Tasks.AnyAsync(t => t.ProjectId == projectId, ct); public Task> GetTasksAsync( Guid projectId, CancellationToken ct = default) => db.Tasks .AsNoTracking() .Where(task => task.ProjectId == projectId) .OrderBy(task => task.State == "Done" ? 1 : 0) .ThenByDescending(task => task.UpdatedAt) .ThenBy(task => task.Id) .ToListAsync(ct); private static OutboxEvent CreateProjectEvent(string type, Project project) => new() { Type = type, AggregateType = "project", AggregateId = project.Id.ToString(), AggregateRevision = 0, PayloadJson = JsonSerializer.Serialize(new { status = project.Status, progress = project.Progress }) }; }