using System.Runtime.CompilerServices;
using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Tests.TestModels;

namespace AyCode.Core.Tests.GeneratedWriters;

/// <summary>
/// Hand-written generated binary writer for TestOrder_All_True.
/// Demonstrates the pattern that the source generator will produce.
/// 
/// Bypasses the runtime switch/delegate property loop:
/// - Direct obj.Property access instead of Func&lt;&gt;.Invoke()
/// - No switch dispatch per property
/// - No boxing for value types
/// - Small method (~500B native) vs 27KB WriteObject — better ICache
/// 
/// Properties are written in alphabetical order to match the runtime serializer.
/// Complex/Collection properties fall back to the runtime serializer via WriteValue.
/// </summary>
internal sealed class TestOrderWriter : IGeneratedBinaryWriter
{
    internal static readonly TestOrderWriter Instance = new();

    public void WriteProperties<TOutput>(object value,
        AcBinarySerializer.BinarySerializationContext<TOutput> context)
        where TOutput : struct, IBinaryOutputBase
    {
        var obj = Unsafe.As<TestOrder_All_True>(value);

        // Properties in alphabetical order (matching runtime serializer):

        // AuditMetadata: MetadataInfo_All_True? (complex, nullable)
        WriteComplexOrNull(obj.AuditMetadata, context);

        // Category: SharedCategory_All_True? (complex, nullable)
        WriteComplexOrNull(obj.Category, context);

        // CreatedAt: DateTime (markerless)
        context.WriteDateTimeBits(obj.CreatedAt);

        // Id: int (markerless)
        context.WriteVarInt(obj.Id);

        // Items: List<TestOrderItem_All_True> (collection)
        WriteComplexOrNull(obj.Items, context);

        // MetadataList: List<MetadataInfo_All_True> (collection)
        WriteComplexOrNull(obj.MetadataList, context);

        // NoMergeItems: List<TestOrderItem_All_True> (collection)
        WriteComplexOrNull(obj.NoMergeItems, context);

        // OrderMetadata: MetadataInfo_All_True? (complex, nullable)
        WriteComplexOrNull(obj.OrderMetadata, context);

        // OrderNumber: string
        AcBinarySerializer.WriteStringGenerated(obj.OrderNumber, context);

        // Owner: SharedUser? (complex, nullable)
        WriteComplexOrNull(obj.Owner, context);

        // PaidDateUtc: DateTime? (nullable)
        var paidDate = obj.PaidDateUtc;
        if (paidDate.HasValue)
        {
            context.WriteByte(BinaryTypeCode.DateTime);
            context.WriteDateTimeBits(paidDate.Value);
        }
        else
        {
            context.WriteByte(BinaryTypeCode.Null);
        }

        // PrimaryTag: SharedTag_All_True? (complex, nullable)
        WriteComplexOrNull(obj.PrimaryTag, context);

        // SecondaryTag: SharedTag_All_True? (complex, nullable)
        WriteComplexOrNull(obj.SecondaryTag, context);

        // Status: TestStatus (enum, markerless)
        context.WriteVarInt((int)obj.Status);

        // Tags: List<SharedTag_All_True> (collection)
        WriteComplexOrNull(obj.Tags, context);

        // TotalAmount: decimal (markerless)
        context.WriteDecimalBits(obj.TotalAmount);
    }

    public void ScanObject<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context) where TOutput : struct, IBinaryOutputBase
    {
        throw new NotImplementedException();
    }

    public void ScanForDuplicates<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context) where TOutput : struct, IBinaryOutputBase
    {
        if (!context.HasCaching) return;
        ScanObject(value, context);
        context.SortWritePlan();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static void WriteComplexOrNull<TOutput>(object? value, AcBinarySerializer.BinarySerializationContext<TOutput> context)
        where TOutput : struct, IBinaryOutputBase
    {
        if (value == null)
        {
            context.WriteByte(BinaryTypeCode.PropertySkip);
            return;
        }

        AcBinarySerializer.WriteValueGenerated(value, value.GetType(), context);
    }
}
