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

namespace AyCode.Core.Serializers;

/// <summary>
/// Base class for property accessors used by serializers.
/// Contains getter functionality and typed delegate fields.
/// Typed getters eliminate runtime cast overhead for value type properties.
/// </summary>
public abstract class PropertyAccessorBase : PropertyMetadataBase
{
    #region Strongly-typed getter delegate fields (eliminates runtime cast)
    
    // Only ONE of these is set based on AccessorType
    private readonly Func<object, int>? _int32Getter;
    private readonly Func<object, long>? _int64Getter;
    private readonly Func<object, bool>? _boolGetter;
    private readonly Func<object, double>? _doubleGetter;
    private readonly Func<object, float>? _floatGetter;
    private readonly Func<object, decimal>? _decimalGetter;
    private readonly Func<object, DateTime>? _dateTimeGetter;
    private readonly Func<object, byte>? _byteGetter;
    private readonly Func<object, short>? _int16Getter;
    private readonly Func<object, ushort>? _uint16Getter;
    private readonly Func<object, uint>? _uint32Getter;
    private readonly Func<object, ulong>? _uint64Getter;
    private readonly Func<object, Guid>? _guidGetter;
    private readonly Func<object, string?>? _stringGetter;
    
    #endregion

    protected PropertyAccessorBase(
        PropertyInfo prop,
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type declaringType)
        : base(prop, declaringType)
    {
        InitializeTypedGetter(declaringType, prop);
    }

    private void InitializeTypedGetter(
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type declaringType,
        PropertyInfo prop)
    {
        switch (AccessorType)
        {
            case PropertyAccessorType.Int32:
                Unsafe.AsRef(in _int32Getter) = AcSerializerCommon.CreateTypedGetter<int>(declaringType, prop);
                break;
            case PropertyAccessorType.Int64:
                Unsafe.AsRef(in _int64Getter) = AcSerializerCommon.CreateTypedGetter<long>(declaringType, prop);
                break;
            case PropertyAccessorType.Boolean:
                Unsafe.AsRef(in _boolGetter) = AcSerializerCommon.CreateTypedGetter<bool>(declaringType, prop);
                break;
            case PropertyAccessorType.Double:
                Unsafe.AsRef(in _doubleGetter) = AcSerializerCommon.CreateTypedGetter<double>(declaringType, prop);
                break;
            case PropertyAccessorType.Single:
                Unsafe.AsRef(in _floatGetter) = AcSerializerCommon.CreateTypedGetter<float>(declaringType, prop);
                break;
            case PropertyAccessorType.Decimal:
                Unsafe.AsRef(in _decimalGetter) = AcSerializerCommon.CreateTypedGetter<decimal>(declaringType, prop);
                break;
            case PropertyAccessorType.DateTime:
                Unsafe.AsRef(in _dateTimeGetter) = AcSerializerCommon.CreateTypedGetter<DateTime>(declaringType, prop);
                break;
            case PropertyAccessorType.Byte:
                Unsafe.AsRef(in _byteGetter) = AcSerializerCommon.CreateTypedGetter<byte>(declaringType, prop);
                break;
            case PropertyAccessorType.Int16:
                Unsafe.AsRef(in _int16Getter) = AcSerializerCommon.CreateTypedGetter<short>(declaringType, prop);
                break;
            case PropertyAccessorType.UInt16:
                Unsafe.AsRef(in _uint16Getter) = AcSerializerCommon.CreateTypedGetter<ushort>(declaringType, prop);
                break;
            case PropertyAccessorType.UInt32:
                Unsafe.AsRef(in _uint32Getter) = AcSerializerCommon.CreateTypedGetter<uint>(declaringType, prop);
                break;
            case PropertyAccessorType.UInt64:
                Unsafe.AsRef(in _uint64Getter) = AcSerializerCommon.CreateTypedGetter<ulong>(declaringType, prop);
                break;
            case PropertyAccessorType.Guid:
                Unsafe.AsRef(in _guidGetter) = AcSerializerCommon.CreateTypedGetter<Guid>(declaringType, prop);
                break;
            case PropertyAccessorType.Enum:
                Unsafe.AsRef(in _int32Getter) = AcSerializerCommon.CreateEnumGetter(declaringType, prop);
                break;
            case PropertyAccessorType.String:
                Unsafe.AsRef(in _stringGetter) = AcSerializerCommon.CreateTypedGetter<string?>(declaringType, prop);
                break;
        }
    }

    #region Typed Getters - Direct invocation, no cast!

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int GetInt32(object obj) => _int32Getter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public long GetInt64(object obj) => _int64Getter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool GetBoolean(object obj) => _boolGetter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public double GetDouble(object obj) => _doubleGetter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public float GetSingle(object obj) => _floatGetter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public decimal GetDecimal(object obj) => _decimalGetter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public DateTime GetDateTime(object obj) => _dateTimeGetter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public byte GetByte(object obj) => _byteGetter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public short GetInt16(object obj) => _int16Getter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public ushort GetUInt16(object obj) => _uint16Getter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public uint GetUInt32(object obj) => _uint32Getter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public ulong GetUInt64(object obj) => _uint64Getter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public Guid GetGuid(object obj) => _guidGetter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int GetEnumAsInt32(object obj) => _int32Getter!(obj);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string? GetString(object obj) => _stringGetter!(obj);

    #endregion
}
