using LightlessSync.API.Data; using LightlessSync.API.Data.Enum; using LightlessSync.API.Data.Extensions; using LightlessSync.API.Dto.Group; namespace LightlessSync.PlayerData.Pairs; /// /// core models for the pair system /// public sealed class PairState { public CharacterData? CharacterData { get; set; } public Guid? TemporaryCollectionId { get; set; } public bool IsEmpty => CharacterData is null && (TemporaryCollectionId is null || TemporaryCollectionId == Guid.Empty); } public readonly record struct PairUniqueIdentifier(string UserId); /// /// link between a pair id and character ident /// public sealed record PairRegistration(PairUniqueIdentifier PairIdent, string? CharacterIdent); /// /// per group membership info for a pair /// public sealed class GroupPairRelationship { public GroupPairRelationship(string groupId, GroupPairUserInfo? info) { GroupId = groupId; UserInfo = info; } public string GroupId { get; } public GroupPairUserInfo? UserInfo { get; private set; } public void SetUserInfo(GroupPairUserInfo? info) { UserInfo = info; } } /// /// runtime view of a single pair connection /// public sealed class PairConnection { public PairConnection(UserData user) { User = user; Groups = new Dictionary(StringComparer.Ordinal); } public UserData User { get; } public bool IsOnline { get; private set; } public string? Ident { get; private set; } public UserPermissions SelfToOtherPermissions { get; private set; } = UserPermissions.NoneSet; public UserPermissions OtherToSelfPermissions { get; private set; } = UserPermissions.NoneSet; public IndividualPairStatus? IndividualPairStatus { get; private set; } public Dictionary Groups { get; } public bool IsPaused => SelfToOtherPermissions.IsPaused(); public bool IsDirectlyPaired => IndividualPairStatus is not null && IndividualPairStatus != API.Data.Enum.IndividualPairStatus.None; public bool IsOneSided => IndividualPairStatus == API.Data.Enum.IndividualPairStatus.OneSided; public bool HasAnyConnection => IsDirectlyPaired || Groups.Count > 0; public void SetOnline(string? ident) { IsOnline = true; Ident = ident; } public void SetOffline() { IsOnline = false; } public void UpdatePermissions(UserPermissions own, UserPermissions other) { SelfToOtherPermissions = own; OtherToSelfPermissions = other; } public void UpdateStatus(IndividualPairStatus? status) { IndividualPairStatus = status; } public void EnsureGroupRelationship(string groupId, GroupPairUserInfo? info) { if (Groups.TryGetValue(groupId, out var relationship)) { relationship.SetUserInfo(info); } else { Groups[groupId] = new GroupPairRelationship(groupId, info); } } public void RemoveGroupRelationship(string groupId) { Groups.Remove(groupId); } } /// /// syncshell metadata plus member connections /// public sealed class Syncshell { public Syncshell(GroupFullInfoDto dto) { GroupFullInfo = dto; Users = new Dictionary(StringComparer.Ordinal); } public GroupFullInfoDto GroupFullInfo { get; private set; } public Dictionary Users { get; } public void Update(GroupFullInfoDto dto) { GroupFullInfo = dto; } } /// /// simple success/failure result /// public readonly struct PairOperationResult { private PairOperationResult(bool success, string? error) { Success = success; Error = error; } public bool Success { get; } public string? Error { get; } public static PairOperationResult Ok() => new(true, null); public static PairOperationResult Fail(string error) => new(false, error); } /// /// typed success/failure result /// public readonly struct PairOperationResult { private PairOperationResult(bool success, T value, string? error) { Success = success; Value = value; Error = error; } public bool Success { get; } public T Value { get; } public string? Error { get; } public static PairOperationResult Ok(T value) => new(true, value, null); public static PairOperationResult Fail(string error) => new(false, default!, error); } /// /// state of which optional plugin warnings were shown /// public record OptionalPluginWarning { public bool ShownHeelsWarning { get; set; } = false; public bool ShownCustomizePlusWarning { get; set; } = false; public bool ShownHonorificWarning { get; set; } = false; public bool ShownMoodlesWarning { get; set; } = false; public bool ShowPetNicknamesWarning { get; set; } = false; } /// /// tracks the handler registered pairs for an ident /// internal sealed class PairHandlerEntry { private readonly HashSet _pairs = new(); public PairHandlerEntry(string ident, IPairHandlerAdapter handler) { Ident = ident; Handler = handler; } public string Ident { get; } public IPairHandlerAdapter Handler { get; } public bool HasPairs => _pairs.Count > 0; public int PairCount => _pairs.Count; public void AddPair(PairUniqueIdentifier pair) { _pairs.Add(pair); } public bool RemovePair(PairUniqueIdentifier pair) { return _pairs.Remove(pair); } public IReadOnlyCollection SnapshotPairs() { if (_pairs.Count == 0) { return Array.Empty(); } return _pairs.ToArray(); } }