using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Dto; using SeaHaven.DataServices.Interfaces; namespace SeaHaven.DataServices.Implementation; public sealed class TeamPermissionOverrideDataService : ITeamPermissionOverrideDataService { private readonly ApplicationDbContext _context; public TeamPermissionOverrideDataService(ApplicationDbContext context) { _context = context; } public async Task GetUserAsync( string userId, CancellationToken cancellationToken) { var userExists = await _context.Users .AsNoTracking() .AnyAsync(user => user.Id == userId, cancellationToken); if (!userExists) return null; var roleName = await ( from userRole in _context.UserRoles.AsNoTracking() join role in _context.Roles.AsNoTracking() on userRole.RoleId equals role.Id where userRole.UserId == userId select role.Name) .FirstOrDefaultAsync(cancellationToken); var overrides = await _context.UserPermissionOverrides .AsNoTracking() .Where(permission => permission.UserId == userId) .ToDictionaryAsync( permission => permission.PermissionKey, permission => permission.State, StringComparer.OrdinalIgnoreCase, cancellationToken); return new TeamPermissionUserData { UserId = userId, RoleName = roleName, Overrides = overrides }; } public Task SetOverrideAsync( string userId, string permissionKey, UserPermissionState state, CancellationToken cancellationToken) { return SetOverrideCoreAsync(userId, permissionKey, state, cancellationToken); } public async Task SetOverridesAsync( string userId, IReadOnlyDictionary overrides, CancellationToken cancellationToken) { foreach (var pair in overrides) await SetOverrideCoreAsync(userId, pair.Key, pair.Value, cancellationToken); } private async Task SetOverrideCoreAsync( string userId, string permissionKey, UserPermissionState state, CancellationToken cancellationToken) { var existing = await _context.UserPermissionOverrides .SingleOrDefaultAsync( permission => permission.UserId == userId && permission.PermissionKey == permissionKey, cancellationToken); if (existing is not null) { existing.State = state; await _context.SaveChangesAsync(cancellationToken); return; } var addition = new UserPermissionOverride { UserId = userId, PermissionKey = permissionKey, State = state }; await _context.UserPermissionOverrides.AddAsync(addition, cancellationToken); try { await _context.SaveChangesAsync(cancellationToken); } catch (DbUpdateException) { // A concurrent write (create or PUT) inserted the same composite key // between our existence check and this save. Converge on the caller's intent. _context.Entry(addition).State = EntityState.Detached; var winner = await _context.UserPermissionOverrides .SingleAsync( permission => permission.UserId == userId && permission.PermissionKey == permissionKey, cancellationToken); winner.State = state; await _context.SaveChangesAsync(cancellationToken); } } public async Task ClearOverridesAsync(string userId, CancellationToken cancellationToken) { await _context.UserPermissionOverrides .Where(item => item.UserId == userId) .ExecuteDeleteAsync(cancellationToken); } }