using System.Security.Claims; using GmRelay.Shared.Domain; namespace GmRelay.Web.Services; public sealed class AuthorizedMembershipService(ISessionStore sessionStore, IHttpContextAccessor httpContextAccessor) { private (string Platform, string ExternalUserId, string Name)? GetCurrentIdentity() { var user = httpContextAccessor.HttpContext?.User; if (user is null || !user.TryGetPlatformIdentity(out var platform, out var externalUserId)) return null; var name = user.FindFirst(ClaimTypes.Name)?.Value ?? externalUserId; return (platform, externalUserId, name); } public async Task ApplyForCurrentUserAsync(Guid groupId, string? message) { var identity = GetCurrentIdentity(); if (identity is null) throw new InvalidOperationException("User is not authenticated."); var playerId = await sessionStore.GetPlayerIdByPlatformIdentityAsync(identity.Value.Platform, identity.Value.ExternalUserId); if (playerId is null) { throw new InvalidOperationException("Player record not found for current user."); } var normalizedMessage = string.IsNullOrWhiteSpace(message) ? null : message.Trim(); if (normalizedMessage?.Length > 1000) { throw new InvalidOperationException("Сообщение заявки должно быть не длиннее 1000 символов."); } return await sessionStore.ApplyForMembershipAsync(groupId, playerId.Value, normalizedMessage); } public async Task> GetMineAsync() { var identity = GetCurrentIdentity(); if (identity is null) return []; var playerId = await sessionStore.GetPlayerIdByPlatformIdentityAsync(identity.Value.Platform, identity.Value.ExternalUserId); if (playerId is null) return []; return await sessionStore.GetMembershipsForPlayerAsync(playerId.Value); } public async Task LeaveClubForCurrentUserAsync(Guid membershipId) { var identity = GetCurrentIdentity(); if (identity is null) throw new InvalidOperationException("User is not authenticated."); var playerId = await sessionStore.GetPlayerIdByPlatformIdentityAsync(identity.Value.Platform, identity.Value.ExternalUserId); if (playerId is null) throw new InvalidOperationException("Player record not found for current user."); await sessionStore.LeaveClubMembershipAsync(membershipId, playerId.Value); } public async Task> GetPendingApplicationsAsync(Guid groupId) { var identity = GetCurrentIdentity(); if (identity is null) throw new InvalidOperationException("User is not authenticated."); if (!await sessionStore.IsGroupManagerAsync(groupId, identity.Value.Platform, identity.Value.ExternalUserId)) { throw new SessionAccessDeniedException(groupId, identity.Value.ExternalUserId); } return await sessionStore.GetPendingApplicationsAsync(groupId); } public async Task GetPendingApplicationsCountForCurrentGmAsync(Guid groupId) { var identity = GetCurrentIdentity(); if (identity is null) return 0; if (!await sessionStore.IsGroupManagerAsync(groupId, identity.Value.Platform, identity.Value.ExternalUserId)) return 0; return await sessionStore.GetPendingApplicationsCountAsync(groupId); } public async Task ApproveForCurrentGmAsync(Guid membershipId) { var (approverPlayerId, groupId) = await ResolveMembershipContextForGmAsync(membershipId); await sessionStore.ApproveMembershipAsync(membershipId, approverPlayerId); } public async Task RejectForCurrentGmAsync(Guid membershipId) { var (approverPlayerId, groupId) = await ResolveMembershipContextForGmAsync(membershipId); await sessionStore.RejectMembershipAsync(membershipId, approverPlayerId); } private async Task<(Guid ApproverPlayerId, Guid GroupId)> ResolveMembershipContextForGmAsync(Guid membershipId) { var identity = GetCurrentIdentity(); if (identity is null) throw new InvalidOperationException("User is not authenticated."); var playerId = await sessionStore.GetPlayerIdByPlatformIdentityAsync(identity.Value.Platform, identity.Value.ExternalUserId); if (playerId is null) throw new InvalidOperationException("Player record not found for current user."); var groupId = await sessionStore.GetGroupIdForMembershipAsync(membershipId); if (groupId is null) throw new InvalidOperationException($"Membership {membershipId} not found."); if (!await sessionStore.IsGroupManagerAsync(groupId.Value, identity.Value.Platform, identity.Value.ExternalUserId)) { throw new SessionAccessDeniedException(groupId.Value, identity.Value.ExternalUserId); } return (playerId.Value, groupId.Value); } }