using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using System.Threading;

namespace AyCode.Core.Serializers.Jsons;

public static partial class AcJsonSerializer
{
    private static class SerializationContextPool
    {
        private static readonly ConcurrentQueue<JsonSerializationContext> Pool = new();

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static JsonSerializationContext Get(in AcJsonSerializerOptions options)
        {
            if (Pool.TryDequeue(out var context))
            {
                context.Reset(options);
                return context;
            }
            return new JsonSerializationContext(options);
        }

        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void ReturnAsync(JsonSerializationContext context)
        {
            // 🔥 FIRE-AND-FORGET: cleanup háttérben
            ThreadPool.UnsafeQueueUserWorkItem(Return, context, preferLocal: true);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Return(JsonSerializationContext context)
        {
            if (Pool.Count < context.Options.MaxContextPoolSize)
            {
                context.Clear();
                Pool.Enqueue(context);
            }
            else
            {
                context.Dispose();  // ✅ Utf8JsonWriter + ArrayBufferWriter cleanup!
            }
        }
    }

    private sealed class JsonSerializationContext : SerializationContextBase<JsonSerializeTypeMetadata, AcJsonSerializerOptions>, IDisposable
    {
        private readonly ArrayBufferWriter<byte> _buffer;
        public Utf8JsonWriter Writer { get; private set; }
        
        // Use shared reference tracker from AcSerializerCommon
        private readonly AcSerializerCommon.SerializationReferenceTracker _refTracker = new();

        private static readonly JsonWriterOptions WriterOptions = new()
        {
            Indented = false,
            SkipValidation = true // Skip validation for performance
        };

        public JsonSerializationContext(in AcJsonSerializerOptions options)
        {
            _buffer = new ArrayBufferWriter<byte>(4096);
            Writer = new Utf8JsonWriter(_buffer, WriterOptions);
            Reset(options);
        }

        /// <summary>
        /// Factory for creating JsonSerializeTypeMetadata instances.
        /// </summary>
        protected override MetadataFactoryDelegate MetadataFactory
            => static ([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(TypeMetadataBase.RequiredMembers)] t)
                => new JsonSerializeTypeMetadata(t);

        public override void Reset(AcJsonSerializerOptions options)
        {
            // IMPORTANT: base.Reset sets Options first
            base.Reset(options);
            
            _refTracker.Reset();
            
            if (ReferenceHandling != ReferenceHandlingMode.None)
            {
                _refTracker.EnsureInitialized();
            }
        }

        public override void Clear()
        {
            Writer.Reset();
            _buffer.Clear();
            _refTracker.Reset();
            
            // Clear wrapper tracking - returns IdentityMap arrays to pool
            base.Clear();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool TrackForScanning(object obj) => _refTracker.TrackForScanning(obj);
        
        /// <summary>
        /// IId-aware tracking for reference scanning.
        /// Uses IId.Id for IId types, RuntimeHelpers.GetHashCode for others.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool TrackForScanningWithIId(object obj, JsonSerializeTypeMetadata metadata)
        {
            int existingRefId;
            return _refTracker.TrackForScanningWithIId(obj, metadata, out existingRefId);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool ShouldWriteId(object obj, out int id) => _refTracker.ShouldWriteId(obj, out id);
        
        /// <summary>
        /// IId-aware check for reference ID writing.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool ShouldWriteIdForIId(object obj, JsonSerializeTypeMetadata metadata, out int id)
            => _refTracker.ShouldWriteIdForIId(obj, metadata, out id);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MarkAsWritten(object obj, int id) => _refTracker.MarkAsWritten(obj, id);
        
        /// <summary>
        /// IId-aware mark as written.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MarkAsWrittenForIId(object obj, JsonSerializeTypeMetadata metadata, int id)
            => _refTracker.MarkAsWrittenForIId(obj, metadata, id);
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool TryGetExistingRef(object obj, out int refId) => _refTracker.TryGetExistingRef(obj, out refId);
        
        /// <summary>
        /// IId-aware existing ref lookup.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool TryGetExistingRefForIId(object obj, JsonSerializeTypeMetadata metadata, out int refId)
            => _refTracker.TryGetExistingRefForIId(obj, metadata, out refId);

        public string GetResult()
        {
            Writer.Flush();
            return Encoding.UTF8.GetString(_buffer.WrittenSpan);
        }

        public void Dispose()
        {
            Writer.Dispose();
        }
    }
}
