using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;

namespace SatelliteProvider.Api.Authentication;

public static class AuthenticationServiceCollectionExtensions
{
    public const string JwtSecretEnvVar = "JWT_SECRET";
    public const string JwtSecretConfigKey = "Jwt:Secret";
    public const int MinSecretByteLength = 32;

    public static IServiceCollection AddSatelliteJwt(this IServiceCollection services, IConfiguration configuration)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentNullException.ThrowIfNull(configuration);

        var secret = ResolveSecretOrThrow(configuration);
        var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));

        services
            .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = signingKey,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.FromSeconds(30),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    RequireSignedTokens = true,
                    RequireExpirationTime = true
                };
            });

        return services;
    }

    internal static string ResolveSecretOrThrow(IConfiguration configuration)
    {
        var secret = Environment.GetEnvironmentVariable(JwtSecretEnvVar);
        if (string.IsNullOrWhiteSpace(secret))
        {
            secret = configuration[JwtSecretConfigKey];
        }

        if (string.IsNullOrWhiteSpace(secret))
        {
            throw new InvalidOperationException(
                $"JWT secret is not configured. Set the {JwtSecretEnvVar} environment variable " +
                $"or the {JwtSecretConfigKey} configuration key to a value of at least {MinSecretByteLength} bytes.");
        }

        var byteLength = Encoding.UTF8.GetByteCount(secret);
        if (byteLength < MinSecretByteLength)
        {
            throw new InvalidOperationException(
                $"JWT secret is too short ({byteLength} bytes). HMAC-SHA256 requires at least {MinSecretByteLength} bytes " +
                $"per RFC 2104 §3. Set {JwtSecretEnvVar} or {JwtSecretConfigKey} to a longer value.");
        }

        return secret;
    }
}
