using Mapster;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using DD.Persistence.Models;
using DD.Persistence.Models.Configurations;
using DD.Persistence.Services;
using DD.Persistence.Services.Interfaces;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using System.Text.Json.Nodes;
using DD.Persistence.Database.Entity;

namespace DD.Persistence.API;

public static class DependencyInjection
{
    public static void MapsterSetup()
    {
        TypeAdapterConfig.GlobalSettings.Default.Config
            .ForType<TechMessageDto, TechMessage>()
            .Ignore(dest => dest.System, dest => dest.SystemId);
    }
    public static void AddSwagger(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddSwaggerGen(c =>
        {
            c.MapType<TimeSpan>(() => new OpenApiSchema { Type = "string", Example = new OpenApiString("0.00:00:00") });
            c.MapType<DateOnly>(() => new OpenApiSchema { Type = "string", Format = "date" });
            c.MapType<JsonValue>(() => new OpenApiSchema
            {
                AnyOf = [
                    new OpenApiSchema {Type = "string", Format = "string" },
                    new OpenApiSchema {Type = "number", Format = "int32" },
                    new OpenApiSchema {Type = "number", Format = "float" }
                ]
            });

            c.CustomOperationIds(e =>
            {
                return $"{e.ActionDescriptor.RouteValues["action"]}";
            });

            c.SwaggerDoc("v1", new OpenApiInfo { Title = "Persistence web api", Version = "v1" });

            var needUseKeyCloak = configuration.GetSection("NeedUseKeyCloak").Get<bool>();
            if (needUseKeyCloak)
                c.AddKeycloakSecurity(configuration);
            else c.AddDefaultSecurity();

            var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
            var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
            var includeControllerXmlComment = true;
            c.IncludeXmlComments(xmlPath, includeControllerXmlComment);
        });
    }

    public static void AddServices(this IServiceCollection services)
    {
        services.AddTransient<IWitsDataService, WitsDataService>();
    }

    #region Authentication
    public static void AddJWTAuthentication(this IServiceCollection services, IConfiguration configuration)
    {
        var needUseKeyCloak = configuration
            .GetSection("NeedUseKeyCloak")
            .Get<bool>();
        if (needUseKeyCloak)
            services.AddKeyCloakAuthentication(configuration);
        else services.AddDefaultAuthentication(configuration);
    }

    private static void AddKeyCloakAuthentication(this IServiceCollection services, IConfiguration configuration)
    {
        var keyCloakHost = configuration["KeyCloakAuthentication:Host"];
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false;
                options.Audience = configuration["KeyCloakAuthentication:Audience"];
                options.MetadataAddress = $"{keyCloakHost}/.well-known/openid-configuration";
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidIssuer = keyCloakHost
                };
            });
    }

    private static void AddDefaultAuthentication(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidIssuer = JwtParams.Issuer,
                    ValidateAudience = true,
                    ValidAudience = JwtParams.Audience,
                    ValidateLifetime = true,
                    IssuerSigningKey = JwtParams.SecurityKey,
                    ValidateIssuerSigningKey = false,
                };
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var accessToken = context.Request.Headers.Authorization
                            .ToString()
                            .Replace(JwtBearerDefaults.AuthenticationScheme, string.Empty)
                            .Trim();

                        context.Token = accessToken;

                        return Task.CompletedTask;
                    },
                    OnTokenValidated = context =>
                    {
                        var username = context.Principal?.Claims
                            .FirstOrDefault(e => e.Type == "username")?.Value;

                        var password = context.Principal?.Claims
                            .FirstOrDefault(e => e.Type == "password")?.Value;

                        var keyCloakUser = configuration
                            .GetSection(nameof(AuthUser))
                            .Get<AuthUser>()!;

                        if (username != keyCloakUser.Username || password != keyCloakUser.Password)
                        {
                            context.Fail("username or password did not match");
                        }

                        return Task.CompletedTask;
                    }
                };
            });
    }
    #endregion

    #region Keycloak
    private static void AddKeycloakSecurity(this SwaggerGenOptions options, IConfiguration configuration)
    {
        var keyCloakHost = configuration["KeyCloakAuthentication:Host"];

        options.AddSecurityDefinition("Keycloak", new OpenApiSecurityScheme
        {
            Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345token'",
            Name = "Authorization",
            In = ParameterLocation.Header,
            Type = SecuritySchemeType.OAuth2,
            Flows = new OpenApiOAuthFlows
            {
                Implicit = new OpenApiOAuthFlow
                {
                    AuthorizationUrl = new Uri($"{keyCloakHost}/protocol/openid-connect/auth"),
                }
            }
        });

        options.AddSecurityRequirement(new OpenApiSecurityRequirement()
        {
            {
                new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference
                    {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Keycloak"
                    },
                    Scheme = "Bearer",
                    Name = "Bearer",
                    In = ParameterLocation.Header,
                },
                new List<string>()
            }
        });
    }

    private static void AddDefaultSecurity(this SwaggerGenOptions options)
    {
        options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
        {
            Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345token'",
            Name = "Authorization",
            In = ParameterLocation.Header,
            Type = SecuritySchemeType.ApiKey,
            Scheme = "Bearer",
        });

        options.AddSecurityRequirement(new OpenApiSecurityRequirement()
        {
            {
                new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference
                    {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer"
                    },
                    Scheme = "oauth2",
                    Name = "Bearer",
                    In = ParameterLocation.Header,
                },
                new List<string>()
            }
        });
    }
    #endregion
}