using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text.Json;
using static AyCode.Core.Helpers.JsonUtilities;

namespace AyCode.Core.Serializers.Jsons;

public static partial class AcJsonDeserializer
{
    #region Primitive Reading

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static object? ReadPrimitive(in JsonElement element, in Type targetType, JsonValueKind valueKind)
    {
        var type = Nullable.GetUnderlyingType(targetType) ?? targetType;

        if (valueKind == JsonValueKind.Number)
        {
            var typeCode = Type.GetTypeCode(type);
            return typeCode switch
            {
                TypeCode.Int32 => element.GetInt32(),
                TypeCode.Int64 => element.GetInt64(),
                TypeCode.Double => element.GetDouble(),
                TypeCode.Decimal => element.GetDecimal(),
                TypeCode.Single => element.GetSingle(),
                TypeCode.Byte => element.GetByte(),
                TypeCode.Int16 => element.GetInt16(),
                TypeCode.UInt16 => element.GetUInt16(),
                TypeCode.UInt32 => element.GetUInt32(),
                TypeCode.UInt64 => element.GetUInt64(),
                TypeCode.SByte => element.GetSByte(),
                _ => type.IsEnum ? Enum.ToObject(type, element.GetInt32()) : null
            };
        }
        
        if (valueKind == JsonValueKind.String)
        {
            if (ReferenceEquals(type, StringType)) return element.GetString();
            if (ReferenceEquals(type, DateTimeType)) return element.GetDateTime();
            if (ReferenceEquals(type, GuidType)) return element.GetGuid();
            if (ReferenceEquals(type, DateTimeOffsetType)) return element.GetDateTimeOffset();
            if (ReferenceEquals(type, TimeSpanType)) return TimeSpan.Parse(element.GetString()!, CultureInfo.InvariantCulture);
            if (type.IsEnum) return Enum.Parse(type, element.GetString()!);
            return null;
        }
        
        if (valueKind == JsonValueKind.True) return true;
        if (valueKind == JsonValueKind.False) return false;

        return null;
    }

    #endregion

    #region Primitive Deserialization

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static bool TryDeserializePrimitiveFast(string json, in Type targetType, out object? result)
    {
        var type = Nullable.GetUnderlyingType(targetType) ?? targetType;
        
        // Handle enums first
        if (type.IsEnum)
        {
            if (json.Length > 0 && json[0] == '"')
            {
                using var doc = JsonDocument.Parse(json);
                result = Enum.Parse(type, doc.RootElement.GetString()!);
            }
            else result = Enum.ToObject(type, int.Parse(json, CultureInfo.InvariantCulture));
            return true;
        }
        
        var typeCode = Type.GetTypeCode(type);
        
        switch (typeCode)
        {
            case TypeCode.Int32: result = int.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.Int64: result = long.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.Boolean: result = json == "true"; return true;
            case TypeCode.Double: result = double.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.Decimal: result = decimal.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.Single: result = float.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.String:
                // If already unwrapped (no quotes), return as-is; otherwise parse JSON
                if (json.Length == 0 || json[0] != '"')
                {
                    result = json;
                    return true;
                }
                using (var doc = JsonDocument.Parse(json))
                {
                    result = doc.RootElement.GetString();
                    return true;
                }
            case TypeCode.DateTime:
                // If already unwrapped (no quotes), parse directly; otherwise use JSON parser
                if (json.Length == 0 || json[0] != '"')
                {
                    result = DateTime.Parse(json, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
                    return true;
                }
                using (var doc = JsonDocument.Parse(json))
                {
                    result = doc.RootElement.GetDateTime();
                    return true;
                }
            case TypeCode.Byte: result = byte.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.Int16: result = short.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.UInt16: result = ushort.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.UInt32: result = uint.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.UInt64: result = ulong.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.SByte: result = sbyte.Parse(json, CultureInfo.InvariantCulture); return true;
            case TypeCode.Char:
                if (json.Length == 0 || json[0] != '"')
                {
                    result = json.Length > 0 ? json[0] : '\0';
                    return true;
                }
                using (var doc = JsonDocument.Parse(json))
                {
                    var s = doc.RootElement.GetString();
                    result = s?.Length > 0 ? s[0] : '\0';
                    return true;
                }
        }
        
        if (ReferenceEquals(type, GuidType))
        {
            // If already unwrapped (no quotes), parse directly
            if (json.Length == 0 || json[0] != '"')
            {
                result = Guid.Parse(json);
                return true;
            }
            using var doc = JsonDocument.Parse(json);
            result = doc.RootElement.GetGuid();
            return true;
        }
        
        if (ReferenceEquals(type, DateTimeOffsetType))
        {
            // If already unwrapped (no quotes), parse directly
            if (json.Length == 0 || json[0] != '"')
            {
                result = DateTimeOffset.Parse(json, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
                return true;
            }
            using var doc = JsonDocument.Parse(json);
            result = doc.RootElement.GetDateTimeOffset();
            return true;
        }
        
        if (ReferenceEquals(type, TimeSpanType))
        {
            // If already unwrapped (no quotes), parse directly
            if (json.Length == 0 || json[0] != '"')
            {
                result = TimeSpan.Parse(json, CultureInfo.InvariantCulture);
                return true;
            }
            using var doc = JsonDocument.Parse(json);
            result = TimeSpan.Parse(doc.RootElement.GetString()!, CultureInfo.InvariantCulture);
            return true;
        }
        
        result = null;
        return false;
    }

    #endregion
}
