// ***********************************************************************
// 
//     AgileWebs
// 
// ***********************************************************************
using Core.Cerberos.Adapters.Common.Constants;
using Core.Cerberos.Adapters.Contracts;
using Core.Cerberos.Adapters.Handlers;
using Core.Cerberos.Adapters.Options;
using Core.Cerberos.Adapters.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Identity.Web;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
namespace Core.Cerberos.Adapters.Extensions
{
    /// 
    /// Extension methods for configuring authentication with various Azure AD setups.
    /// 
    public static class AuthenticationExtension
    {
        /// 
        /// Configures authentication using Azure AD for an API that requires downstream API access.
        /// 
        /// The  to add the services to.
        /// The  containing Azure AD configuration settings.
        public static void ConfigureAuthentication(this IServiceCollection services, IConfiguration configuration, AuthSettings authSettings)
        {
            var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty;
            var azureAdInMemorySettings = new Dictionary
            {
                { "AzureAdB2C:Instance",  authSettings.AzureADInstance ?? string.Empty },
                { "AzureAdB2C:TenantId", authSettings.AzureADTenantId ?? string.Empty },
                { "AzureAdB2C:ClientId", authSettings.AzureADClientId ?? string.Empty },
                { "AzureAdB2C:ClientSecret", authSettings.AzureADClientSecret ?? string.Empty }
            };
            var configurationBuilder = new ConfigurationBuilder()
                .AddConfiguration(configuration)
                .AddInMemoryCollection(azureAdInMemorySettings);
            var combinedConfiguration = configurationBuilder.Build();
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
               .AddMicrosoftIdentityWebApi(combinedConfiguration.GetSection("AzureAdB2C"), Schemes.AzureScheme)
               .EnableTokenAcquisitionToCallDownstreamApi()
               .AddMicrosoftGraph(configuration.GetSection("MicrosoftGraph"))
               .AddInMemoryTokenCaches();
            var rsa = RSA.Create();
            rsa.ImportFromPem(authSettings.PrivateKey?.ToCharArray());
            var rsaPrivateKey = new RsaSecurityKey(rsa);
            var rsaPublic = RSA.Create();
            rsaPublic.ImportFromPem(authSettings.PublicKey?.ToCharArray());
            var rsaPublicKey = new RsaSecurityKey(rsaPublic);
            var jwtAppSettingOptions = configuration.GetSection("B2C:JwtIssuerOptions");
            var jwtIssuerOptions = jwtAppSettingOptions.Get();
            if (string.IsNullOrEmpty(jwtIssuerOptions?.Issuer) || string.IsNullOrEmpty(jwtIssuerOptions.Audience))
                throw new InvalidOperationException("JwtIssuerOptions are not configured correctly.");
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(Schemes.HeathScheme, x =>
            {
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidIssuer = jwtIssuerOptions?.Issuer,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidAudience = jwtIssuerOptions?.Audience,
                    IssuerSigningKey = rsaPublicKey
                };
            });
            services.Configure(options =>
            {
                options.Issuer = jwtIssuerOptions?.Issuer;
                options.Audience = jwtIssuerOptions?.Audience;
                options.SigningCredentials = new SigningCredentials(rsaPrivateKey, SecurityAlgorithms.RsaSha256);
            });
            services.AddSingleton(jwtAppSettingOptions);
            services.AddTransient();
            services.AddTransient();
        }
    }
}