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

namespace AyCode.Core.Serializers.Jsons;

/// <summary>
/// JSON-specific property setter base class.
/// Adds typed setters for common primitives to avoid boxing during deserialization.
/// </summary>
public abstract class JsonPropertySetterBase : PropertySetterBase
{
    // Pre-boxed boolean values to avoid repeated boxing
    private static readonly object BoxedTrue = true;
    private static readonly object BoxedFalse = false;

    // Typed setters for common primitive types (avoid boxing)
    internal readonly Action<object, int>? _setInt32;
    internal readonly Action<object, long>? _setInt64;
    internal readonly Action<object, double>? _setDouble;
    internal readonly Action<object, bool>? _setBool;
    internal readonly Action<object, decimal>? _setDecimal;
    internal readonly Action<object, float>? _setSingle;
    internal readonly Action<object, DateTime>? _setDateTime;
    internal readonly Action<object, Guid>? _setGuid;

    protected JsonPropertySetterBase(PropertyInfo prop, Type declaringType) 
        : base(prop, declaringType)
    {
        // Create typed setters for common primitives to avoid boxing
        if (!IsNullable)
        {
            if (ReferenceEquals(PropertyType, IntType))
                _setInt32 = AcSerializerCommon.CreateTypedSetter<int>(declaringType, prop);
            else if (ReferenceEquals(PropertyType, LongType))
                _setInt64 = AcSerializerCommon.CreateTypedSetter<long>(declaringType, prop);
            else if (ReferenceEquals(PropertyType, DoubleType))
                _setDouble = AcSerializerCommon.CreateTypedSetter<double>(declaringType, prop);
            else if (ReferenceEquals(PropertyType, BoolType))
                _setBool = AcSerializerCommon.CreateTypedSetter<bool>(declaringType, prop);
            else if (ReferenceEquals(PropertyType, DecimalType))
                _setDecimal = AcSerializerCommon.CreateTypedSetter<decimal>(declaringType, prop);
            else if (ReferenceEquals(PropertyType, FloatType))
                _setSingle = AcSerializerCommon.CreateTypedSetter<float>(declaringType, prop);
            else if (ReferenceEquals(PropertyType, DateTimeType))
                _setDateTime = AcSerializerCommon.CreateTypedSetter<DateTime>(declaringType, prop);
            else if (ReferenceEquals(PropertyType, GuidType))
                _setGuid = AcSerializerCommon.CreateTypedSetter<Guid>(declaringType, prop);
        }
    }

    /// <summary>
    /// Try to set a boolean value without boxing.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TrySetBoolean(object target, bool value)
    {
        if (_setBool != null)
        {
            _setBool(target, value);
            return true;
        }
        _setter(target, value ? BoxedTrue : BoxedFalse);
        return true;
    }

    /// <summary>
    /// Try to set an int32 value without boxing.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TrySetInt32(object target, int value)
    {
        if (_setInt32 != null)
        {
            _setInt32(target, value);
            return true;
        }
        return false;
    }

    /// <summary>
    /// Try to set an int64 value without boxing.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TrySetInt64(object target, long value)
    {
        if (_setInt64 != null)
        {
            _setInt64(target, value);
            return true;
        }
        return false;
    }

    /// <summary>
    /// Try to set a double value without boxing.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TrySetDouble(object target, double value)
    {
        if (_setDouble != null)
        {
            _setDouble(target, value);
            return true;
        }
        return false;
    }

    /// <summary>
    /// Try to set a decimal value without boxing.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TrySetDecimal(object target, decimal value)
    {
        if (_setDecimal != null)
        {
            _setDecimal(target, value);
            return true;
        }
        return false;
    }

    /// <summary>
    /// Try to set a float value without boxing.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TrySetSingle(object target, float value)
    {
        if (_setSingle != null)
        {
            _setSingle(target, value);
            return true;
        }
        return false;
    }

    /// <summary>
    /// Try to set a DateTime value without boxing.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TrySetDateTime(object target, DateTime value)
    {
        if (_setDateTime != null)
        {
            _setDateTime(target, value);
            return true;
        }
        return false;
    }

    /// <summary>
    /// Try to set a Guid value without boxing.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TrySetGuid(object target, Guid value)
    {
        if (_setGuid != null)
        {
            _setGuid(target, value);
            return true;
        }
        return false;
    }
}
