using AyCode.Core.Extensions; using MessagePack; using Newtonsoft.Json.Linq; using System.Text.RegularExpressions; using AyCode.Core.Interfaces; using System.Collections.Generic; using System.Linq.Expressions; namespace AyCode.Services.SignalRs; public class IdMessage { public List Ids { get; private set; } public IdMessage() { Ids = []; } /// /// Creates IdMessage with multiple parameters serialized directly as JSON. /// Each parameter is serialized independently without array wrapping. /// Use object[] explicitly to pass multiple parameters. /// public IdMessage(object[] ids) { // Pre-allocate capacity to avoid list resizing Ids = new List(ids.Length); for (var i = 0; i < ids.Length; i++) { Ids.Add(ids[i].ToJson()); } } /// /// Creates IdMessage with a single parameter serialized as JSON. /// Collections (List, Array, etc.) are serialized as a single JSON array. /// public IdMessage(object id) { // Pre-allocate for single item Ids = new List(1) { id.ToJson() }; } /// /// Creates IdMessage with multiple Guid parameters. /// Each Guid is serialized as a separate Id entry. /// public IdMessage(IEnumerable ids) { // Materialize to array once to get count and avoid multiple enumeration var idsArray = ids as Guid[] ?? ids.ToArray(); Ids = new List(idsArray.Length); for (var i = 0; i < idsArray.Length; i++) { Ids.Add(idsArray[i].ToJson()); } } public override string ToString() { return string.Join("; ", Ids); } } [MessagePackObject] public class SignalPostJsonMessage { [Key(0)] public string PostDataJson { get; set; } = ""; public SignalPostJsonMessage() {} protected SignalPostJsonMessage(string postDataJson) => PostDataJson = postDataJson; } [MessagePackObject(AllowPrivate = false)] public class SignalPostJsonDataMessage : SignalPostJsonMessage, ISignalPostMessage //where TPostDataType : class { [IgnoreMember] private TPostDataType? _postData; [IgnoreMember] public TPostDataType PostData { get { return _postData ??= PostDataJson.JsonTo()!; } private init { _postData = value; PostDataJson = _postData.ToJson(); } } public SignalPostJsonDataMessage() : base() {} public SignalPostJsonDataMessage(TPostDataType postData) => PostData = postData; public SignalPostJsonDataMessage(string postDataJson) : base(postDataJson) {} } [MessagePackObject] public class SignalPostMessage(TPostData postData) : ISignalPostMessage { [Key(0)] public TPostData? PostData { get; set; } = postData; } public interface ISignalPostMessage : ISignalRMessage { TPostData? PostData { get; } } [MessagePackObject] public class SignalRequestByIdMessage(Guid id) : ISignalRequestMessage, IId { [Key(0)] public Guid Id { get; set; } = id; } public interface ISignalRequestMessage : ISignalRMessage { TRequestId Id { get; set; } } public interface ISignalRMessage { } [MessagePackObject] public sealed class SignalResponseJsonMessage : ISignalResponseMessage { [Key(0)] public int MessageTag { get; set; } [Key(1)] public SignalResponseStatus Status { get; set; } [Key(2)] public string? ResponseData { get; set; } = null; [IgnoreMember] public string? ResponseDataJson => ResponseData; public SignalResponseJsonMessage(){} public SignalResponseJsonMessage(int messageTag, SignalResponseStatus status) { Status = status; MessageTag = messageTag; } /// /// Creates a response with the given data serialized as JSON. /// If responseData is already a JSON string (starts with { or [), it will be used directly. /// All other data types are serialized to JSON format. /// public SignalResponseJsonMessage(int messageTag, SignalResponseStatus status, object? responseData) : this(messageTag, status) { if (responseData == null) { ResponseData = null; return; } // If responseData is already a JSON string, use it directly if (responseData is string strData) { var trimmed = strData.Trim(); if (trimmed.Length > 1 && (trimmed[0] == '{' || trimmed[0] == '[') && (trimmed[^1] == '}' || trimmed[^1] == ']')) { // Already JSON - use directly without re-serialization ResponseData = strData; return; } } // Serialize to JSON ResponseData = responseData.ToJson(); } } /// /// Signal response message with lazy deserialization support. /// ResponseData is only deserialized on first access and cached. /// Use ResponseDataJson for direct JSON access without deserialization. /// [MessagePackObject(AllowPrivate = false)] public sealed class SignalResponseMessage : ISignalResponseMessage { [IgnoreMember] private TResponseData? _responseData; [IgnoreMember] private bool _isDeserialized; [Key(0)] public int MessageTag { get; set; } [Key(1)] public SignalResponseStatus Status { get; set; } /// /// Raw JSON string. Use this for direct JSON access without triggering deserialization. /// [Key(2)] public string? ResponseDataJson { get; set; } /// /// Deserialized response data. Lazy-loaded on first access. /// [IgnoreMember] public TResponseData? ResponseData { get { if (!_isDeserialized) { _responseData = ResponseDataJson != null ? ResponseDataJson.JsonTo() : default; _isDeserialized = true; } return _responseData; } set { _responseData = value; _isDeserialized = true; ResponseDataJson = value?.ToJson(); } } public SignalResponseMessage() { } public SignalResponseMessage(int messageTag, SignalResponseStatus status) { MessageTag = messageTag; Status = status; } public SignalResponseMessage(int messageTag, SignalResponseStatus status, TResponseData? responseData) : this(messageTag, status) { ResponseData = responseData; } public SignalResponseMessage(int messageTag, SignalResponseStatus status, string? responseDataJson) : this(messageTag, status) { ResponseDataJson = responseDataJson; } } public interface ISignalResponseMessage : ISignalResponseMessage { /// /// Deserialized response data. May trigger lazy deserialization. /// TResponseData? ResponseData { get; set; } /// /// Raw JSON string for direct access without deserialization. /// string? ResponseDataJson { get; } } public interface ISignalResponseMessage : ISignalRMessage { int MessageTag { get; set; } SignalResponseStatus Status { get; set; } } public enum SignalResponseStatus : byte { Error = 0, Success = 5 } public interface IAcSignalRHubClient : IAcSignalRHubBase { Task SendMessageToServerAsync(int messageTag, ISignalRMessage? message, int? requestId ); //Task SendRequestToServerAsync(int messageTag, int requestId); }