using AyCode.Core.Extensions;
using AyCode.Core.Serializers.Jsons;
using AyCode.Core.Tests.TestModels;
using BenchmarkDotNet.Attributes;
using MessagePack;

namespace AyCode.Core.Benchmarks;

/// <summary>
/// SignalR communication benchmarks measuring the full serialization workflow:
/// Client ? IdMessage ? MessagePack ? Server ? Deserialize ? Response ? MessagePack ? Client
/// </summary>
[MemoryDiagnoser]
public class SignalRCommunicationBenchmarks
{
    // Shared test data
    private SignalRBenchmarkData _data = null !;
    // Pre-serialized messages for deserialization benchmarks
    private byte[] _singleIntMessage = null !;
    private byte[] _twoIntMessage = null !;
    private byte[] _fiveParamsMessage = null !;
    private byte[] _complexOrderItemMessage = null !;
    private byte[] _complexOrderMessage = null !;
    private byte[] _intArrayMessage = null !;
    private byte[] _mixedParamsMessage = null !;
    // Pre-serialized response for client-side deserialization
    private byte[] _successResponseMessage = null !;
    private byte[] _complexResponseMessage = null !;
    [GlobalSetup]
    public void Setup()
    {
        _data = new SignalRBenchmarkData();
        // Copy pre-serialized messages
        _singleIntMessage = _data.SingleIntMessage;
        _twoIntMessage = _data.TwoIntMessage;
        _fiveParamsMessage = _data.FiveParamsMessage;
        _complexOrderItemMessage = _data.ComplexOrderItemMessage;
        _complexOrderMessage = _data.ComplexOrderMessage;
        _intArrayMessage = _data.IntArrayMessage;
        _mixedParamsMessage = _data.MixedParamsMessage;
        // Pre-serialize response messages
        _successResponseMessage = SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.SingleIntParam, "Received: 42");
        _complexResponseMessage = SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.TestOrderParam, _data.TestOrder);
        Console.WriteLine("=== SignalR Message Size Comparison ===");
        Console.WriteLine($"Single int message: {_singleIntMessage.Length} bytes");
        Console.WriteLine($"Two int message: {_twoIntMessage.Length} bytes");
        Console.WriteLine($"Five params message: {_fiveParamsMessage.Length} bytes");
        Console.WriteLine($"Complex OrderItem message: {_complexOrderItemMessage.Length} bytes");
        Console.WriteLine($"Complex Order message: {_complexOrderMessage.Length} bytes");
        Console.WriteLine($"Int array message: {_intArrayMessage.Length} bytes");
        Console.WriteLine($"Mixed params message: {_mixedParamsMessage.Length} bytes");
        Console.WriteLine($"Success response: {_successResponseMessage.Length} bytes");
        Console.WriteLine($"Complex response: {_complexResponseMessage.Length} bytes");
    }

#region Client-Side: Message Creation (IdMessage + MessagePack Serialization)
    [Benchmark(Description = "Client: Create single int message")]
    [BenchmarkCategory("Client", "Create")]
    public byte[] Client_CreateSingleIntMessage() => SignalRMessageFactory.CreateSingleParamMessage(42);
    [Benchmark(Description = "Client: Create two int message")]
    [BenchmarkCategory("Client", "Create")]
    public byte[] Client_CreateTwoIntMessage() => SignalRMessageFactory.CreateIdMessage(10, 20);
    [Benchmark(Description = "Client: Create five params message")]
    [BenchmarkCategory("Client", "Create")]
    public byte[] Client_CreateFiveParamsMessage() => SignalRMessageFactory.CreateIdMessage(42, "hello", true, _data.TestGuid, 99.99m);
    [Benchmark(Description = "Client: Create complex OrderItem message")]
    [BenchmarkCategory("Client", "Create")]
    public byte[] Client_CreateComplexOrderItemMessage() => SignalRMessageFactory.CreateComplexObjectMessage(_data.TestOrderItem);
    [Benchmark(Description = "Client: Create complex Order message")]
    [BenchmarkCategory("Client", "Create")]
    public byte[] Client_CreateComplexOrderMessage() => SignalRMessageFactory.CreateComplexObjectMessage(_data.TestOrder);
#endregion
#region Server-Side: Message Deserialization (MessagePack + JSON)
    [Benchmark(Description = "Server: Deserialize single int")]
    [BenchmarkCategory("Server", "Deserialize")]
    public int Server_DeserializeSingleInt()
    {
        var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_singleIntMessage, SignalRMessageFactory.ContractlessOptions);
        var idMessage = postMessage.PostDataJson!.JsonTo<SignalRIdMessageDto>()!;
        return AcJsonDeserializer.Deserialize<int>(idMessage.Ids[0]);
    }

    [Benchmark(Description = "Server: Deserialize two ints")]
    [BenchmarkCategory("Server", "Deserialize")]
    public (int, int) Server_DeserializeTwoInts()
    {
        var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_twoIntMessage, SignalRMessageFactory.ContractlessOptions);
        var idMessage = postMessage.PostDataJson!.JsonTo<SignalRIdMessageDto>()!;
        var a = AcJsonDeserializer.Deserialize<int>(idMessage.Ids[0]);
        var b = AcJsonDeserializer.Deserialize<int>(idMessage.Ids[1]);
        return (a, b);
    }

    [Benchmark(Description = "Server: Deserialize five params")]
    [BenchmarkCategory("Server", "Deserialize")]
    public (int, string, bool, Guid, decimal) Server_DeserializeFiveParams()
    {
        var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_fiveParamsMessage, SignalRMessageFactory.ContractlessOptions);
        var idMessage = postMessage.PostDataJson!.JsonTo<SignalRIdMessageDto>()!;
        var a = AcJsonDeserializer.Deserialize<int>(idMessage.Ids[0]);
        var b = AcJsonDeserializer.Deserialize<string>(idMessage.Ids[1])!;
        var c = AcJsonDeserializer.Deserialize<bool>(idMessage.Ids[2]);
        var d = AcJsonDeserializer.Deserialize<Guid>(idMessage.Ids[3]);
        var e = AcJsonDeserializer.Deserialize<decimal>(idMessage.Ids[4]);
        return (a, b, c, d, e);
    }

    [Benchmark(Description = "Server: Deserialize complex OrderItem")]
    [BenchmarkCategory("Server", "Deserialize")]
    public TestOrderItem_All_True Server_DeserializeComplexOrderItem()
    {
        var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_complexOrderItemMessage, SignalRMessageFactory.ContractlessOptions);
        return postMessage.PostDataJson!.JsonTo<TestOrderItem_All_True>()!;
    }

    [Benchmark(Description = "Server: Deserialize complex Order")]
    [BenchmarkCategory("Server", "Deserialize")]
    public TestOrder_All_True Server_DeserializeComplexOrder()
    {
        var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(_complexOrderMessage, SignalRMessageFactory.ContractlessOptions);
        return postMessage.PostDataJson!.JsonTo<TestOrder_All_True>()!;
    }

#endregion
#region Server-Side: Response Creation (JSON + MessagePack Serialization)
    [Benchmark(Description = "Server: Create success response (string)")]
    [BenchmarkCategory("Server", "Response")]
    public byte[] Server_CreateSuccessStringResponse() => SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.SingleIntParam, "Received: 42");
    [Benchmark(Description = "Server: Create success response (OrderItem)")]
    [BenchmarkCategory("Server", "Response")]
    public byte[] Server_CreateSuccessOrderItemResponse() => SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.TestOrderItemParam, _data.TestOrderItem);
    [Benchmark(Description = "Server: Create success response (Order)")]
    [BenchmarkCategory("Server", "Response")]
    public byte[] Server_CreateSuccessOrderResponse() => SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.TestOrderParam, _data.TestOrder);
#endregion
#region Client-Side: Response Deserialization
    [Benchmark(Description = "Client: Deserialize string response")]
    [BenchmarkCategory("Client", "Response")]
    public string? Client_DeserializeStringResponse()
    {
        var response = SignalRMessageFactory.DeserializeResponse(_successResponseMessage);
        return response?.ResponseData;
    }

    [Benchmark(Description = "Client: Deserialize complex Order response")]
    [BenchmarkCategory("Client", "Response")]
    public TestOrder_All_True? Client_DeserializeOrderResponse()
    {
        var response = SignalRMessageFactory.DeserializeResponse(_complexResponseMessage);
        return response?.ResponseData?.JsonTo<TestOrder_All_True>();
    }

#endregion
#region Full Round-Trip Benchmarks
    [Benchmark(Description = "Full: Single int round-trip")]
    [BenchmarkCategory("Full")]
    public string? Full_SingleIntRoundTrip()
    {
        // Client creates message
        var requestBytes = SignalRMessageFactory.CreateSingleParamMessage(42);
        // Server deserializes
        var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(requestBytes, SignalRMessageFactory.ContractlessOptions);
        var idMessage = postMessage.PostDataJson!.JsonTo<SignalRIdMessageDto>()!;
        var value = AcJsonDeserializer.Deserialize<int>(idMessage.Ids[0]);
        // Server creates response
        var responseBytes = SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.SingleIntParam, $"Received: {value}");
        // Client deserializes response
        var response = SignalRMessageFactory.DeserializeResponse(responseBytes);
        return response?.ResponseData;
    }

    [Benchmark(Description = "Full: Complex Order round-trip")]
    [BenchmarkCategory("Full")]
    public TestOrder_All_True? Full_ComplexOrderRoundTrip()
    {
        // Client creates message
        var requestBytes = SignalRMessageFactory.CreateComplexObjectMessage(_data.TestOrder);
        // Server deserializes
        var postMessage = MessagePackSerializer.Deserialize<SignalRPostMessageDto>(requestBytes, SignalRMessageFactory.ContractlessOptions);
        var order = postMessage.PostDataJson!.JsonTo<TestOrder_All_True>()!;
        // Server modifies and creates response
        order.OrderNumber = "PROCESSED-" + order.OrderNumber;
        var responseBytes = SignalRMessageFactory.CreateSuccessResponse(CommonSignalRTags.TestOrderParam, order);
        // Client deserializes response
        var response = SignalRMessageFactory.DeserializeResponse(responseBytes);
        return response?.ResponseData?.JsonTo<TestOrder_All_True>();
    }
#endregion
}