using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace VCommon.VOpenApi.Docgen
{
    public static class TypeExtensionMethod
    {
        public static string GetFriendlyTypeName(this Type type)
        {
            string fullName;
            if (type.IsGenericType)
            {
                var gtd = type.GetGenericTypeDefinition();
                var gargs = type.GetGenericArguments();
                var gtdFullName = gtd.FullName ?? throw new ArgumentNullException(nameof(type), "Missing full name");
                fullName = gtdFullName.Split('`')[0] + "[" + string.Join(",", gargs.Select(p => $"[{p.FullName}]")) + "]";
            }
            else
            {
                fullName = type.FullName ?? throw new ArgumentNullException(nameof(type), "Missing full name");
            }
            return fullName;
        }

        public static bool IsArray(this Type toCheck)
        {
            if (toCheck.IsArray) return true;
            return null != toCheck.GetInterface(nameof(IEnumerable));
        }

        public static bool IsDic(this Type toCheck)
        {
            return toCheck.GetInterfaces().Any(p => p.IsGenericType && p.GetGenericTypeDefinition() == typeof(IDictionary<,>));
        }

        public static Type GetArrayElementType(this Type toGet)
        {
            if (toGet.IsArray) return toGet.GetElementType();

            var ge = toGet.GetInterfaces().FirstOrDefault(p => p.IsGenericType && p.GetGenericTypeDefinition() == typeof(IEnumerable<>));
            return null != ge
                ? ge.GetGenericArguments()[0]
                : typeof(object);
        }

        public static bool IsPrimitiveType(this Type toCheck)
        {
            return toCheck.IsEnum
                   || PrimitiveTypeNames.Contains(toCheck.FullName)
                   || toCheck.IsGenericType && toCheck.GetGenericTypeDefinition() == typeof(Nullable<>) && PrimitiveTypeNames.Contains(toCheck.GetGenericArguments()[0].FullName);
        }

        public static string GetPrimitiveTypeName(this Type type)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)
                ? type.GetGenericArguments()[0].FullName
                : type.FullName;
        }

        private static readonly HashSet<string> PrimitiveTypeNames = new HashSet<string>
        {
            "System.Boolean"
            ,"System.Byte"
            ,"System.SByte"
            ,"System.Int16"
            ,"System.UInt16"
            ,"System.Int32"
            ,"System.UInt32"
            ,"System.Int64"
            ,"System.UInt64"
            ,"System.Single"
            ,"System.Double"
            ,"System.Decimal"
            ,"System.Byte[]"
            ,"System.DateTime"
            ,"System.DateTimeOffset"
            ,"System.Guid"
            ,"System.String"
            ,"System.Object"
        };
    }
}