using AyCode.Core.Extensions;
using MessagePack;
using MessagePack.Resolvers;

namespace AyCode.Core.Tests.TestModels;

/// <summary>
/// Common SignalR test/benchmark infrastructure.
/// Provides message creation and serialization helpers used by both tests and benchmarks.
/// </summary>
public static class SignalRMessageFactory
{
    /// <summary>
    /// Cached MessagePack options for ContractlessStandardResolver
    /// </summary>
    public static readonly MessagePackSerializerOptions ContractlessOptions = ContractlessStandardResolver.Options;

    /// <summary>
    /// Creates a MessagePack message for multiple parameters using IdMessage format.
    /// Each parameter is serialized directly as JSON.
    /// </summary>
    public static byte[] CreateIdMessage(params object[] values)
    {
        var idMessage = new SignalRIdMessageDto(values);
        var postMessage = new SignalRPostMessageDto { PostDataJson = idMessage.ToJson() };
        return MessagePackSerializer.Serialize(postMessage, ContractlessOptions);
    }

    /// <summary>
    /// Creates a MessagePack message for a single primitive parameter.
    /// </summary>
    public static byte[] CreateSingleParamMessage<T>(T value) where T : notnull
    {
        return CreateIdMessage(value);
    }

    /// <summary>
    /// Creates a MessagePack message for a complex object parameter.
    /// Uses PostDataJson pattern for single complex objects.
    /// </summary>
    public static byte[] CreateComplexObjectMessage<T>(T obj)
    {
        var json = obj.ToJson();
        var postMessage = new SignalRPostMessageDto { PostDataJson = json };
        return MessagePackSerializer.Serialize(postMessage, ContractlessOptions);
    }

    /// <summary>
    /// Creates an empty MessagePack message for parameterless methods.
    /// </summary>
    public static byte[] CreateEmptyMessage()
    {
        var postMessage = new SignalRPostMessageDto();
        return MessagePackSerializer.Serialize(postMessage, ContractlessOptions);
    }

    /// <summary>
    /// Creates a response message in MessagePack format.
    /// </summary>
    public static byte[] CreateResponseMessage(int messageTag, byte status, string? responseDataJson)
    {
        var response = new SignalRResponseDto
        {
            MessageTag = messageTag,
            Status = status,
            ResponseData = responseDataJson
        };
        return MessagePackSerializer.Serialize(response, ContractlessOptions);
    }

    /// <summary>
    /// Creates a success response message in MessagePack format.
    /// </summary>
    public static byte[] CreateSuccessResponse<T>(int messageTag, T data)
    {
        return CreateResponseMessage(messageTag, 5, data.ToJson()); // 5 = Success
    }

    /// <summary>
    /// Creates an error response message in MessagePack format.
    /// </summary>
    public static byte[] CreateErrorResponse(int messageTag)
    {
        return CreateResponseMessage(messageTag, 0, null); // 0 = Error
    }

    /// <summary>
    /// Deserializes a MessagePack message to IdMessage DTO.
    /// </summary>
    public static SignalRIdMessageDto? DeserializeToIdMessage(byte[] messageBytes)
    {
        if (messageBytes == null || messageBytes.Length == 0) return null;
        
        try
        {
            var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(messageBytes, ContractlessOptions);
            return postMessage.PostDataJson?.JsonTo<SignalRIdMessageDto>();
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    /// Deserializes a MessagePack response message.
    /// </summary>
    public static SignalRResponseDto? DeserializeResponse(byte[] messageBytes)
    {
        if (messageBytes == null || messageBytes.Length == 0) return null;
        
        try
        {
            return MessagePackSerializer.Deserialize<SignalRResponseDto>(messageBytes, ContractlessOptions);
        }
        catch
        {
            return null;
        }
    }
}

/// <summary>
/// Lightweight DTO for IdMessage serialization/deserialization in tests and benchmarks.
/// Mirrors the structure of IdMessage without dependencies on AyCode.Services.
/// </summary>
public class SignalRIdMessageDto
{
    public List<string> Ids { get; set; } = [];

    public SignalRIdMessageDto()
    {
    }

    public SignalRIdMessageDto(object[] ids)
    {
        Ids.AddRange(ids.Select(x => x.ToJson()));
    }

    public SignalRIdMessageDto(object id)
    {
        Ids.Add(id.ToJson());
    }
}

/// <summary>
/// Lightweight DTO for SignalR post message serialization.
/// Mirrors SignalPostJsonMessage structure.
/// </summary>
[MessagePackObject]
public class SignalRPostMessageDto
{
    [Key(0)]
    public string? PostDataJson { get; set; }
}

/// <summary>
/// Lightweight DTO for SignalR response message serialization.
/// Mirrors SignalResponseJsonMessage structure.
/// </summary>
[MessagePackObject]
public class SignalRResponseDto
{
    [Key(0)]
    public int MessageTag { get; set; }

    [Key(1)]
    public byte Status { get; set; }

    [Key(2)]
    public string? ResponseData { get; set; }

    [IgnoreMember]
    public bool IsSuccess => Status == 5;
    
    [IgnoreMember]
    public bool IsError => Status == 0;
}

/// <summary>
/// Common SignalR message tags for testing.
/// These mirror the production tags but are defined here for test/benchmark independence.
/// </summary>
public static class CommonSignalRTags
{
    // Primitive parameter tags
    public const int SingleIntParam = 100;
    public const int TwoIntParams = 101;
    public const int BoolParam = 102;
    public const int StringParam = 103;
    public const int GuidParam = 104;
    public const int EnumParam = 105;
    public const int NoParams = 107;
    public const int MultipleTypesParams = 109;
    
    // Extended primitives
    public const int DecimalParam = 140;
    public const int DateTimeParam = 141;
    public const int DoubleParam = 143;
    public const int LongParam = 144;
    
    // Complex object tags
    public const int TestOrderItemParam = 120;
    public const int TestOrderParam = 121;
    public const int SharedTagParam = 122;
    
    // Collection tags
    public const int IntArrayParam = 130;
    public const int GuidArrayParam = 131;
    public const int StringListParam = 132;
    public const int TestOrderItemListParam = 133;
    public const int IntListParam = 134;
    public const int BoolArrayParam = 135;
    public const int MixedWithArrayParam = 136;
    
    // Mixed parameter scenarios
    public const int IntAndDtoParam = 160;
    public const int DtoAndListParam = 161;
    public const int ThreeComplexParams = 162;
    public const int FiveParams = 164;
}

/// <summary>
/// Pre-built test messages for benchmarking.
/// Caches serialized messages to avoid setup overhead in benchmark iterations.
/// </summary>
public class SignalRBenchmarkData
{
    // Pre-serialized messages
    public byte[] SingleIntMessage { get; }
    public byte[] TwoIntMessage { get; }
    public byte[] FiveParamsMessage { get; }
    public byte[] ComplexOrderItemMessage { get; }
    public byte[] ComplexOrderMessage { get; }
    public byte[] IntArrayMessage { get; }
    public byte[] MixedParamsMessage { get; }
    
    // Test data
    public TestOrderItem_All_True TestOrderItem { get; }
    public TestOrder_All_True TestOrder { get; }
    public int[] IntArray { get; }
    public Guid TestGuid { get; }

    public SignalRBenchmarkData()
    {
        // Create test data
        TestGuid = Guid.NewGuid();
        IntArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        TestOrderItem = new TestOrderItem_All_True 
        { 
            Id = 42, 
            ProductName = "Benchmark Product", 
            Quantity = 100,
            UnitPrice = 99.99m,
            Status = TestStatus.Active
        };
        TestOrder = TestDataFactory.CreateOrder(itemCount: 3, palletsPerItem: 2, measurementsPerPallet: 2);

        // Pre-serialize messages
        SingleIntMessage = SignalRMessageFactory.CreateSingleParamMessage(42);
        TwoIntMessage = SignalRMessageFactory.CreateIdMessage(10, 20);
        FiveParamsMessage = SignalRMessageFactory.CreateIdMessage(42, "hello", true, TestGuid, 99.99m);
        ComplexOrderItemMessage = SignalRMessageFactory.CreateComplexObjectMessage(TestOrderItem);
        ComplexOrderMessage = SignalRMessageFactory.CreateComplexObjectMessage(TestOrder);
        IntArrayMessage = SignalRMessageFactory.CreateComplexObjectMessage(IntArray);
        MixedParamsMessage = SignalRMessageFactory.CreateIdMessage(true, IntArray, "hello");
    }
}
