using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using System.Web.Http;
using System.Web.Http.Description;
using System.Web.Http.Filters;
using System.Web.Http.Metadata;
using System.Web.Http.Routing;
using System.Web.Http.Validation;
using System.Web.Http.Validation.Providers;
using System.Xml;
using System.Xml.Linq;
using Autofac;
using Autofac.Integration.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Cors;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Owin;
using Swashbuckle.Application;
using Swashbuckle.Swagger;
using Autofac.Integration.WebApi;
using Autofac.Core;
using Microsoft.Owin.FileSystems;
using Microsoft.Owin.StaticFiles;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Web.Http.Controllers;
using Microsoft.Owin.Hosting;
[assembly: OwinStartup(typeof(XdCxRhDW.WebApi.Startup))]
namespace XdCxRhDW.WebApi
{
    /// 
    /// WebApi启动类
    /// 
    public class Startup
    {
        private static List svrs = new List();
        private static List configs = new List();
        private static string _controllerXmlName { get; set; }
        private static string _dtoXmlName { get; set; }
        internal static string _timeZoneUtc;
        /// 
        /// 启动http服务,会自动关闭之前启动的服务
        /// 
        /// 
        /// controller所在程序集xml文件名称
        /// dto所在程序集xml文件名称
        /// 时区
        public static void Start(int port, string controllerXmlName, string dtoXmlName, string timeZoneUtc = "UTC+08:00")
        {
            //不要删除这句代码,VS引用优化检测到没有使用Microsoft.Owin.Host.HttpListener.dll则可能不会将此dll复制到本地导致http服务启动失败
            Console.WriteLine(typeof(Microsoft.Owin.Host.HttpListener.OwinHttpListener));
            _timeZoneUtc = timeZoneUtc;
            _controllerXmlName = controllerXmlName;
            _dtoXmlName = dtoXmlName;
            foreach (var item in svrs)
            {
                try
                {
                    item.Dispose();
                }
                catch
                {
                }
            }
            foreach (var item in configs)
            {
                try
                {
                    item.Filters.Clear();
                    item.Services.Dispose();
                    item.Routes.Dispose();
                    item.Formatters.Clear();
                    item.Dispose();
                }
                catch
                {
                }
            }
            svrs.Clear();
            configs.Clear();
            StartOptions options = new StartOptions();
            options.Urls.Add($"http://+:{port}");
            var svr = WebApp.Start(options);
            svrs.Add(svr);
            GC.Collect();
        }
        /// 
        /// 
        /// 
        /// 
        public void Configuration(IAppBuilder app)
        {
            //启用目录浏览和静态文件
            Directory.CreateDirectory("wwwroot");
            var physicalFileSystem = new PhysicalFileSystem(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot"));//目录浏览物理地址
            var options = new FileServerOptions
            {
                RequestPath = new PathString("/wwwroot"),//目录浏览地址
                EnableDefaultFiles = true,
                EnableDirectoryBrowsing = true,//启用目录浏览
                FileSystem = physicalFileSystem
            };
            options.StaticFileOptions.FileSystem = physicalFileSystem;
            options.StaticFileOptions.ServeUnknownFileTypes = true;//允许下载wwwroot中的所有类型文件
            app.UseFileServer(options);
            HttpConfiguration config = new HttpConfiguration();
            configs.Add(config);
            IEnumerable modelValidatorProviders = config.Services.GetModelValidatorProviders();
            DataAnnotationsModelValidatorProvider provider = (DataAnnotationsModelValidatorProvider)
                    modelValidatorProviders.Single(x => x is DataAnnotationsModelValidatorProvider);
            //var provider2 = (DataMemberModelValidatorProvider)
            //  modelValidatorProviders.Single(x => x is DataMemberModelValidatorProvider);
            provider.RegisterDefaultValidatableObjectAdapter(typeof(CustomModelValidator));
            JsonSerializerSettings setting = new JsonSerializerSettings()
            {
                //日期类型默认格式化处理
                DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,
                DateFormatString = "yyyy-MM-dd HH:mm:ss.fff",
                //驼峰样式
                //ContractResolver = new CamelCasePropertyNamesContractResolver(),
                //空值处理
                //NullValueHandling = NullValueHandling.Ignore,
                //设置序列化的最大层数
                MaxDepth = 10,
                //解决json序列化时的循环引用问题
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            };
            config.Formatters.JsonFormatter.SerializerSettings = setting;
            config.Formatters.Remove(config.Formatters.XmlFormatter);
            config.Filters.Add(new HandlerErrorAttribute());
            config.Filters.Add(new ValidateFilter());
            //config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
            config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
            //config.Routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
            //config.Routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new { action = "Post" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) });
            ConfigureSwagger(config);
            //添加路由路径
            config.MapHttpAttributeRoutes();
            var builder = new ContainerBuilder();
            var controllerAssemblys = AppDomain.CurrentDomain.GetAssemblies().Where(p =>
                p.GetTypes().Any(t => t.BaseType == typeof(BaseController))).ToArray();
            builder.RegisterApiControllers(controllerAssemblys);
            var serviceTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(p => p.GetTypes())
                .Where(p => p.Namespace != null && p.Namespace.EndsWith(".Service")).ToList();
            foreach (var serviceType in serviceTypes)
            {
                //单例模式注入Service
                builder.RegisterType(serviceType).SingleInstance();
            }
            var container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
            GlobalConfiguration.Configuration.DependencyResolver = config.DependencyResolver;
            AutofacUtil.Container = container;
            //app.UseAutofacLifetimeScopeInjector(container);
            app.UseAutofacMiddleware(container);
            app.UseAutofacWebApi(config);
            app.UseCors(CorsOptions.AllowAll);
            app.UseWebApi(config);
        }
        private static void ConfigureSwagger(HttpConfiguration config)
        {
            var thisAssembly = typeof(Startup).Assembly;
            string exeName = Assembly.GetEntryAssembly().GetName().Name;
            config.EnableSwagger(c =>
             {
                 c.IgnoreObsoleteActions();//忽略过时的方法
                 c.IgnoreObsoleteProperties();//忽略过时的属性
                 c.PrettyPrint();//漂亮缩进
                 c.SingleApiVersion("v1", $"{exeName}Http接口");
                 c.ApiKey("123456");
                 var webApiXmlPath0 = $"{AppDomain.CurrentDomain.BaseDirectory}{System.Reflection.Assembly.GetAssembly(typeof(Startup)).GetName().Name}.xml";
                 c.IncludeXmlComments(webApiXmlPath0);//WebApi模型描述
                 var webApiXmlPath1 = $"{AppDomain.CurrentDomain.BaseDirectory}{Path.GetFileNameWithoutExtension(_dtoXmlName)}.xml";
                 c.IncludeXmlComments(webApiXmlPath1);//dto模型描述
                 var webApiXmlPath2 = $"{AppDomain.CurrentDomain.BaseDirectory}{Path.GetFileNameWithoutExtension(_controllerXmlName)}.xml";
                 c.IncludeXmlComments(webApiXmlPath2);//控制器中方法描述
                 var webApiXmlPath3 = $"{AppDomain.CurrentDomain.BaseDirectory}{typeof(AjaxResult).Assembly.GetName().Name}.xml";
                 c.IncludeXmlComments(webApiXmlPath3);//返回值描述
                 //控制器本身描述
                 string controllerXmlPath1 = $"{AppDomain.CurrentDomain.BaseDirectory}{Path.GetFileNameWithoutExtension(_controllerXmlName)}.xml";
                 c.CustomProvider(defaultProvider => new SwaggerControllerDescProvider(defaultProvider,new string[] { webApiXmlPath0, controllerXmlPath1 }));
                 c.OperationFilter();
                 c.SchemaFilter();
                 c.SchemaFilter();
             })
             .EnableSwaggerUi(c =>
             {
                 c.InjectJavaScript(thisAssembly, $"{thisAssembly.GetName().Name}.Swagger.js");
                 //c.DocumentTitle($"{exeName}Http接口");
             });
        }
        class FileUploadOperation : IOperationFilter
        {
            public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
            {
                if (operation.parameters == null)
                {
                    operation.parameters = new List();
                }
                var requestAttributes = apiDescription.GetControllerAndActionAttributes();
                foreach (var attr in requestAttributes)
                {
                    operation.parameters.Add(new Swashbuckle.Swagger.Parameter
                    {
                        description = attr.Description,
                        name = attr.Name,
                        @in = "formData",
                        required = true,
                        type = "file",
                    });
                    operation.consumes.Add("multipart/form-data");
                }
            }
        }
        class ValidateFilter : IActionFilter
        {
            public bool AllowMultiple { get; }
            public Task ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func> continuation)
            {
                if (!actionContext.ModelState.IsValid)
                {
                    string msg = "";
                    var err = actionContext.ModelState.Values?.Last()?.Errors?.Last();
                    if (err != null)
                    {
                        if (!string.IsNullOrWhiteSpace(err.ErrorMessage))
                            msg = err.ErrorMessage;
                        else
                            msg = err.Exception.Message;
                    }
                    return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
                    {
                        Content = new StringContent(
                        JsonConvert.SerializeObject(
                        new AjaxResult
                        {
                            code = 0,
                            data = null,
                            msg = msg,
                        }), Encoding.UTF8, "application/json")
                    });
                }
                return continuation();
            }
        }
        class HandlerErrorAttribute : ExceptionFilterAttribute
        {
            /// 
            /// 控制器方法中出现异常,会调用该方法捕获异常
            /// 
            /// 提供使用
            public override void OnException(HttpActionExecutedContext context)
            {
                if (context.Exception.GetType() != typeof(System.OperationCanceledException))
                    Serilog.Log.Error(context.Exception, context.Exception.Message);
                else
                    return;
                base.OnException(context);
                string msg = context.Exception.Message;
                if (context.Exception.GetType() == typeof(FileNotFoundException))
                {
                    //防止程序路径泄露到前端
                    msg = "未能找到文件" + context.Exception.Message.Substring(context.Exception.Message.LastIndexOf("\\") + 1);
                }
                throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new StringContent(
                   JsonConvert.SerializeObject(
                    new AjaxResult
                    {
                        code = 0,
                        data = null,
                        msg = msg
                    }), Encoding.UTF8, "application/json")
                });
            }
        };
        class SwaggerControllerDescProvider : ISwaggerProvider
        {
            private readonly ISwaggerProvider _swaggerProvider;
            private static ConcurrentDictionary _cache = new ConcurrentDictionary();
            private readonly string[] _xml;
            /// 
            /// 
            /// 
            /// 
            /// xml文档路径
            public SwaggerControllerDescProvider(ISwaggerProvider swaggerProvider, string[] xml)
            {
                _swaggerProvider = swaggerProvider;
                _xml = xml;
            }
            public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
            {
                var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
                SwaggerDocument srcDoc = null;
                //只读取一次
                if (!_cache.TryGetValue(cacheKey, out srcDoc))
                {
                    srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);
                    srcDoc.vendorExtensions = new Dictionary { { "ControllerDesc", GetControllerDesc() } };
                    _cache.TryAdd(cacheKey, srcDoc);
                }
                return srcDoc;
            }
            /// 
            /// 从API文档中读取控制器描述
            /// 
            /// 所有控制器描述
            public ConcurrentDictionary GetControllerDesc()
            {
                ConcurrentDictionary controllerDescDict = new ConcurrentDictionary();
                if (_xml != null)
                {
                    foreach (var item in _xml)
                    {
                        if (File.Exists(item))
                        {
                            XmlDocument xmldoc = new XmlDocument();
                            xmldoc.Load(item);
                            string type = string.Empty, path = string.Empty, controllerName = string.Empty;
                            string[] arrPath;
                            int length = -1, cCount = "Controller".Length;
                            XmlNode summaryNode = null;
                            foreach (XmlNode node in xmldoc.SelectNodes("//member"))
                            {
                                type = node.Attributes["name"].Value;
                                if (type.StartsWith("T:"))
                                {
                                    //控制器
                                    arrPath = type.Split('.');
                                    length = arrPath.Length;
                                    controllerName = arrPath[length - 1];
                                    if (controllerName.EndsWith("Controller"))
                                    {
                                        //获取控制器注释
                                        summaryNode = node.SelectSingleNode("summary");
                                        string key = controllerName.Remove(controllerName.Length - cCount, cCount);
                                        if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
                                        {
                                            controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
                                        }
                                    }
                                }
                            }
                        }
                    }
                    
                }
                return controllerDescDict;
            }
        }
    }
    /// 
    /// Swagger文件上传特性标注
    /// 
    [AttributeUsage(AttributeTargets.Method)]
    public sealed class SwaggerFormAttribute : Attribute
    {
        /// 
        /// 
        /// 
        public SwaggerFormAttribute()
        {
            this.Name = "文件";
            this.Description = "选择文件";
        }
        /// 
        /// Swagger特性标注
        /// 
        /// 
        /// 
        public SwaggerFormAttribute(string name, string description)
        {
            Name = name;
            Description = description;
        }
        /// 
        /// 名称
        /// 
        public string Name { get; private set; }
        /// 
        /// 描述
        /// 
        public string Description { get; private set; }
    }
    /// 
    /// autofac属性注入
    /// 
    [AttributeUsage(AttributeTargets.Property)]
    public class AutowiredAttribute : Attribute
    {
    }
    // 属性注入选择器
    class AutowiredPropertySelector : IPropertySelector
    {
        public bool InjectProperty(PropertyInfo propertyInfo, object instance)
        {
            // 带有 AutowiredAttribute 特性的属性会进行属性注入
            return propertyInfo.CustomAttributes.Any(it => it.AttributeType == typeof(AutowiredAttribute));
        }
    }
    class SwaggerEnumFilter : ISchemaFilter
    {
        public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
        {
            UpdateSchemaDescription(schema, type);
        }
        private void UpdateSchemaDescription(Schema schema, Type type)
        {
            if (type.IsEnum)//枚举直接应用在controller接口中
            {
                var items = GetEnumInfo(type);
                if (items.Length > 0)
                {
                    var description = GetEnumInfo(type);
                    schema.description = string.IsNullOrEmpty(schema.description) ? description : $"{schema.description}:{description}";
                }
            }
            else if (type.IsClass && type != typeof(string))//枚举在类的属性中
            {
                if (schema.properties == null) return;
                var props = type.GetProperties();
                foreach (var prop in props)
                {
                    if (schema.properties.ContainsKey(prop.Name))
                    {
                        var propScheama = schema.properties[prop.Name];
                        if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
                        {
                            UpdateSchemaDescription(propScheama, prop.PropertyType);
                        }
                        else
                        {
                            if (prop.PropertyType.IsEnum)
                            {
                                var description = GetEnumInfo(prop.PropertyType);
                                propScheama.description = string.IsNullOrWhiteSpace(propScheama.description) ? description : $"{propScheama.description}:{description}";
                                propScheama.@enum = null;
                            }
                        }
                    }
                }
            }
        }
        /// 
        /// 获取枚举值+描述  
        /// 
        /// 
        /// 
        private string GetEnumInfo(Type enumType)
        {
            var fields = enumType.GetFields();
            List list = new List();
            foreach (var field in fields)
            {
                if (!field.FieldType.IsEnum) continue;
                string description = null;
                if (description == null)//取DescriptionAttribute的值
                {
                    var descriptionAttr = field.GetCustomAttribute();
                    if (descriptionAttr != null && !string.IsNullOrWhiteSpace(descriptionAttr.Description))
                    {
                        description = descriptionAttr.Description;
                    }
                }
                if (description == null)//取DisplayAttribute的值
                {
                    var dispalyAttr = field.GetCustomAttribute();
                    if (dispalyAttr != null && !string.IsNullOrWhiteSpace(dispalyAttr.Name))
                    {
                        description = dispalyAttr.Name;
                    }
                }
                if (description == null)//取DisplayNameAttribute的值
                {
                    var dispalyNameAttr = field.GetCustomAttribute();
                    if (dispalyNameAttr != null && !string.IsNullOrWhiteSpace(dispalyNameAttr.DisplayName))
                    {
                        description = dispalyNameAttr.DisplayName;
                    }
                }
                if (description == null)//取字段名
                {
                    description = field.Name;
                }
                var value = field.GetValue(null);
                list.Add($"{description}={(int)value}");
            }
            if (enumType.GetCustomAttribute() != null)//支持按位与的枚举
            {
                list.Add("(多个类型请将对应数字相加)");
            }
            return string.Join(",", list);
        }
    }
    class CustomModelValidator : ModelValidator
    {
        public CustomModelValidator(IEnumerable modelValidatorProviders) : base(modelValidatorProviders)
        {
        }
        public override IEnumerable Validate(ModelMetadata metadata, object container)
        {
            if (metadata.IsComplexType && metadata.Model == null)
            {
                return new List { new ModelValidationResult { MemberName = metadata.GetDisplayName(), Message = "请求参数对象不能为空" } };
            }
            if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType))
            {
                var validationResult = (metadata.Model as IValidatableObject).Validate(new ValidationContext(metadata.Model));
                if (validationResult != null)
                {
                    var modelValidationResults = new List();
                    foreach (var result in validationResult)
                    {
                        if (result == null) continue;
                        modelValidationResults.Add(new ModelValidationResult
                        {
                            MemberName = string.Join(",", result.MemberNames),
                            Message = result.ErrorMessage
                        });
                    }
                    return modelValidationResults;
                }
                return null;
            }
            return GetModelValidator(ValidatorProviders).Validate(metadata, container);
        }
    }
    class SwaggerDefalutValueFilter : ISchemaFilter
    {
        public SwaggerDefalutValueFilter()
        {
            var cc = this.GetHashCode();
        }
        public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
        {
            if (schema.properties == null)
            {
                return;
            }
            var props = type.GetProperties().Where(p => p.PropertyType == typeof(DateTime) || p.PropertyType == typeof(DateTime?));
            var props2 = type.GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset) || p.PropertyType == typeof(DateTimeOffset?));
            foreach (PropertyInfo propertyInfo in props)
            {
                foreach (KeyValuePair property in schema.properties)
                {
                    if (propertyInfo.Name == property.Key)
                    {
                        property.Value.example = "2023-05-12 12:00:00";
                        if (!string.IsNullOrWhiteSpace(Startup._timeZoneUtc) && propertyInfo.Name.EndsWith("Time"))
                        {
                            property.Value.description = $"{property.Value.description}({Startup._timeZoneUtc})";
                        }
                        else
                        {
                        }
                        break;
                    }
                }
            }
            foreach (PropertyInfo propertyInfo in props2)
            {
                foreach (KeyValuePair property in schema.properties)
                {
                    if (propertyInfo.Name == property.Key)
                    {
                        property.Value.example = "2023-05-12 12:00:00 +0800";
                        if (!string.IsNullOrWhiteSpace(Startup._timeZoneUtc) && propertyInfo.Name.EndsWith("Time"))
                        {
                            property.Value.description = $"{property.Value.description}({Startup._timeZoneUtc})";
                        }
                        else
                        {
                        }
                        break;
                    }
                }
            }
        }
    }
}