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? MasterProfileSlug = null, string? MasterDisplayName = null); 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 SetSessionPublicAsync(Guid sessionId, Guid groupId, bool isPublic); Task SetBatchPublicAsync(Guid batchId, Guid groupId, bool isPublic); Task GetPublicClubBySlugAsync(string slug); Task GetPublicSessionAsync(Guid sessionId); Task IsGroupManagerAsync(Guid groupId, string platform, string externalUserId); Task IsGroupOwnerAsync(Guid groupId, 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); // --- 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); } public sealed record LinkedIdentity( string Platform, string ExternalUserId, string DisplayName, string? ExternalUsername, string? AvatarUrl, DateTime LinkedAt);