using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace YGNT.Exam.Web.Host.Extension
{
    /// <summary>
    /// Add enum value descriptions to Swagger
    /// </summary>
    public class SwaggerEnumFilter : IDocumentFilter
    {

        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        //public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
        {
            Dictionary<string, Type> dict = GetAllEnum();

            foreach (var item in swaggerDoc.Components.Schemas)
            //foreach (var item in swaggerDoc.Definitions)
            {
                var property = item.Value;
                var typeName = item.Key;
                if (property.Enum != null && property.Enum.Count > 0)
                {
                    Type itemType = null;
                    itemType = dict.ContainsKey(key: typeName) ? dict[key: typeName] : null;
                    List<OpenApiInteger> list = new List<OpenApiInteger>();
                    foreach (var val in property.Enum)
                    {
                        list.Add(item: (OpenApiInteger)val);
                    }
                    property.Description += DescribeEnum(type: itemType, enums: list);
                }
            }
        }
        private static Dictionary<string, Type> GetAllEnum()
        {
            var list = new List<string>()
            {
                "YGNT.QuestionLibrary.Core",
                "YGNT.QuestionLibrary.Application",
                "YGNT.Exam.Application",
                "YGNT.Exam.Core",
            };
            Dictionary<string, Type> dict = new Dictionary<string, Type>();
            foreach (var assembly in list)
            {
                Assembly ass = Assembly.Load(assemblyString: assembly);
                Type[] types = ass.GetTypes();

                foreach (Type item in types)
                {
                    if (item.IsEnum)
                    {
                        //不要定义多个名称相同的枚举
                        dict.Add(key: item.Name, value: item);
                    }
                }
            }
            return dict;
        }

        private static string DescribeEnum(Type type, List<OpenApiInteger> enums)
        {
            var enumDescriptions = new List<string>();
            foreach (var item in enums)
            {
                if (type == null) continue;
                var value = Enum.Parse(enumType: type, value: item.Value.ToString());
                var desc = GetDescription(t: type, value: value);

                if (string.IsNullOrEmpty(value: desc))
                    enumDescriptions.Add(item: $"{item.Value}:{Enum.GetName(enumType: type, value: value)}; ");
                else
                    enumDescriptions.Add(item: $"{item.Value}:{Enum.GetName(enumType: type, value: value)},{desc}; ");

            }
            return $"<br/>{Environment.NewLine}{string.Join(separator: "<br/>" + Environment.NewLine, values: enumDescriptions)}";
        }

        private static string GetDescription(Type t, object value)
        {
            foreach (MemberInfo mInfo in t.GetMembers())
            {
                if (mInfo.Name == t.GetEnumName(value: value))
                {
                    foreach (Attribute attr in Attribute.GetCustomAttributes(element: mInfo))
                    {
                        if (attr.GetType() == typeof(DescriptionAttribute))
                        {
                            return ((DescriptionAttribute)attr).Description;
                        }
                    }
                }
            }
            return string.Empty;
        }
    }
}