using System.Text;
using Hangfire;
using Hangfire.Redis.StackExchange;
using JiShe.CollectBus.Host.Hangfire;
using JiShe.CollectBus.Host.HealthChecks;
using JiShe.CollectBus.Host.Swaggers;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using StackExchange.Redis;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.Auditing;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Caching;
using Volo.Abp.Modularity;
using TouchSocket.Core;
using TouchSocket.Sockets;
using JiShe.CollectBus.Plugins;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using JiShe.CollectBus.Cassandra;


namespace JiShe.CollectBus.Host
{
    public partial class CollectBusHostModule
    {
        /// <summary>
        /// Configures the hangfire.
        /// </summary>
        /// <param name="context">The context.</param>
        private void ConfigureHangfire(ServiceConfigurationContext context)
        {
            var redisStorageOptions = new RedisStorageOptions()
            {
                Db = context.Services.GetConfiguration().GetValue<int>("Redis:HangfireDB")
            };

            Configure<AbpBackgroundJobOptions>(options => { options.IsJobExecutionEnabled = true; });

            context.Services.AddHangfire(config =>
            {
                config.UseRedisStorage(
                        context.Services.GetConfiguration().GetValue<string>("Redis:Configuration"), redisStorageOptions)
                    .WithJobExpirationTimeout(TimeSpan.FromDays(7));
                var delaysInSeconds = new[] { 10, 60, 60 * 3 }; // 重试时间间隔
                const int Attempts = 3; // 重试次数
                config.UseFilter(new AutomaticRetryAttribute() { Attempts = Attempts, DelaysInSeconds = delaysInSeconds });
                //config.UseFilter(new AutoDeleteAfterSuccessAttribute(TimeSpan.FromDays(7)));
                config.UseFilter(new JobRetryLastFilter(Attempts));
            });
            context.Services.AddHangfireServer();
        }

        /// <summary>
        /// Configures the JWT authentication.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="configuration">The configuration.</param>
        private void ConfigureJwtAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters =
                        new TokenValidationParameters()
                        {
                            // 是否开启签名认证
                            ValidateIssuerSigningKey = true,
                            ValidateIssuer = true,
                            ValidateAudience = true,
                            ValidateLifetime = true,
                            ClockSkew = TimeSpan.Zero,
                            ValidIssuer = configuration["Jwt:Issuer"],
                            ValidAudience = configuration["Jwt:Audience"],
                            IssuerSigningKey =
                                new SymmetricSecurityKey(
                                    Encoding.ASCII.GetBytes(configuration["Jwt:SecurityKey"]))
                        };

                    options.Events = new JwtBearerEvents
                    {
                        OnMessageReceived = currentContext =>
                        {
                            var path = currentContext.HttpContext.Request.Path;
                            if (path.StartsWithSegments("/login"))
                            {
                                return Task.CompletedTask;
                            }

                            var accessToken = string.Empty;
                            if (currentContext.HttpContext.Request.Headers.ContainsKey("Authorization"))
                            {
                                accessToken = currentContext.HttpContext.Request.Headers["Authorization"];
                                if (!string.IsNullOrWhiteSpace(accessToken))
                                {
                                    accessToken = accessToken.Split(" ").LastOrDefault();
                                }
                            }

                            if (string.IsNullOrWhiteSpace(accessToken))
                            {
                                accessToken = currentContext.Request.Query["access_token"].FirstOrDefault();
                            }

                            if (string.IsNullOrWhiteSpace(accessToken))
                            {
                                accessToken = currentContext.Request.Cookies[@CollectBusHostConst.DefaultCookieName];
                            }

                            currentContext.Token = accessToken;
                            currentContext.Request.Headers.Remove("Authorization");
                            currentContext.Request.Headers.Add("Authorization", $"Bearer {accessToken}");

                            return Task.CompletedTask;
                        }
                    };
                });
        }

        /// <summary>
        /// Configures the cache.
        /// </summary>
        /// <param name="context">The context.</param>
        private void ConfigureCache(ServiceConfigurationContext context)
        {
            Configure<AbpDistributedCacheOptions>(
                options => { options.KeyPrefix = "CollectBus:"; });
            var configuration = context.Services.GetConfiguration();
            var redis = ConnectionMultiplexer.Connect($"{configuration.GetValue<string>("Redis:Configuration")},defaultdatabase={configuration.GetValue<int>("Redis:DefaultDB")}");
            context.Services
                .AddDataProtection()
                .PersistKeysToStackExchangeRedis(redis, "CollectBus-Protection-Keys");
        }

        /// <summary>
        /// Configures the swagger services.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="configuration">The configuration.</param>
        private void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddSwaggerGen(
                options =>
                {
                    configuration.GetSection("SwaggerConfig").Get<List<SwaggerConfig>>()?.ForEach(group =>
                    {
                        options.SwaggerDoc(group.GroupName,
                            new OpenApiInfo { Title = group.Title, Version = group.Version });
                    });

                    options.DocInclusionPredicate((docName, apiDes) =>
                    {
                        if (docName == "Basic" && string.IsNullOrWhiteSpace(apiDes.GroupName)) return true;
                        return docName == apiDes.GroupName;
                    });

                    options.EnableAnnotations();
                    options.DocumentFilter<HiddenAbpDefaultApiFilter>();
                    options.SchemaFilter<EnumSchemaFilter>();
                    var xmlPaths = Directory.GetFiles(AppContext.BaseDirectory, "*.xml")
                        .Where(a => a.EndsWith("Application.xml") ||
                                    a.EndsWith("Application.Contracts.xml") ||
                                    a.EndsWith("httpApi.xml") ||
                                    a.EndsWith("Host.xml"))
                        .Distinct()
                        .ToList();
                    foreach (var xml in xmlPaths) options.IncludeXmlComments(xml, true);
                });
        }

        /// <summary>
        /// Configures the audit log.
        /// </summary>
        /// <param name="context">The context.</param>
        private void ConfigureAuditLog(ServiceConfigurationContext context)
        {
            Configure<AbpAuditingOptions>
            (
                options =>
                {
                    options.IsEnabled = true;
                    options.EntityHistorySelectors.AddAllEntities();
                    options.ApplicationName = "JiShe.CollectBus";
                }
            );

            Configure<AbpAspNetCoreAuditingOptions>(
                options =>
                {
                    options.IgnoredUrls.Add("/AuditLogs/page");
                    options.IgnoredUrls.Add("/hangfire/stats");
                    options.IgnoredUrls.Add("/hangfire/recurring/trigger");
                    options.IgnoredUrls.Add("/cap");
                    options.IgnoredUrls.Add("/");
                });
        }


        /// <summary>
        /// Configures the custom.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="configuration">The configuration.</param>
        private void ConfigureCustom(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }

        /// <summary>
        /// Configures the network.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="configuration">The configuration.</param>
        public void ConfigureNetwork(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddTcpService(config =>
            {
                config.SetListenIPHosts(int.Parse(configuration["TCP:ClientPort"] ?? "10500"))
                    //.SetTcpDataHandlingAdapter(()=>new StandardFixedHeaderDataHandlingAdapter())
                    //.SetGetDefaultNewId(() => Guid.NewGuid().ToString())//定义ClientId的生成策略
                    .ConfigurePlugins(a =>
                    {
                        a.Add<TcpCloseMonitor>();
                        a.Add<TcpMonitor>();
                        a.Add<ServerMonitor>();
                    });
            });

            context.Services.AddUdpSession(config =>
            {
                config.SetBindIPHost(int.Parse(configuration["UDP:ClientPort"] ?? "10500"))
                    .ConfigurePlugins(a =>
                    {
                        a.Add<UdpMonitor>();
                        a.Add<ServerMonitor>();
                    })
                    .UseBroadcast()
                    .SetUdpDataHandlingAdapter(() => new NormalUdpDataHandlingAdapter());
            });
        }

        /// <summary>
        /// 健康检查
        /// </summary>
        /// <param name="context"></param>
        /// <param name="configuration"></param>
        private void ConfigureHealthChecks(ServiceConfigurationContext context, IConfiguration configuration)
        {
            if (!configuration.GetValue<bool>("HealthChecks:IsEnable")) return;
            var cassandraConfig = new CassandraConfig();
            configuration.GetSection("Cassandra").Bind(cassandraConfig);
            context.Services.AddHealthChecks()
                .AddMongoDb(configuration.GetConnectionString("Default"), "MongoDB", HealthStatus.Unhealthy)
                .AddRedis(configuration.GetValue<string>("Redis:Configuration") ?? string.Empty, "Redis",
                    HealthStatus.Unhealthy)
                .AddKafka(new Confluent.Kafka.ProducerConfig
                {
                    BootstrapServers = configuration.GetConnectionString("Kafka")
                }, "Kafka", failureStatus: HealthStatus.Unhealthy)

                .AddCheck<CassandraHealthCheck>("Cassandra")
                .AddCheck<IoTdbHealthCheck>("IoTDB");

            context.Services
                .AddHealthChecksUI(options =>
                {
                    options.AddHealthCheckEndpoint("JiSheCollectBus", "/health"); // 映射本地端点
                })
                .AddInMemoryStorage();


        }
    }
}