using GmRelay.Shared.Domain; using GmRelay.Shared.Features.Showcase; namespace GmRelay.Web.Services; public sealed record PlayerAttendanceStats( Guid PlayerId, string DisplayName, string? ExternalUsername, long TotalSessions, long ConfirmedCount, long DeclinedCount, long NoResponseCount, long WaitlistedCount, long CancellationAffectedCount, decimal AttendanceRate); public sealed record SessionAuditLogEntry( Guid Id, Guid SessionId, string ActorExternalUserId, string ActorName, string ChangeType, string? OldValue, string? NewValue, DateTime ChangedAt); public sealed record WebPublicGroupSettings( Guid GroupId, string GroupName, string? PublicSlug, bool PublicScheduleEnabled, int PublicSessionCount); public sealed record WebPublicSession( Guid Id, Guid GroupId, string GroupName, string? GroupSlug, string Title, DateTime ScheduledAt, string Status, int? MaxPlayers, int ActivePlayerCount, int WaitlistedPlayerCount, string PublicationMode = PublicationModeExtensions.NoneValue, bool IsMembersOnly = false, string? MasterProfileSlug = null, string? MasterDisplayName = null); public sealed record WebMembership( Guid MembershipId, Guid GroupId, string GroupName, string? GroupSlug, string Status, string Role, string? Message, DateTime AppliedAt, DateTime? DecidedAt, string? DecidedByDisplayName); public sealed record WebPendingApplication( Guid MembershipId, Guid PlayerId, string DisplayName, string Platform, string? ExternalUsername, string? Message, DateTime AppliedAt); public sealed record WebClubShowcaseSession( Guid Id, string Title, DateTime ScheduledAt, string Status, string? System, bool IsOneShot, string? Format, int? DurationMinutes, string? CoverImageUrl, int? MaxPlayers, int ActivePlayerCount, int WaitlistedPlayerCount, string PublicationMode, bool IsMembersOnly, string? Description, bool AllowDirectRegistration); public sealed record WebPublicClub( Guid GroupId, string Name, string Slug, IReadOnlyList Sessions, string? MasterProfileSlug = null, string? MasterDisplayName = null); public sealed record MasterProfileSettings( Guid PlayerId, string DisplayName, string? PublicSlug, bool IsPublic, string? Bio); public sealed record PublicMasterClub( Guid GroupId, string Name, string Slug); public sealed record PublicMasterProfile( string Slug, string DisplayName, string? Bio, IReadOnlyList Clubs, IReadOnlyList Sessions); public interface ISessionStore { Task> GetGroupsForUserAsync(string platform, string externalUserId); Task GetGroupAsync(Guid groupId); Task GetPublicGroupSettingsAsync(Guid groupId); Task UpdatePublicGroupSettingsAsync(Guid groupId, string? publicSlug, bool publicScheduleEnabled); Task SetSessionPublicationModeAsync(Guid sessionId, Guid groupId, PublicationMode mode); Task SetBatchPublicationModeAsync(Guid batchId, Guid groupId, PublicationMode mode); Task GetPublicClubBySlugAsync(string slug, Guid? viewerPlayerId); Task GetPublicSessionAsync(Guid sessionId, Guid? viewerPlayerId); Task IsGroupManagerAsync(Guid groupId, string platform, string externalUserId); Task IsGroupOwnerAsync(Guid groupId, string platform, string externalUserId); Task IsActiveClubMemberAsync(Guid groupId, Guid playerId); Task GetPlayerIdByPlatformIdentityAsync(string platform, string externalUserId); Task> GetGroupManagersAsync(Guid groupId); Task> GetUpcomingSessionsAsync(Guid groupId); Task GetSessionAsync(Guid sessionId); Task GetBatchAsync(Guid batchId); Task UpdateSessionAsync(Guid sessionId, Guid groupId, string title, DateTime scheduledAt, string joinLink, int? maxPlayers); Task PromoteWaitlistedPlayerAsync(Guid sessionId, Guid groupId); Task UpdateBatchDetailsAsync(Guid batchId, Guid groupId, string title, string joinLink); Task UpdateBatchNotificationModeAsync(Guid batchId, Guid groupId, SessionNotificationMode notificationMode); Task RescheduleBatchAsync(Guid batchId, Guid groupId, DateTime firstScheduledAt, int intervalDays); Task CloneBatchAsync(Guid batchId, Guid groupId, BatchCloneInterval interval); Task> GetCampaignTemplatesAsync(Guid groupId); Task GetCampaignTemplateAsync(Guid templateId); Task CreateCampaignTemplateAsync(Guid groupId, CreateCampaignTemplateRequest request); Task DeleteCampaignTemplateAsync(Guid templateId, Guid groupId); Task CreateBatchFromTemplateAsync(Guid templateId, Guid groupId, DateTime firstScheduledAt); Task AddGroupCoGmAsync(Guid groupId, string ownerPlatform, string ownerExternalUserId, string coGmPlatform, string coGmExternalUserId, string displayName, string? externalUsername); Task RemoveGroupCoGmAsync(Guid groupId, string coGmPlatform, string coGmExternalUserId); Task> GetSessionParticipantsAsync(Guid sessionId); Task RemovePlayerFromSessionAsync(Guid sessionId, Guid groupId, Guid participantId); Task> GetGroupAttendanceStatsAsync(Guid groupId); Task LogSessionChangeAsync(Guid sessionId, string actorExternalUserId, string actorName, string changeType, string? oldValue, string? newValue); Task> GetSessionHistoryAsync(Guid sessionId); Task UpsertDiscordUserAsync(string discordId, string displayName, string? avatarUrl); Task GetMasterProfileSettingsAsync(string platform, string externalUserId); Task UpdateMasterProfileSettingsAsync(string platform, string externalUserId, string? publicSlug, bool isPublic, string displayName, string? bio); Task GetPublicMasterProfileBySlugAsync(string slug, Guid? viewerPlayerId); // --- Identity linking (issue #35) --- Task ResolveEffectivePlayerIdAsync(string platform, string externalUserId); Task> GetLinkedIdentitiesAsync(string platform, string externalUserId); Task LinkIdentityAsync(string currentPlatform, string currentExternalUserId, string targetPlatform, string targetExternalUserId, string? currentName); Task UnlinkIdentityAsync(string currentPlatform, string currentExternalUserId, string targetPlatform, string targetExternalUserId); Task UpsertPlayerAsync(string platform, string externalUserId, string displayName, string? avatarUrl); // --- Showcase / game catalog (issue #39) --- Task> GetShowcaseSessionsAsync(ShowcaseFilter filter, int page, int pageSize); Task GetShowcaseSessionAsync(Guid sessionId); Task RegisterFromShowcaseAsync(Guid sessionId, string platform, string externalUserId, string displayName); // --- Private club showcases / memberships (issue #110) --- Task> GetClubShowcaseSessionsAsync(Guid groupId, Guid? viewerPlayerId, int page, int pageSize); Task GetPendingApplicationsCountAsync(Guid groupId); Task> GetPendingApplicationsAsync(Guid groupId); Task> GetMembershipsForPlayerAsync(Guid playerId); Task ApplyForMembershipAsync(Guid groupId, Guid playerId, string? message); Task ApproveMembershipAsync(Guid membershipId, Guid approverPlayerId); Task RejectMembershipAsync(Guid membershipId, Guid approverPlayerId); Task LeaveClubMembershipAsync(Guid membershipId, Guid playerId); Task GetGroupIdForMembershipAsync(Guid membershipId); } public sealed record LinkedIdentity( string Platform, string ExternalUserId, string DisplayName, string? ExternalUsername, string? AvatarUrl, DateTime LinkedAt);