using System.ComponentModel;
using System.Text;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Volo.Abp.AspNetCore.Mvc;

namespace NPin.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection;

public static class SwaggerAddExtensions
{
    public static IServiceCollection AddNPinSwaggerGen<TProgram>(this IServiceCollection services,
        Action<SwaggerGenOptions>? action = null)
    {
        var serviceProvider = services.BuildServiceProvider();
        var mvcOptions = serviceProvider.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>();

        var mvcSettings = mvcOptions.Value.ConventionalControllers.ConventionalControllerSettings
            .DistinctBy(x => x.RemoteServiceName);

        services.AddAbpSwaggerGen(
            options =>
            {
                action?.Invoke(options);

                // 配置分组,还需要去重,支持重写,如果外部传入后，将以外部为准
                foreach (var setting in mvcSettings.OrderBy(x => x.RemoteServiceName))
                {
                    if (!options.SwaggerGeneratorOptions.SwaggerDocs.ContainsKey(setting.RemoteServiceName))
                    {
                        options.SwaggerDoc(setting.RemoteServiceName,
                            new OpenApiInfo { Title = setting.RemoteServiceName, Version = "v1" });
                    }
                }

                // 根据分组名称过滤 API 文档
                options.DocInclusionPredicate((docName, apiDesc) =>
                {
                    if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
                    {
                        var settingOrNull = mvcSettings
                            .FirstOrDefault(x => x.Assembly == controllerActionDescriptor.ControllerTypeInfo.Assembly);
                        if (settingOrNull is not null)
                        {
                            return docName == settingOrNull.RemoteServiceName;
                        }
                    }

                    return false;
                });

                // options.CustomSchemaIds(type => type.Name);
                options.CustomSchemaIds(type => $"{type.Namespace}.{type.Name}");

                var basePath = Path.GetDirectoryName(typeof(TProgram).Assembly.Location);
                if (basePath is not null)
                {
                    foreach (var item in Directory.GetFiles(basePath, "*.xml"))
                    {
                        options.IncludeXmlComments(item, true);
                    }
                }

                options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme
                {
                    Description = "输入JWT-Token",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.Http,
                    Scheme = "Bearer"
                });

                var schema = new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "JWT" }
                };
                options.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    [schema] = Array.Empty<string>()
                });
                options.OperationFilter<AddRequiredHeaderParameter>();
                options.SchemaFilter<EnumSchemaFilter>();
            });
        return services;
    }
}

/// <summary>
/// Swagger文档枚举字段显示枚举属性和枚举值,以及枚举描述
/// </summary>
public class EnumSchemaFilter : ISchemaFilter
{
    /// <summary>
    /// 实现接口
    /// </summary>
    /// <param name="model"></param>
    /// <param name="context"></param>
    public void Apply(OpenApiSchema model, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            model.Enum.Clear();
            model.Type = "string";
            model.Format = null;


            StringBuilder stringBuilder = new StringBuilder();
            Enum.GetNames(context.Type)
                .ToList()
                .ForEach(name =>
                {
                    Enum e = (Enum)Enum.Parse(context.Type, name);
                    var descrptionOrNull = GetEnumDescription(e);
                    model.Enum.Add(new OpenApiString(name));
                    stringBuilder.Append(
                        $"【枚举：{name}{(descrptionOrNull is null ? string.Empty : $"({descrptionOrNull})")}={Convert.ToInt64(Enum.Parse(context.Type, name))}】<br />");
                });
            model.Description = stringBuilder.ToString();
        }
    }

    private static string? GetEnumDescription(Enum value)
    {
        var fieldInfo = value.GetType().GetField(value.ToString());
        var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : null;
    }
}

public class AddRequiredHeaderParameter : IOperationFilter
{
    public static string HeaderKey { get; set; } = "__tenant";

    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (operation.Parameters == null)
            operation.Parameters = new List<OpenApiParameter>();
        operation.Parameters.Add(new OpenApiParameter
        {
            Name = HeaderKey,
            In = ParameterLocation.Header,
            Required = false,
            AllowEmptyValue = true,
            Description = "租户id或者租户名称（可空，默认租户）"
        });
    }
}