using CloudBuilder.Core.Application.Builders;
using CloudBuilder.Core.Application.Options;
using CloudBuilder.Core.Application.Packs;
using CloudBuilder.Core.Authorization;
using CloudBuilder.Core.Extensions;
using CloudBuilder.Core.Policy;
using CloudBuilder.Core.Service;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.IdentityModel.Tokens;
using System.Text;
/*
使用JWT：
1.认证(AddAuthentication)
1.1开启Bearer认证
1.2注册JwtBearer服务
3.开启中间件(app.UseAuthentication())
4.实现IAuthenticationService接口认证服务
5.实现IAuthorizationPermissionService接口授权服务

2.授权(AddAuthorization)
2.1无状态授权[Authorize]
1.2有状态授权[Authorize["Permission"]] AddAuthorization注册自定义授权

*/

namespace CloudBuilder.Core.DependencyInjection.Authorization
{
    [PackLevel(45)]
    public class AuthorizationPack : CloudBuilderPack
    {
        /// <summary>
        /// 获取 模块级别
        /// </summary>
        public override PackLevel Level => PackLevel.Framework;

        /// <summary>
        /// 获取 模块启动顺序，模块启动的顺序先按级别启动，级别内部再按此顺序启动
        /// </summary>
        public override int Order => 2;

        /// <summary>
        /// 将服务添加到依赖注入服务容器中
        /// </summary>
        /// <param name="services">依赖注入服务容器</param>
        /// <returns></returns>
        public override IServiceCollection AddServices(IServiceCollection services, CloudBuilderBuilder builder)
        {
            /*策略授权
              1.导入
                Microsoft.AspNetCore.Authorization
                Microsoft.AspNetCore.Authentication.JwtBearer
              2.具体实现逻辑交由外部的PermissionServices:IAuthorizationPermissionService来处理
              */

            CloudBuilderOptions section = builder.CloudBuilderOptions;
            if (section.Jwt == null) return services;

            var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(section.Jwt.Secret)), SecurityAlgorithms.HmacSha256);

            // 角色与接口的权限要求参数
            var permissionRequirement = new PermissionRequirement();

            //初始化认证信息
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;

                //Token Validation Parameters
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ClockSkew= TimeSpan.Zero,
                    //通常，验证库(至少是MS之一)可以补偿时钟偏斜。 ClockSkew的默认值为5分钟。
                    ValidateIssuerSigningKey = true,
                    //获取或设置要使用的Microsoft.IdentityModel.Tokens.SecurityKey用于签名验证。
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(section.Jwt.Secret)),
                    //获取或设置一个System.String，它表示将使用的有效发行者检查代币的发行者。
                    ValidIssuer = section.Jwt.Issuer,
                    //获取或设置一个字符串，该字符串表示将用于检查的有效受众反对令牌的观众。
                    ValidAudience = section.Jwt.Audience,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    NameClaimType = ClaimPolicy.CLAINM_USERID,
                    RoleClaimType = ClaimPolicy.ROLE,                    
                };
            });

            //启用全局授权(所有的Controller就无需加入Authorize属性，如果加入就会重复执行授权)
            if (section.EnableGlobalAuthorize)
            {
                services.Configure<MvcOptions>(options =>
                {
                    options.Filters.Add(new AuthorizeFilter());
                });
            }

            //初始化授权信息(使用自定义复杂的授权)
            services.AddAuthorization(options =>
            {
                options.AddPolicy(AuthorizePolicy.PERMISSION, policy => policy.Requirements.Add(permissionRequirement));
            });

            var baseType = typeof(IAuthorize);

            var allAssemblies = CloudBuilderBuilder.GetAssemblies();

            var types = allAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(type => type != baseType && type.HasImplementedRawGeneric(baseType))
                .Where(x => x.IsClass).ToList();

            foreach (var implementType in types)
            {
                services.AddScoped(implementType.GetInterfaces().FirstOrDefault(), implementType);
            }

            services.AddSingleton(new JwtGenerate(section.Jwt));

            return services;
        }

    }
}