using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using OpenWarehouse.auth.api.Common.AuthTokenRepo;

namespace OpenWarehouse.auth.api.Services;

public static class AuthenticationConfigService
{
    public static T AddAuthenticationService<T>(this T builder)
        where T : IHostApplicationBuilder
    {
        var privateKeyPath = Path.Combine(builder.Environment.ContentRootPath, "private_key_pkcs8.pem");
        var rsa = RSA.Create();

        if (!File.Exists(privateKeyPath))
        {
            rsa = RSA.Create(2048);
            var privateKeyPem = ExportPrivateKeyToPem(rsa);
            File.WriteAllText(privateKeyPath, privateKeyPem);
            Console.WriteLine($"Generated new RSA private key at: {privateKeyPath}");
        }
        else
        {
            var privateKey = File.ReadAllText(privateKeyPath);
            rsa.ImportFromPem(privateKey);
        }

        var rsaPublic = rsa.ExportParameters(false);
        var rsaSecurityKey = new RsaSecurityKey(rsa)
        {
            KeyId = "1"
        };

        // Configure JWT Bearer authentication
        builder.Services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = rsaSecurityKey,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    TokenDecryptionKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
                        builder.Configuration["Jwt:AesKey"] ?? throw new InvalidOperationException("Missing Jwt:AesKey in configuration"))),
                    ValidAudience = builder.Configuration["Jwt:Audience"],
                    ValidIssuer = builder.Configuration["Jwt:Issuer"],
                };

                options.Events = new JwtBearerEvents
                {
                    OnTokenValidated = async context =>
                    {
                        var tokenBlacklistService = context.HttpContext.RequestServices.GetRequiredService<AuthTokenRepo>();
                        var token = context.SecurityToken.Id;

                        if (await tokenBlacklistService.TokenIsLock(guid: token))
                        {
                            context.Fail("This token has been revoked.");
                        }
                    },
                };
            });
        
        builder.Services.AddSingleton(new
        {
            keys = new[]
            {
                new
                {
                    kty = "RSA",
                    e = Base64UrlEncoder.Encode(rsaPublic.Exponent),
                    n = Base64UrlEncoder.Encode(rsaPublic.Modulus),
                    use = "sig",
                    alg = "RS256"
                }
            }
        });

        return builder;
    }
    
    private static string ExportPrivateKeyToPem(RSA rsa)
    {
        var privateKeyBytes = rsa.ExportPkcs8PrivateKey();
        var builder = new StringBuilder();
        builder.AppendLine("-----BEGIN PRIVATE KEY-----");
        builder.AppendLine(Convert.ToBase64String(privateKeyBytes, Base64FormattingOptions.InsertLineBreaks));
        builder.AppendLine("-----END PRIVATE KEY-----");
        return builder.ToString();
    }
}
