using AyCode.Core.Serializers.Attributes;
using AyCode.Core.Serializers.Binaries;
using BenchmarkDotNet.Attributes;
using MessagePack;
using MessagePack.Resolvers;

namespace AyCode.Core.Benchmarks;

/// <summary>
/// Pure contractless model - NO MessagePack attributes.
/// This tests TRUE runtime serialization without any source generation.
/// </summary>
[AcBinarySerializable]
public class PureContractlessModel
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string Description { get; set; } = "";
    public bool IsActive { get; set; }
    public double Value { get; set; }
    public decimal Price { get; set; }
    public long BigNumber { get; set; }
    public DateTime CreatedAt { get; set; }
    public Guid UniqueId { get; set; }
    public int Count { get; set; }
    public string Category { get; set; } = "";
    public string Status { get; set; } = "";
}

/// <summary>
/// Benchmark model with only primitive types - fully supported by Source Generator.
/// MessagePack attributes added for fair comparison with Source Generator.
/// </summary>
[AcBinarySerializable]
[MessagePackObject]
public class PrimitiveBenchmarkModel
{
    [Key(0)] public int Id { get; set; }
    [Key(1)] public string Name { get; set; } = "";
    [Key(2)] public string Description { get; set; } = "";
    [Key(3)] public bool IsActive { get; set; }
    [Key(4)] public double Value { get; set; }
    [Key(5)] public decimal Price { get; set; }
    [Key(6)] public long BigNumber { get; set; }
    [Key(7)] public DateTime CreatedAt { get; set; }
    [Key(8)] public Guid UniqueId { get; set; }
    [Key(9)] public int Count { get; set; }
    [Key(10)] public string Category { get; set; } = "";
    [Key(11)] public string Status { get; set; } = "";
}

/// <summary>
/// TRUE Contractless benchmark - no attributes, pure runtime serialization.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class PureContractlessBenchmark
{
    private PureContractlessModel _testData = null!;
    private byte[] _acBinaryData = null!;
    private byte[] _msgPackContractlessData = null!;
    
    private AcBinarySerializerOptions _binaryOptions = null!;
    private MessagePackSerializerOptions _msgPackContractlessOptions = null!;

    [GlobalSetup]
    public void Setup()
    {
        _testData = new PureContractlessModel
        {
            Id = 12345,
            Name = "Test Product Name",
            Description = "This is a longer description for testing string serialization performance",
            IsActive = true,
            Value = 123.456789,
            Price = 99.99m,
            BigNumber = 9876543210L,
            CreatedAt = DateTime.UtcNow,
            UniqueId = Guid.NewGuid(),
            Count = 42,
            Category = "Electronics",
            Status = "Available"
        };

        _binaryOptions = AcBinarySerializerOptions.WithoutReferenceHandling;
        _msgPackContractlessOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);

        _acBinaryData = AcBinarySerializer.Serialize(_testData, _binaryOptions);
        _msgPackContractlessData = MessagePackSerializer.Serialize(_testData, _msgPackContractlessOptions);

        Console.WriteLine($"=== Pure Contractless (NO attributes) ===");
        Console.WriteLine($"AcBinary:             {_acBinaryData.Length} bytes");
        Console.WriteLine($"MsgPack Contractless: {_msgPackContractlessData.Length} bytes");
    }

    [Benchmark(Description = "AcBinary Serialize")]
    public byte[] Serialize_AcBinary() 
        => AcBinarySerializer.Serialize(_testData, _binaryOptions);

    [Benchmark(Description = "MsgPack Contractless Serialize", Baseline = true)]
    public byte[] Serialize_MsgPack_Contractless() 
        => MessagePackSerializer.Serialize(_testData, _msgPackContractlessOptions);

    [Benchmark(Description = "AcBinary Deserialize")]
    public PureContractlessModel? Deserialize_AcBinary() 
        => AcBinaryDeserializer.Deserialize<PureContractlessModel>(_acBinaryData);

    [Benchmark(Description = "MsgPack Contractless Deserialize")]
    public PureContractlessModel? Deserialize_MsgPack_Contractless() 
        => MessagePackSerializer.Deserialize<PureContractlessModel>(_msgPackContractlessData, _msgPackContractlessOptions);
}

/// <summary>
/// Benchmark comparing Source Generator vs Runtime serialization for primitive-only types.
/// Uses MessagePack with [MessagePackObject] attributes for fair Source Generator comparison.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class SourceGeneratorVsRuntimeBenchmark
{
    private PrimitiveBenchmarkModel _testData = null!;
    private byte[] _runtimeSerializedData = null!;
    private byte[] _msgPackData = null!;
    private byte[] _msgPackContractlessData = null!;
    
    private AcBinarySerializerOptions _binaryOptions = null!;
    private MessagePackSerializerOptions _msgPackOptions = null!;
    private MessagePackSerializerOptions _msgPackContractlessOptions = null!;

    [GlobalSetup]
    public void Setup()
    {
        _testData = new PrimitiveBenchmarkModel
        {
            Id = 12345,
            Name = "Test Product Name",
            Description = "This is a longer description for testing string serialization performance",
            IsActive = true,
            Value = 123.456789,
            Price = 99.99m,
            BigNumber = 9876543210L,
            CreatedAt = DateTime.UtcNow,
            UniqueId = Guid.NewGuid(),
            Count = 42,
            Category = "Electronics",
            Status = "Available"
        };

        _binaryOptions = AcBinarySerializerOptions.WithoutReferenceHandling;
        
        // MessagePack with Source Generator (uses [MessagePackObject] + [Key] attributes)
        _msgPackOptions = MessagePackSerializerOptions.Standard;
        
        // MessagePack without Source Generator (Contractless - reflection based, like AcBinary runtime)
        _msgPackContractlessOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);

        // Pre-serialize for deserialize benchmarks
        _runtimeSerializedData = AcBinarySerializer.Serialize(_testData, _binaryOptions);
        _msgPackData = MessagePackSerializer.Serialize(_testData, _msgPackOptions);
        _msgPackContractlessData = MessagePackSerializer.Serialize(_testData, _msgPackContractlessOptions);

        // Print sizes
        Console.WriteLine($"=== Primitive Model Serialization ===");
        Console.WriteLine($"AcBinary Runtime:        {_runtimeSerializedData.Length} bytes");
        Console.WriteLine($"MessagePack SourceGen:   {_msgPackData.Length} bytes");
        Console.WriteLine($"MessagePack Contractless:{_msgPackContractlessData.Length} bytes");
        
        // Verify generated serializer exists
        var generatedType = typeof(PrimitiveBenchmarkModel).Assembly.GetType(
            $"{typeof(PrimitiveBenchmarkModel).FullName}_AcBinarySerializer");
        Console.WriteLine($"AcBinary Generated serializer found: {generatedType != null}");
    }

    #region Serialize Benchmarks

    [Benchmark(Description = "AcBinary Runtime Serialize")]
    public byte[] Serialize_AcBinary_Runtime() 
        => AcBinarySerializer.Serialize(_testData, _binaryOptions);

    [Benchmark(Description = "MsgPack SourceGen Serialize", Baseline = true)]
    public byte[] Serialize_MsgPack_SourceGen() 
        => MessagePackSerializer.Serialize(_testData, _msgPackOptions);

    [Benchmark(Description = "MsgPack Contractless Serialize")]
    public byte[] Serialize_MsgPack_Contractless() 
        => MessagePackSerializer.Serialize(_testData, _msgPackContractlessOptions);

    #endregion

    #region Deserialize Benchmarks

    [Benchmark(Description = "AcBinary Runtime Deserialize")]
    public PrimitiveBenchmarkModel? Deserialize_AcBinary_Runtime() 
        => AcBinaryDeserializer.Deserialize<PrimitiveBenchmarkModel>(_runtimeSerializedData);

    [Benchmark(Description = "MsgPack SourceGen Deserialize")]
    public PrimitiveBenchmarkModel? Deserialize_MsgPack_SourceGen() 
        => MessagePackSerializer.Deserialize<PrimitiveBenchmarkModel>(_msgPackData, _msgPackOptions);

    [Benchmark(Description = "MsgPack Contractless Deserialize")]
    public PrimitiveBenchmarkModel? Deserialize_MsgPack_Contractless() 
        => MessagePackSerializer.Deserialize<PrimitiveBenchmarkModel>(_msgPackContractlessData, _msgPackContractlessOptions);

    #endregion
}

/// <summary>
/// Repeated string benchmark model - tests string interning performance.
/// MessagePack attributes added for fair comparison.
/// </summary>
[AcBinarySerializable]
[MessagePackObject]
public class RepeatedStringBenchmarkModel
{
    [Key(0)] public int Id { get; set; }
    [Key(1)] public string Status { get; set; } = "";
    [Key(2)] public string Category { get; set; } = "";
    [Key(3)] public string Priority { get; set; } = "";
    [Key(4)] public string Type { get; set; } = "";
}

/// <summary>
/// Benchmark for types with repeated string values - where AcBinary string interning helps.
/// Compares against both MessagePack SourceGen and Contractless modes.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class RepeatedStringBenchmark
{
    private List<RepeatedStringBenchmarkModel> _items = null!;
    private byte[] _acBinaryData = null!;
    private byte[] _msgPackData = null!;
    private byte[] _msgPackContractlessData = null!;
    
    private AcBinarySerializerOptions _binaryOptions = null!;
    private MessagePackSerializerOptions _msgPackOptions = null!;
    private MessagePackSerializerOptions _msgPackContractlessOptions = null!;

    [Params(100, 500)]
    public int ItemCount { get; set; }

    [GlobalSetup]
    public void Setup()
    {
        _items = Enumerable.Range(0, ItemCount).Select(i => new RepeatedStringBenchmarkModel
        {
            Id = i,
            Status = i % 3 == 0 ? "Pending" : i % 3 == 1 ? "Processing" : "Completed",
            Category = $"Category_{i % 5}",
            Priority = i % 2 == 0 ? "High" : "Low",
            Type = i % 4 == 0 ? "TypeA" : i % 4 == 1 ? "TypeB" : i % 4 == 2 ? "TypeC" : "TypeD"
        }).ToList();

        _binaryOptions = AcBinarySerializerOptions.WithoutReferenceHandling;
        _msgPackOptions = MessagePackSerializerOptions.Standard;
        _msgPackContractlessOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);

        _acBinaryData = AcBinarySerializer.Serialize(_items, _binaryOptions);
        _msgPackData = MessagePackSerializer.Serialize(_items, _msgPackOptions);
        _msgPackContractlessData = MessagePackSerializer.Serialize(_items, _msgPackContractlessOptions);

        Console.WriteLine($"=== Repeated Strings ({ItemCount} items) ===");
        Console.WriteLine($"AcBinary:             {_acBinaryData.Length} bytes");
        Console.WriteLine($"MsgPack SourceGen:    {_msgPackData.Length} bytes");
        Console.WriteLine($"MsgPack Contractless: {_msgPackContractlessData.Length} bytes");
    }

    [Benchmark(Description = "AcBinary Serialize")]
    public byte[] Serialize_AcBinary() 
        => AcBinarySerializer.Serialize(_items, _binaryOptions);

    [Benchmark(Description = "MsgPack SourceGen Serialize", Baseline = true)]
    public byte[] Serialize_MsgPack_SourceGen() 
        => MessagePackSerializer.Serialize(_items, _msgPackOptions);

    [Benchmark(Description = "MsgPack Contractless Serialize")]
    public byte[] Serialize_MsgPack_Contractless() 
        => MessagePackSerializer.Serialize(_items, _msgPackContractlessOptions);

    [Benchmark(Description = "AcBinary Deserialize")]
    public List<RepeatedStringBenchmarkModel>? Deserialize_AcBinary() 
        => AcBinaryDeserializer.Deserialize<List<RepeatedStringBenchmarkModel>>(_acBinaryData);

    [Benchmark(Description = "MsgPack SourceGen Deserialize")]
    public List<RepeatedStringBenchmarkModel>? Deserialize_MsgPack_SourceGen() 
        => MessagePackSerializer.Deserialize<List<RepeatedStringBenchmarkModel>>(_msgPackData, _msgPackOptions);

    [Benchmark(Description = "MsgPack Contractless Deserialize")]
    public List<RepeatedStringBenchmarkModel>? Deserialize_MsgPack_Contractless() 
        => MessagePackSerializer.Deserialize<List<RepeatedStringBenchmarkModel>>(_msgPackContractlessData, _msgPackContractlessOptions);
}
