using System.Collections.Concurrent; using Luna.Identity.Abstractions; namespace Luna.Identity; public class UserIdentityService(IPairingCodeGenerator pairingCodeGenerator) : IUserIdentityService { // Todo: User creation private readonly UserIdentity currentUser = new( UserId: Guid.NewGuid().ToString(), UserName: "Erik"); private readonly ConcurrentDictionary> pairingCodes = new(); private readonly ConcurrentDictionary> linkedChannels = new(); public UserIdentity GetCurrentUser() => currentUser; public string StartChannelLink(string userId, string channelType) { if (!pairingCodes.ContainsKey(userId)) pairingCodes[userId] = new List(); var existingCode = pairingCodes[userId] .SingleOrDefault(c => c.ChannelType == channelType); switch (existingCode) { case {} when !IsPairingCodeExpired(existingCode): return existingCode.Code; case {} when IsPairingCodeExpired(existingCode): pairingCodes[userId].Remove(existingCode); return CreateNewPairingCode(userId, channelType); default: return CreateNewPairingCode(userId, channelType); } } public void CompleteChannelLink(string userId, string channelType, string code) { if (!linkedChannels.ContainsKey(userId)) linkedChannels[userId] = new List(); if (linkedChannels[userId].Any(channel => channel.ChannelType == channelType && channel.UserId == userId)) throw new InvalidOperationException($"Channel {channelType} already linked to user with id {userId}"); if (!pairingCodes.TryGetValue(userId, out var userCodes)) throw new InvalidOperationException($"No active pairing codes for user with id {userId}"); var userCode = userCodes.SingleOrDefault(c => c.ChannelType == channelType && c.Code == code); if (userCode is null) throw new InvalidOperationException($"Pairing code {code} did not match any active codes for channel {channelType}"); if (IsPairingCodeExpired(userCode)) throw new InvalidOperationException($"Pairing code {code} is already expired"); linkedChannels[userId].Add(new ChannelLink( ChannelType: channelType, ChannelUserId: userId, UserId: userId, LinkedAt: DateTimeOffset.UtcNow)); } public bool IsChannelLinked(string userId, string channelType) => linkedChannels.TryGetValue(userId, out var channelLinks) && channelLinks.Any(link => link.ChannelType == channelType && link.UserId == userId); private bool IsPairingCodeExpired(PairingCode code) => code.CreatedAt + code.TimeToLive < DateTimeOffset.UtcNow; private string CreateNewPairingCode(string userId, string channelType) { var code = pairingCodeGenerator.Generate(channelType, userId); pairingCodes[userId].Add(code); return code.Code; } }