using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;

//using ReferenceEqualityComparer = AyCode.Core.Serializers.ReferenceEqualityComparer;

namespace AyCode.Core.Serializers.Toons;

public static partial class AcToonSerializer
{
    #region Context Pool

    private static class ToonSerializationContextPool
    {
        private static readonly ConcurrentQueue<ToonSerializationContext> Pool = new();

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

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Return(ToonSerializationContext context)
        {
            if (Pool.Count < context.Options.MaxContextPoolSize)
            {
                context.Clear();
                Pool.Enqueue(context);
            }
        }
    }

    #endregion

    #region Serialization Context

    /// <summary>
    /// Pooled context for Toon serialization.
    /// Handles output building, indentation, and reference tracking.
    /// </summary>
    private sealed class ToonSerializationContext : SerializationContextBase<ToonSerializeTypeMetadata, AcToonSerializerOptions>
    {
        private readonly StringBuilder _builder;
        private Dictionary<object, int>? _scanOccurrences;
        private Dictionary<object, int>? _writtenRefs;
        private HashSet<object>? _multiReferenced;
        private HashSet<Type>? _registeredTypes;
        private int _nextRefId;

        public int CurrentIndentLevel { get; set; }

        public ToonSerializationContext(AcToonSerializerOptions options)
        {
            _builder = new StringBuilder(4096);
            Reset(options);
        }

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

        public override void Reset(AcToonSerializerOptions options)
        {
            // IMPORTANT: base.Reset sets Options first
            base.Reset(options);
            
            CurrentIndentLevel = 0;
            _nextRefId = 1;

            if (ReferenceHandling != ReferenceHandlingMode.None)
            {
                _scanOccurrences ??= new Dictionary<object, int>(64, ReferenceEqualityComparer.Instance);
                _writtenRefs ??= new Dictionary<object, int>(32, ReferenceEqualityComparer.Instance);
                _multiReferenced ??= new HashSet<object>(32, ReferenceEqualityComparer.Instance);
            }

            _registeredTypes ??= new HashSet<Type>(16);
        }

        public void Clear()
        {
            _builder.Clear();
            _scanOccurrences?.Clear();
            _writtenRefs?.Clear();
            _multiReferenced?.Clear();
            _registeredTypes?.Clear();
            CurrentIndentLevel = 0;
            _nextRefId = 1;
        }

        #region Reference Handling

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool TrackForScanning(object obj)
        {
            if (_scanOccurrences == null) return true;

            ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(_scanOccurrences, obj, out var exists);
            if (exists)
            {
                count++;
                _multiReferenced!.Add(obj);
                return false;
            }
            count = 1;
            return true;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool ShouldWriteRef(object obj, out int refId)
        {
            if (_multiReferenced != null && _multiReferenced.Contains(obj) && !_writtenRefs!.ContainsKey(obj))
            {
                refId = _nextRefId++;
                return true;
            }
            refId = 0;
            return false;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MarkAsWritten(object obj, int refId) => _writtenRefs![obj] = refId;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool TryGetExistingRef(object obj, out int refId)
        {
            if (_writtenRefs != null && _writtenRefs.TryGetValue(obj, out refId))
                return true;
            refId = 0;
            return false;
        }

        #endregion

        #region Type Registration

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool RegisterType(Type type)
        {
            if (_registeredTypes == null) return false;
            return _registeredTypes.Add(type);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsTypeRegistered(Type type)
        {
            return _registeredTypes?.Contains(type) ?? false;
        }

        #endregion

        #region Output Methods

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Write(string text)
        {
            _builder.Append(text);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Write(char c)
        {
            _builder.Append(c);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void WriteLine()
        {
            _builder.AppendLine();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void WriteLine(string text)
        {
            _builder.AppendLine(text);
        }

        public void WriteIndent()
        {
            if (!Options.UseIndentation) return;

            for (var i = 0; i < CurrentIndentLevel; i++)
            {
                _builder.Append(Options.IndentString);
            }
        }

        public void WriteIndentedLine(string text)
        {
            WriteIndent();
            WriteLine(text);
        }

        public void WriteProperty(string name, string value, string? inlineComment = null)
        {
            WriteIndent();
            _builder.Append(name);

            // Token optimization: no spaces around '=' when indentation is disabled
            if (Options.UseIndentation)
            {
                _builder.Append(" = ");
            }
            else
            {
                _builder.Append('=');
            }

            _builder.Append(value);

            if (inlineComment != null && Options.UseInlineComments)
            {
                _builder.Append("  # ");
                _builder.Append(inlineComment);
            }

            WriteLine();
        }

        public void WriteComment(string comment)
        {
            WriteIndent();
            _builder.Append("# ");
            _builder.AppendLine(comment);
        }

        public string GetResult()
        {
            return _builder.ToString();
        }

        #endregion
    }

    #endregion
}
