using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using AyCode.Core.Helpers;
using AyCode.Core.Serializers;
using AyCode.Core.Serializers.Attributes;
using static AyCode.Core.Helpers.JsonUtilities;
namespace AyCode.Core.Serializers.Binaries;

public static partial class AcBinaryDeserializer
{
    internal sealed class BinaryDeserializeTypeMetadata : DeserializeTypeMetadataBase
    {
        /// <summary>
        /// Properties array ordered alphabetically by name for index-based lookup.
        /// This matches the serializer's ordering, enabling O(1) array access.
        /// </summary>
        public BinaryPropertySetterInfo[] PropertiesArray { get; }

        /// <summary>
        /// True if this type has a Source Generator generated deserializer available.
        /// Note: Due to ref struct limitations, the generated code cannot be called via delegates.
        /// The generated code must be called directly from consumer code that has access to it.
        /// </summary>
        public bool HasGeneratedDeserializer { get; }

        /// <summary>
        /// The generated serializer type, if available.
        /// Consumer code can use this to call the generated methods directly.
        /// </summary>
        public Type? GeneratedSerializerType { get; }

        public BinaryDeserializeTypeMetadata(
            [DynamicallyAccessedMembers(RequiredMembers)] Type type)
            : base(type, HasJsonIgnoreAttribute)
        {
            // Use pre-computed WritableProperties directly - no method call overhead!
            var orderedProperties = WritableProperties;

            PropertiesArray = new BinaryPropertySetterInfo[orderedProperties.Length];
            var complexCount = 0;
            for (var i = 0; i < orderedProperties.Length; i++)
            {
                var setter = new BinaryPropertySetterInfo(orderedProperties[i], type);
                if (setter.IsComplexType)
                    setter.ComplexPropertyIndex = complexCount++;
                PropertiesArray[i] = setter;
            }
            ComplexPropertyCount = complexCount;

            // Fast check: only look for generated serializer if type has [AcBinarySerializable] attribute
            if (false && type.IsDefined(typeof(AcBinarySerializableAttribute), inherit: false))
            {
                GeneratedSerializerType = FindGeneratedSerializerType(type);
                HasGeneratedDeserializer = GeneratedSerializerType != null;
            }
            else
            {
                GeneratedSerializerType = null;
                HasGeneratedDeserializer = false;
            }
        }

        /// <summary>
        /// Gets property by index (O(1) array access). Used for index-based deserialization.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public BinaryPropertySetterInfo? GetPropertyByIndex(int index)
            => (uint)index < (uint)PropertiesArray.Length ? PropertiesArray[index] : null;

        /// <summary>
        /// Finds the generated serializer type for the given type.
        /// </summary>
        private static Type? FindGeneratedSerializerType(Type type)
        {
            // Generated class name format: {ClassName}_AcBinarySerializer
            var generatedTypeName = $"{type.FullName}_AcBinarySerializer";
            
            // Try to find in the same assembly
            var serializerType = type.Assembly.GetType(generatedTypeName);
            
            // For nested types, the generated class is at namespace level
            if (serializerType == null && type.IsNested)
            {
                var ns = type.Namespace ?? "";
                var simpleName = $"{(string.IsNullOrEmpty(ns) ? "" : ns + ".")}{type.Name}_AcBinarySerializer";
                serializerType = type.Assembly.GetType(simpleName);
            }

            return serializerType;
        }
    }

    /// <summary>
    /// Binary deserialization property setter with typed setters for performance.
    /// </summary>
    internal sealed class BinaryPropertySetterInfo : BinaryPropertySetterBase
    {
        private static readonly Func<object, object?> NullGetter = static _ => null;
        private static readonly Action<object, object?> NullSetter = static (_, _) => { };

        // Fields for manual constructor case
        private readonly Func<object, object?>? _manualGetter;
        private readonly Action<object, object?>? _manualSetter;
        private readonly string? _manualName;
        private readonly Type? _manualPropertyType;
        private readonly bool _isManualConstruction;
        private readonly Type? _manualElementType;
        private readonly Type? _manualElementIdType;
        private readonly Func<object, object?>? _manualElementIdGetter;
        private readonly bool _manualIsIIdCollection;

        public BinaryPropertySetterInfo(
            PropertyInfo property,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type declaringType)
            : base(property, declaringType)
        {
            _isManualConstruction = false;
        }

        /// <summary>
        /// Constructor for manually created property info (used in merge operations).
        /// </summary>
        public BinaryPropertySetterInfo(
            string name,
            Type propertyType,
            bool isCollection,
            Type? elementType,
            Type? elementIdType,
            Func<object, object?>? elementIdGetter)
            : base(CreateDummyProperty(), typeof(DummyClass))
        {
            _isManualConstruction = true;
            _manualName = name;
            _manualPropertyType = propertyType;
            _manualElementType = elementType;
            _manualElementIdType = elementIdType;
            _manualElementIdGetter = elementIdGetter;
            _manualIsIIdCollection = elementIdGetter != null && elementIdType != null;
            _manualGetter = NullGetter;
            _manualSetter = NullSetter;
        }

        // Dummy property for manual construction - we override all relevant properties
        private static PropertyInfo CreateDummyProperty()
        {
            return typeof(DummyClass).GetProperty(nameof(DummyClass.DummyProp))!;
        }

        private sealed class DummyClass
        {
            public object? DummyProp { get; set; }
        }

        // Override properties for manual constructor
        public new string Name => _isManualConstruction ? _manualName! : base.Name;
        public new Type PropertyType => _isManualConstruction ? _manualPropertyType! : base.PropertyType;
        public new Type? ElementType => _isManualConstruction ? _manualElementType : base.ElementType;
        public new Type? ElementIdType => _isManualConstruction ? _manualElementIdType : base.ElementIdType;
        public new Func<object, object?>? ElementIdGetter => _isManualConstruction ? _manualElementIdGetter : base.ElementIdGetter;
        public new bool IsIIdCollection => _isManualConstruction ? _manualIsIIdCollection : base.IsIIdCollection;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public new object? GetValue(object target) => _isManualConstruction ? _manualGetter!(target) : base.GetValue(target);

        public override void SetValue(object target, object? value)
        {
            if (_isManualConstruction)
            {
                _manualSetter!(target, value);
                return;
            }

            try
            {
                base.SetValue(target, value);
            }
            catch (InvalidCastException ex)
            {
                ThrowDetailedCastException(target, value, ex);
            }
        }

        private void ThrowDetailedCastException(object target, object? value, InvalidCastException ex)
        {
            var valueType = value?.GetType().FullName ?? "null";
            var propType = PropertyType.FullName;
            var targetTypeName = target.GetType().FullName;
            var underlyingType = Nullable.GetUnderlyingType(PropertyType);
            
            var valueInfo = value switch
            {
                null => "null",
                int i => $"int:{i}",
                long l => $"long:{l}",
                DateTime dt => $"DateTime:{dt:O}",
                string s => $"string:'{s}'",
                _ => $"{value.GetType().Name}:{value}"
            };
            
            throw new InvalidCastException(
                $"Cannot set property '{Name}' on type '{targetTypeName}' - " +
                $"PropertyType: '{propType}', ValueType: '{valueType}', Value: {valueInfo}, " +
                $"IsNullable: {underlyingType != null}, UnderlyingType: {underlyingType?.FullName ?? "N/A"}, " +
                $"IsValueType: {PropertyType.IsValueType}, IsCollection: {IsCollection}, IsComplexType: {IsComplexType}",
                ex);
        }
    }
}
