using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using static AyCode.Core.Helpers.JsonUtilities;

namespace AyCode.Core.Serializers.Toons;

public static partial class AcToonSerializer
{
    /// <summary>
    /// Cached metadata for a type including properties, type name, and descriptions.
    /// Uses SerializeTypeMetadataBase infrastructure for shared caching across all serializers.
    /// </summary>
    private sealed class ToonSerializeTypeMetadata : SerializeTypeMetadataBase
    {
        public string TypeName { get; }
        public string ShortTypeName { get; }
        public ToonPropertyAccessor[] Properties { get; }
        public bool IsCollection { get; }
        public bool IsDictionary { get; }
        public Type? ElementType { get; }
        public ToonDescriptionAttribute? CustomDescription { get; }

        public ToonSerializeTypeMetadata(
            [DynamicallyAccessedMembers(RequiredMembers)] Type type)
            : base(type, HasToonIgnoreAttribute)
        {
            TypeName = type.FullName ?? type.Name;
            ShortTypeName = type.Name;

            // Get custom attribute if present
            CustomDescription = type.GetCustomAttribute<ToonDescriptionAttribute>();

            // Check if collection or dictionary (use shared JsonUtilities methods)
            IsDictionary = IsDictionaryType(type, out _, out _);
            if (!IsDictionary)
            {
                // Only treat as collection if it implements IEnumerable (excluding string)
                if (typeof(IEnumerable).IsAssignableFrom(type) && !ReferenceEquals(type, StringType))
                {
                    ElementType = GetCollectionElementType(type);
                    IsCollection = true;
                }
            }

            // Build property accessors using pre-computed ReadableProperties from base
            if (!IsCollection && !IsDictionary && !IsPrimitiveOrStringFast(type))
            {
                var props = ReadableProperties
                    .Select(p => new ToonPropertyAccessor(p, type))
                    .ToArray();

                Properties = props;
            }
            else
            {
                Properties = [];
            }
        }
    }


    /// <summary>
    /// Property accessor with compiled getter and cached navigation metadata.
    /// Extends PropertyAccessorBase for shared functionality and adds Toon-specific metadata.
    /// </summary>
    private sealed class ToonPropertyAccessor : PropertyAccessorBase
    {
        public PropertyInfo PropertyInfo { get; }
        public string TypeDisplayName { get; }
        public ToonDescriptionAttribute? CustomDescription { get; }

        private AcNavigationPropertyInfo? _navigation;

        /// <summary>
        /// Navigation metadata is lazily initialized to avoid circular reference issues
        /// when types reference each other (e.g., OrderDto ? OrderItemDto).
        /// </summary>
        public AcNavigationPropertyInfo Navigation => _navigation ??= DetectNavigationMetadata(PropertyInfo, CustomDescription);

        public ToonPropertyAccessor(PropertyInfo prop, Type declaringType)
            : base(prop, declaringType)
        {
            PropertyInfo = prop;

            // Build type display name for meta section
            TypeDisplayName = BuildTypeDisplayName(PropertyType);

            // Get custom attribute if present
            CustomDescription = prop.GetCustomAttribute<ToonDescriptionAttribute>();
        }

        /// <summary>
        /// Checks if value is default/null without boxing value types.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsDefaultValue(object? value)
        {
            if (value == null) return true;

            switch (PropertyTypeCode)
            {
                case TypeCode.Int32: return (int)value == 0;
                case TypeCode.Int64: return (long)value == 0L;
                case TypeCode.Double: return (double)value == 0.0;
                case TypeCode.Decimal: return (decimal)value == 0m;
                case TypeCode.Single: return (float)value == 0f;
                case TypeCode.Byte: return (byte)value == 0;
                case TypeCode.Int16: return (short)value == 0;
                case TypeCode.UInt16: return (ushort)value == 0;
                case TypeCode.UInt32: return (uint)value == 0;
                case TypeCode.UInt64: return (ulong)value == 0;
                case TypeCode.SByte: return (sbyte)value == 0;
                case TypeCode.Boolean: return (bool)value == false;
                case TypeCode.String: return string.IsNullOrEmpty((string)value);
            }

            if (PropertyType.IsEnum) return Convert.ToInt32(value) == 0;
            if (ReferenceEquals(PropertyType, GuidType)) return (Guid)value == Guid.Empty;

            return false;
        }

        /// <summary>
        /// Build human-readable C# type name for meta section.
        /// Uses shared AcSerializerCommon.GetCSharpTypeName for consistency.
        /// </summary>
        private static string BuildTypeDisplayName(Type type)
        {
            return AcSerializerCommon.GetCSharpTypeName(type, useShortNames: true);
        }
    }
}
