using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using ReallifeGamemode.Server.Types;
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;

namespace ReallifeGamemode.Server.Common
{
  public static class TypeExtensions
  {
    private static readonly JsonSerializerSettings settings = new JsonSerializerSettings()
    {
      NullValueHandling = NullValueHandling.Ignore,
      DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,
      DateTimeZoneHandling = DateTimeZoneHandling.Utc,
      DateParseHandling = DateParseHandling.DateTime
    };

    static TypeExtensions()
    {
      settings.Converters.Add(new StringEnumConverter());
    }

    public static string SerializeJson(this object obj)
    {
      if (obj == null)
      {
        throw new ArgumentNullException(nameof(obj));
      }

      return JsonConvert.SerializeObject(obj, settings);
    }

    public static T DeserializeJson<T>(this string str)
    {
      if (str == null)
      {
        throw new ArgumentNullException(nameof(str));
      }

      return JsonConvert.DeserializeObject<T>(str, settings);
    }

    public static bool IsNullOrEmpty(this string str)
    {
      if (str == null)
      {
        return true;
      }

      if (str.Trim().Length == 0)
      {
        return true;
      }

      return false;
    }

    public static bool IsNotNullOrEmpty(this string str) => !str.IsNullOrEmpty();

    public static T GetEnumAttribute<T>(this Enum @enum) where T : Attribute
    {
      var type = @enum?.GetType();
      var memberInfo = type?.GetMember(@enum.ToString()).First();
      return memberInfo?.GetCustomAttribute<T>();
    }

    public static string GetValue(this Enum @enum)
    {
      return @enum?.GetEnumAttribute<EnumMemberAttribute>()?.Value;
    }

    public static string GetPrefix(this ChatPrefix prefix)
    {
      return prefix.GetValue();
    }

    public static bool TryParseEnum(this string str, Type type, out object @enum)
    {
      var members = Enum.GetValues(type).Cast<Enum>();
      var directEnumMember = members.Where(e => e.ToString().ToLower() == str.ToLower()).FirstOrDefault();

      if (directEnumMember != null)
      {
        @enum = directEnumMember;
        return true;
      }

      var memberValueMember = members.Where(e => e.GetValue()?.ToLower() == str.ToLower()).FirstOrDefault();

      if (memberValueMember != null)
      {
        @enum = memberValueMember;
        return true;
      }

      var nativeResult = Enum.TryParse(type, str, true, out var result);
      @enum = result ?? (default);
      return nativeResult;
    }

    public static long ToLong(this object obj)
    {
      return long.Parse(obj.ToString());
    }

    public static int ToInt(this object obj)
    {
      return (int)obj.ToLong();
    }
  }
}
