using Mws.Application.Common; using Mws.Domain.Projects; namespace Mws.Application.Projects; public class ProjectMemberService(IUnitOfWork uow) : IProjectMemberService { public async Task> GetMembersAsync(Guid userId, Guid projectId, CancellationToken ct = default) { var isMember = await uow.Projects.IsMemberAsync(projectId, userId, ct); if (!isMember) { throw new NotFoundException("Project not found"); } var members = await uow.Projects.GetMembersWithUserAsync(projectId, ct); var permissions = await uow.Projects.GetMemberPermissionsAsync(projectId, ProjectPermissionScreens.Documents, ct); return members.Select(m => { permissions.TryGetValue(m.UserId, out var p); return new ProjectMemberDto { UserId = m.UserId, Username = m.User.Username, DisplayName = m.User.DisplayName, Role = m.Role, CanViewDocuments = m.Role == MemberRole.Owner || (p?.CanView ?? false), CanCreateDocuments = m.Role == MemberRole.Owner || (p?.CanCreate ?? false), CanEditDocuments = m.Role == MemberRole.Owner || (p?.CanEdit ?? false), CanDeleteDocuments = m.Role == MemberRole.Owner || (p?.CanDelete ?? false), }; }).ToList(); } public async Task AddMemberAsync(Guid userId, Guid projectId, AddMemberRequest request, CancellationToken ct = default) { if (await GetMemberRoleAsync(userId, projectId, ct) != MemberRole.Owner) { throw new ForbiddenException("Only the project owner can add members"); } var user = await uow.Users.GetByIdAsync(request.UserId, ct) ?? throw new NotFoundException("User not found"); var already = await uow.Projects.IsMemberAsync(projectId, request.UserId, ct); if (already) { throw new BadRequestException("User is already a member of this project"); } var role = request.Role is MemberRole.Owner or MemberRole.Member ? request.Role : MemberRole.Member; var member = new ProjectMember { ProjectId = projectId, UserId = request.UserId, Role = role, }; uow.Projects.AddMember(member); uow.Projects.AddMemberPermission(new ProjectMemberPermission { ProjectId = projectId, UserId = request.UserId, Screen = ProjectPermissionScreens.Documents, CanView = true, CanCreate = role == MemberRole.Owner, CanEdit = role == MemberRole.Owner, CanDelete = role == MemberRole.Owner, }); await uow.SaveChangesAsync(ct); return new ProjectMemberDto { UserId = user.Id, Username = user.Username, DisplayName = user.DisplayName, Role = member.Role, CanViewDocuments = true, CanCreateDocuments = role == MemberRole.Owner, CanEditDocuments = role == MemberRole.Owner, CanDeleteDocuments = role == MemberRole.Owner, }; } public async Task UpdateMemberDocumentPermissionsAsync( Guid userId, Guid projectId, Guid memberUserId, UpdateMemberDocumentPermissionsRequest request, CancellationToken ct = default) { if (await GetMemberRoleAsync(userId, projectId, ct) != MemberRole.Owner) { throw new ForbiddenException("Only the project owner can change member permissions"); } var member = await uow.Projects.GetMemberWithUserAsync(projectId, memberUserId, ct) ?? throw new NotFoundException("Member not found in project"); if (member.Role == MemberRole.Owner) { throw new BadRequestException("Owner permissions cannot be changed"); } var permission = await uow.Projects.GetMemberPermissionAsync(projectId, memberUserId, ProjectPermissionScreens.Documents, ct); if (permission is null) { permission = new ProjectMemberPermission { ProjectId = projectId, UserId = memberUserId, Screen = ProjectPermissionScreens.Documents }; uow.Projects.AddMemberPermission(permission); } permission.CanView = request.CanViewDocuments; permission.CanCreate = request.CanCreateDocuments; permission.CanEdit = request.CanEditDocuments; permission.CanDelete = request.CanDeleteDocuments; await uow.SaveChangesAsync(ct); return new ProjectMemberDto { UserId = member.UserId, Username = member.User.Username, DisplayName = member.User.DisplayName, Role = member.Role, CanViewDocuments = permission.CanView, CanCreateDocuments = permission.CanCreate, CanEditDocuments = permission.CanEdit, CanDeleteDocuments = permission.CanDelete, }; } public async Task RemoveMemberAsync(Guid userId, Guid projectId, Guid memberUserId, CancellationToken ct = default) { if (await GetMemberRoleAsync(userId, projectId, ct) != MemberRole.Owner) { throw new ForbiddenException("Only the project owner can remove members"); } var member = await uow.Projects.GetMemberAsync(projectId, memberUserId, ct) ?? throw new NotFoundException("Member not found in project"); var owners = await uow.Projects.CountOwnersAsync(projectId, ct); if (member.Role == MemberRole.Owner && owners <= 1) { throw new BadRequestException("Cannot remove the last owner of the project"); } uow.Projects.RemoveMember(member); await uow.SaveChangesAsync(ct); } private Task GetMemberRoleAsync(Guid userId, Guid projectId, CancellationToken ct = default) => uow.Projects.GetMemberRoleAsync(projectId, userId, ct); }