using System.Globalization;
using System.Text.Json.Serialization;
using System.Text.Json;
using System;
using System.Text.RegularExpressions;

namespace Kit.Helpers
{
    using System.Text.Encodings.Web;
    using System.Text.Json;
    using System.Text.Unicode;
    using System.Threading.Tasks;

    public static class JsonHelper
    {
        public static TObject? JsonDeserialize<TObject>(this string json)
        {
            if (json == null || string.IsNullOrWhiteSpace(json)) return default(TObject);
            var options = new JsonSerializerOptions();
            options.Converters.Add(new DateTimeIsoFormatConverter());
            options.PropertyNameCaseInsensitive = true;
            return JsonSerializer.Deserialize<TObject>(json, options);
        }

        public static string JsonSerialize<TObject>(this TObject serializeObject, bool enableCyrillic = false, bool includeFields = false, bool writeIntended = false, JsonNamingPolicy? propertyNamingPolicy = null)
        {
            if (serializeObject == null) return "{}";

            var options = new JsonSerializerOptions
            {
                IncludeFields = includeFields,
                Encoder = enableCyrillic ? JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic) : JavaScriptEncoder.Create(UnicodeRanges.BasicLatin),
                Converters = { new DateTimeIsoFormatConverter() },
                WriteIndented = writeIntended,
            };

            if (propertyNamingPolicy != null)
            {
                options.PropertyNamingPolicy = propertyNamingPolicy;
            }

            string result = JsonSerializer.Serialize(serializeObject, options);

            return result;
        }

        public static async Task<TObject> JsonDeserialize<TObject>(this Task<string> task)
        {
            string json = await task.ConfigureAwait(false);
            return await Task<TObject>.Run(() => json.JsonDeserialize<TObject>()).ConfigureAwait(false);
        }
    }
}
public class DateTimeIsoFormatConverter : JsonConverter<DateTime>
{
    private readonly string format = "yyyy-MM-ddTHH:mm";

    public DateTimeIsoFormatConverter() { }

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string item = reader.GetString();

        if (DateTime.TryParseExact(item, this.format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime result))
        {
            return result;
        }

        for (int i = 1; i < 10; i++)
        {
            string format_item = "yyyy-MM-ddTHH:mm:ss." + new string('f', i);
            if (DateTime.TryParseExact(item, format_item, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime result_item))
            {
                return result_item;
            }
            if (DateTime.TryParseExact(item, format_item + "Z", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out result_item))
            {
                return result_item;
            }
            if (DateTime.TryParseExact(item, format_item + "zzz", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out result_item))
            {
                return result_item;
            }
        }

        Match match = Regex.Match(item, @"\/Date\((\d+)\)\/");
        if (match.Success)
        {
            long milliseconds = long.Parse(match.Groups[1].Value);
            return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(milliseconds);
        }

        throw new FormatException();
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        ArgumentNullException.ThrowIfNull(writer, nameof(writer));

        writer.WriteStringValue(value
            .ToUniversalTime()
            .ToString(
                this.format,
                CultureInfo.InvariantCulture));
    }
}