using System.Reflection;
using FluentValidation;
using FluentValidation.AspNetCore;
using Hotline.Api.Realtimes;
using Hotline.Application;
using Hotline.NewRock;
using Hotline.Permissions;
using Hotline.Repository.SqlSugar.Extensions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Serilog;
using Mapster;
using Tr.Sdk;
using XF.Domain.Dependency;
using XF.Domain.Filters;
using XF.Domain.Options;
using XF.EasyCaching;
using XF.Utility.MQ;
using Hotline.Ai.Jths;
using Hotline.Api.Sdk;
using Hotline.CallCenter.Configs;
using Hotline.Wex;
using Hotline.Application.CallCenter.Calls;
using Hotline.Application.CallCenter;
using Hotline.Authentications;
using Hotline.CallCenter.Calls;
using Hotline.Configurations;
using Hotline.DI;
using Hotline.Settings.TimeLimitDomain.ExpireTimeSupplier;
using XF.Domain.Authentications;
using Hotline.WeChat;
using TianQue.Sdk;
using Hotline.Orders;
using XF.Domain.Repository.Events;
using Hotline.Orders.DatabaseEventHandler;
using Hotline.Ai.XingTang;
using Hotline.Pdf;
using Hotline.XingTang;
using Hotline.Snapshot.IRepository;
using Hotline.Validators;
using Hotline.Api.Filter;
using Hotline.Caching.Interfaces;
using Hotline.Settings;


namespace Hotline.Api;

internal static class StartupExtensions
{
    const string CorsOrigins = "CorsOrigins";
    internal static WebApplication ConfigureServices(this WebApplicationBuilder builder)
    {
        var services = builder.Services;
        var configuration = builder.Configuration;

        services.AddHttpContextAccessor();

#if DEBUG
        builder.WebHost.UseUrls("http://*:50100");
#endif

        services.Configure<IdentityConfiguration>(d => configuration.GetSection(nameof(IdentityConfiguration)).Bind(d));

        var appConfigurationSection = configuration.GetRequiredSection(nameof(AppConfiguration));
        var appConfiguration = appConfigurationSection.Get<AppConfiguration>();
        if (appConfiguration is null) throw new ArgumentNullException(nameof(appConfiguration));
        services.Configure<AppConfiguration>(d => appConfigurationSection.Bind(d));

        var callCenterConfigurationSection = configuration.GetRequiredSection(nameof(CallCenterConfiguration));
        var callCenterConfiguration = callCenterConfigurationSection.Get<CallCenterConfiguration>();
        services.Configure<CallCenterConfiguration>(d => callCenterConfigurationSection.Bind(d));

        services.Configure<CityBaseConfiguration>(d => configuration.GetSection(nameof(CityBaseConfiguration)).Bind(d));
        //services.Configure<SendSmsConfiguration>(d => configuration.GetSection("SendSms").Bind(d));

        // Add services to the container.
        services
            .BatchInjectServices(d =>
            {
                var attr = d.GetCustomAttribute(typeof(InjectionAttribute)) as InjectionAttribute;
                if (attr is null) return true;
                return attr.IsEnable(appConfiguration.AppScope,
                    appConfiguration.GetDefaultAppScopeConfiguration().CallCenterType);
            })
            .RegisterRepository()
            .AddApplication()
            .AddScoped(typeof(IPasswordHasher<>), typeof(PasswordHasher<>))
            .AddHttpClient()
            ;

        services.AddKeyedScoped<ISessionContext, ProvinceSessionContext>(ProvinceSessionContext.Key)
            .AddKeyedScoped<ISessionContext, Police110SessionContext>(Police110SessionContext.Key)
            ;

        //cache
        services.AddCache(d =>
        {
            var cacheConfig = configuration.GetSection("Cache").Get<CacheOptions>();
            cacheConfig.Adapt(d);
            d.Prefix = "Hotline";
            d.TopicName = "hotline-topic";
        });

        //Authentication
        services.RegisterAuthentication(configuration);

        services.AddControllers(options =>
            {
                options.Filters.Add<UnifyResponseFilter>();
                options.Filters.Add<UserFriendlyExceptionFilter>();
            })
            ;

        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
        services.AddEndpointsApiExplorer();

        //swagger
        services.RegisterSwagger();

        //signalR
        services.RegisterSignalR(configuration);

        /* CORS */
        services.RegisterCors(configuration, CorsOrigins);

        //mapster
        services.RegisterMapper();

        //mediatr
        services.RegisterMediatR(appConfiguration);

        //app scope
        switch (appConfiguration.AppScope)
        {
            case AppDefaults.AppScope.YiBin:
                //jths 
                services.AddAiJths(appConfiguration.YiBin.AiQuality.Url);
                services.AddAiVisitService(
                    appConfiguration.YiBin.AiVisit.Url,
                    appConfiguration.YiBin.AiVisit.Appkey,
                    appConfiguration.YiBin.AiVisit.ServiceVersion);

                //宜宾企业服务
                services.AddYbEnterpriseSdk(appConfiguration.YiBin.Enterprise.AddressUrl)
                    .AddKeyedScoped<ISessionContext, YbEnterpriseSessionContext>(YbEnterpriseSessionContext.Key)
                    .AddKeyedScoped<ISessionContext, ZzptSessionContext>(ZzptSessionContext.Key);
                break;
            case AppDefaults.AppScope.ZiGong:
                services.AddAiXingTang(appConfiguration.ZiGong.AiQuality.Url);
                break;
            case AppDefaults.AppScope.LuZhou:
                break;
        }

        //callcenter
        var callcenterType = appConfiguration.GetDefaultAppScopeConfiguration().CallCenterType;
        switch (callcenterType)
        {
            case AppDefaults.CallCenterType.XunShi:
                services.AddNewRock(callCenterConfiguration.NewRock);
                break;
            case AppDefaults.CallCenterType.WeiErXin:
                services
                    .AddWex(callCenterConfiguration.Wex)
                    .AddWexDb(configuration);
                break;
            case AppDefaults.CallCenterType.TianRun:
                services
                    .AddScoped<ICallApplication, TianRunCallApplication>()
                    .AddScoped<ITrApplication, TrApplication>()
                    //.AddHostedService<CurrentWaitNumService>()
                    //.AddHostedService<TelsStatusRefreshService>()
                    .AddTrSdk(callCenterConfiguration.TianRun.Address,
                        callCenterConfiguration.TianRun.Username,
                        callCenterConfiguration.TianRun.Password);
                break;
            case AppDefaults.CallCenterType.XingTang:
                services.AddXingTangDb(callCenterConfiguration.XingTang)
                    .AddXingTangSDK()
                    .AddScoped<ICallApplication, XingTangCallApplication>()
                    .AddScoped<CallIdManager>()
                    ;
                break;
            default:
                break;
        }

        //services.AddScoped(typeof(IUpdateDatabase<>), typeof(UpdateDatabase<>));
        services.AddScoped<IUpdateDatabaseEvent<OrderVisitDetail>, OrderVisitDetailEventHandler>();
        // services.AddScoped<IUpdateDatabaseEvent<OrderSnapshot>, OrderSnapshotEventHandler>();

        //sqlsugar
        services.AddSqlSugar(configuration);

        //validator
        services.AddFluentValidationAutoValidation(config =>
        {
            config.DisableDataAnnotationsValidation = true;
        })
            .AddValidatorsFromAssembly(typeof(ValidatorExtensions).Assembly);

        //mq
        services.AddMq(()=>configuration.GetSection(nameof(MqConfiguration)).Get<MqConfiguration>());

        //job
        services.RegisterJob(appConfiguration);

        services.AddPdfManager();

        //compression
        services.RegisterCompression();

        //authorization
        services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
        services.RegisterAuthorization(configuration);

        services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
        services.AddScoped<ExpireTimeFactory>();
        services.AddScoped<IExpireTimeSupplier, DaySupplier>();
        services.AddScoped<IExpireTimeSupplier, WorkDaySupplier>();
        services.AddScoped<IExpireTimeSupplier, HourSupplier>();

        services.AddWeChatService(configuration);

        services.AddScoped<IGuiderSystemService, TiqnQueService>();

        services.AddScoped<ClientIpFilterAttribute>(sp =>
        {
            var loggerFactory = sp.GetService<ILoggerFactory>();
            var logger = loggerFactory.CreateLogger<ClientIpFilterAttribute>();
            var cacheManager = sp.GetService<ISystemSettingCacheManager>();
            var whiteIps = cacheManager.GetSetting(SettingConstants.WhiteIp).SettingValue
                .Where(d => !string.IsNullOrEmpty(d))
                .ToList();

            return new ClientIpFilterAttribute(whiteIps, logger);
        });

        return builder.Build();
    }

    internal static WebApplication ConfigurePipelines(this WebApplication app)
    {
        app.UseSerilogRequestLogging();
        
        // Configure the HTTP request pipeline.
        var swaggerEnable = app.Configuration.GetSection("Swagger").Get<bool>();
        if (swaggerEnable)
        {
            app.UseSwagger();
            app.UseSwaggerUI(options =>
            {
                options.DefaultModelsExpandDepth(1);
                options.DefaultModelExpandDepth(5);
            });
            //app.UseSwaggerUI(c =>
            //{
            //    //c.DocExpansion(DocExpansion.None);
            //    //c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
            //});
        }

        app.UseCors(CorsOrigins);

        app.UseAuthentication();
        app.UseAuthorization();
        //app.MapHub<CallCenterHub>("/hubs/callcenter");
        app.MapHub<HotlineHub>("/hubs/hotline");

        app.MapControllers()
            .RequireAuthorization();
        //app.MapSubscribeHandler();
        app.UseWeChat();

        // 记录交互日志
        //app.UseRequestResponseLogging(app.Configuration);

        app.UseResponseCompression();

        return app;
    }
}