using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Autofac;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using PoJun.MongoDB.Repository;
using ASHermed.SinoInterface.Tools;
using ASHermed.SinoInterface.WebApi.Filters;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;
using Polly;
using System.Globalization;
using Microsoft.AspNetCore.Localization;
using PoJun.Util.Helpers;
using Ashermed.BaseFramework;
using Microsoft.AspNetCore.Http.Features;
using ASHermed.SinoInterface.IFramework;
using Autofac.Extensions.DependencyInjection;

namespace ASHermed.SinoInterface.WebApi
{
    /// <summary>
    /// Startup
    /// </summary>
    public class Startup
    {
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="configuration"></param>
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        /// <summary>
        /// Configuration
        /// </summary>
        public IConfiguration Configuration { get; }

        /// <summary>
        /// Api版本信息
        /// </summary>
        private IApiVersionDescriptionProvider Provider;

        /// <summary>
        /// 环境
        /// </summary>
        private readonly static List<string> Env = new List<string>() { "demo", "prod" };

        /// <summary>
        /// This method gets called by the runtime. Use this method to add services to the container.
        /// </summary>
        /// <param name="services"></param>
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            #region HttpClient注入
            services.AddHttpClient(HttpClientConst.TCService_CLIENT_NAME, cfg =>
            {
                var baseUrl = Configuration["AppSettings:TCBaseAddress"];
                cfg.BaseAddress = new Uri(baseUrl);
                cfg.Timeout = new TimeSpan(0, 0, 10, 0);
            });
            services.AddHttpClient(HttpClientConst.AdminBase_CLIENT_NAME, cfg =>
            {
                var baseUrl = Configuration["AppSettings:AdminBaseAddress"];
                cfg.BaseAddress = new Uri(baseUrl);
                cfg.Timeout = new TimeSpan(0, 0, 10, 0);
            });
            services.AddHttpClient(HttpClientConst.CFFileDown_CLIENT_NAME, cfg =>
            {
                var baseUrl = Configuration["AppSettings:CFFileDownAddress"];
                cfg.BaseAddress = new Uri(baseUrl);
                cfg.Timeout = new TimeSpan(0, 0, 10, 0);
            });

            #endregion
            //注册MongoDB仓储
            RepositoryContainer.RegisterAll(AutofacModuleRegister.GetAllAssembliesName());
            services.AddControllers();

            services.AddMvc(option =>
            {

                //option.Filters.Add(typeof(PatUserTokenAttribute));
                //option.Filters.Add(typeof(AuthenticationAttribute));
                option.Filters.Add(typeof(ExceptionLogAttribute));
                option.Filters.Add(typeof(RequestLogAttribute));
                option.Filters.Add(typeof(ResponseLogAttribute));
                //option.Filters.Add<VersionAttribute>();
                option.MaxModelValidationErrors = 100;
            })
            .AddNewtonsoftJson(option =>
            {
                option.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                //忽略循环引用
                option.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                //驼峰格式
                //option.SerializerSettings.ContractResolver = APIConfig.GetInstance().IsLittleCamelFormat ? new CamelCasePropertyNamesContractResolver() : new DefaultContractResolver();
                //增加参数自动去除前后空格转换器
                option.SerializerSettings.Converters.Add(new TrimmingConverter());
            });

            #region 解决跨域

            //解决跨域（如果不用可以注释掉）
            services.AddCors(options =>
            {
                options.AddPolicy("EnableCrossDomain", builder =>
                {
                    builder.AllowAnyOrigin()//允许任何来源的主机访问
                                            //builder.WithOrigins(APIConfig.GetInstance().RequestSource)
                    .AllowAnyMethod()
                    .AllowAnyHeader();
                    //.AllowCredentials();//指定处理cookie
                });
            });

            #endregion

            services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            #region 接口版本控制

            services.AddApiVersioning(options =>
                {
                    //当设置为 true 时, API 将返回响应标头中支持的版本信息
                    options.ReportApiVersions = true;
                    //此选项将用于不提供版本的请求。默认情况下, 假定的 API 版本为1.0。
                    options.AssumeDefaultVersionWhenUnspecified = true;
                    //此选项用于指定在请求中未指定版本时要使用的默认 API 版本。这将默认版本为1.0。
                    options.DefaultApiVersion = new Microsoft.AspNetCore.Mvc.ApiVersion(1, 0);
                })
                .AddVersionedApiExplorer(options =>
                {
                    // 版本名的格式：v+版本号
                    options.GroupNameFormat = "'v'V";
                    options.AssumeDefaultVersionWhenUnspecified = true;
                });

            #endregion

            #region 注册Swagger服务

            if (!Env.Contains(APIConfig.GetInstance().Environment))
            {
                services.AddSwaggerGen(c =>
                {
                    //多版本控制
                    foreach (var item in Provider.ApiVersionDescriptions)
                    {
                        //添加文档信息
                        c.SwaggerDoc(item.GroupName, new OpenApiInfo
                        {
                            Title = ".Net Core 标准开发框架",
                            Version = item.ApiVersion.ToString(),
                            Description = "ASP.NET CORE 3.1 WebApi",
                            Contact = new OpenApiContact
                            {
                                Name = "杨江军",
                                Email = "jiangjun.yang@ashermed.com"
                            }
                        });
                    }

                    //c.DocumentFilter<SwaggerEnumFilter>();
                    //if (SingleSignOnConfig.GetInstance().IsEnable)
                    //{
                    //    c.OperationFilter<AddSwaggerHeaderParameterFilter>();
                    //}

                    #region 读取xml信息

                    //启用xml注释. 该方法第二个参数启用控制器的注释，默认为false.
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"), true);
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"ASHermed.SinoInterface.Api.ContractModel.xml"), true);
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"ASHermed.SinoInterface.ContractModel.xml"), true);
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"ASHermed.SinoInterface.Enum.xml"), true);
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"ASHermed.SinoInterface.Entity.xml"), true);
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"ASHermed.SinoInterface.Code.xml"), true);
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"ASHermed.SinoInterface.Api.IService.xml"), true);
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"ASHermed.SinoInterface.Api.Service.xml"), true);

                    #endregion
                });
            }

            #endregion

            //注册权限验证
            //services.AddScoped<AuthenticationAttribute>();
            services.AddScoped<PatUserTokenAttribute>();
            services.AddScoped<CignaUserTokenAttribute>();
            services.AddScoped<VersionAttribute>();

            services.AddScoped<TokenIgnoreFilter>();

            #region Quartz调度框架注册

            //注入Quartz调度类（如果不用可以注释掉）
            services.AddSingleton<QuartzStartup>();
            services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
            services.AddSingleton<IJobFactory, IOCJobFactory>();

            #endregion

            #region 自定义job注册

            //注入自定义job
           
            #endregion

            services.Configure<ApiBehaviorOptions>(options =>
            {
                //禁用.net core webapi 项目本身的模型参数绑定验证体系
                options.SuppressModelStateInvalidFilter = true;
            });
            services.AddHttpClient();
            services.AddHttpClient("base").AddTransientHttpErrorPolicy(x => x.RetryAsync(3));//添加重试策略。 若请求失败，最多可重试三次;

            services.Configure<FormOptions>(options => { options.KeyLengthLimit = int.MaxValue; });

            services.AddMemoryCache();
            //注入 HttpClientHelp（如果不用可以注释掉）
            services.AddTransient<HttpClientHelp>();//框架带的
            services.AddTransient<AppConfigurtaion>();
            services.AddSingleton<RedisHelperClient>();
            services.AddTransient<HttpClientHelper>();//后加的好用
            services.AddTransient<MemoryCacheHelper>();
            //services.AddSingleton<RabbitMQConsumerDelay>();
            //services.AddSingleton<RabbitMQConsumer>();
            //services.AddSingleton<RabbitMQHeartbeatMonitoring>();

            return RegisterAutofac(services);//注册Autofac
        }

        #region 注册Autofac

        /// <summary>
        /// 注册Autofac
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        private IServiceProvider RegisterAutofac(IServiceCollection services)
        {
            //实例化Autofac容器
            var builder = new ContainerBuilder();
            //将Services中的服务填充到Autofac中
            builder.Populate(services);
            //获取接口版本信息
            this.Provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
            //新模块组件注册    
            builder.RegisterModule<AutofacModuleRegister>();
            //创建容器
            var Container = builder.Build();
            //第三方IOC接管 core内置DI容器 
            return new AutofacServiceProvider(Container);
        }

        #endregion

        /// <summary>
        /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        /// <param name="httpContextAccessor"></param>
        public async void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpContextAccessor httpContextAccessor)
        {
            #region 解决docker中时间类型ToString后变为3/12/2020 10:16:00格式的问题

            var zh = new CultureInfo("zh-CN");
            zh.DateTimeFormat.FullDateTimePattern = "yyyy-MM-dd HH:mm:ss";
            zh.DateTimeFormat.LongDatePattern = "yyyy-MM-dd";
            zh.DateTimeFormat.LongTimePattern = "HH:mm:ss";
            zh.DateTimeFormat.ShortDatePattern = "yyyy-MM-dd";
            zh.DateTimeFormat.ShortTimePattern = "HH:mm:ss";
            var supportedCultures = new List<CultureInfo>
            {
               zh,
            };

            app.UseRequestLocalization(new RequestLocalizationOptions
            {
                //这里指定默认语言包
                DefaultRequestCulture = new RequestCulture("zh-CN"),
                SupportedCultures = supportedCultures,
                SupportedUICultures = supportedCultures
            });

            #endregion


            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            MyHttpContext.HttpContextAccessor = httpContextAccessor;

            #region 解决跨域

            //解决跨域（如果不用可以注释掉）
            app.UseCors("EnableCrossDomain");

            #endregion

            app.UseCors();
            //app.UseStaticFiles();//注册wwwroot静态文件（如果不用可以注释掉）            
            app.UseRouting();
            //app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            #region 启动Job

            //执行数据导入定时同步Job
            //var quartz = app.ApplicationServices.GetRequiredService<QuartzStartup>();  //生成cron: https://cron.qqe2.com/

            #endregion

            #region 启用Swagger

            if (!Env.Contains(APIConfig.GetInstance().Environment))
            {
                //启用Swagger中间件
                app.UseSwagger();
                //配置SwaggerUI
                app.UseSwaggerUI(c =>
                {
                    foreach (var item in Provider.ApiVersionDescriptions)
                    {
                        c.SwaggerEndpoint($"/swagger/{item.GroupName}/swagger.json", SysUtil.GetSystemId() + item.ApiVersion);
                    }
                });
            }

            #endregion
        }
    }
}
