using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;
using Tiku.Application.Security;
using Tiku.Domain.Tenancy;

namespace Tiku.IntegrationTests.Api;

internal static class TestJwtKeys
{
    public const string Issuer = "tiku-backend";
    public const string Audience = "tiku-api";
    public const string KeyId = "integration-test-rsa-key";

    public static string PrivateKeyPem { get; } = CreatePrivateKeyPem();
    public static string PublicKeyPem { get; } = CreatePublicKeyPem();

    public static string CreateToken(
        IEnumerable<Claim> claims,
        AuthRealm realm = AuthRealm.Tenant,
        bool includeStandardClaims = true,
        string? keyId = null)
    {
        using var rsa = RSA.Create();
        rsa.ImportFromPem(PrivateKeyPem);
        var key = new RsaSecurityKey(rsa)
        {
            KeyId = keyId ?? KeyId,
            CryptoProviderFactory = new CryptoProviderFactory
            {
                CacheSignatureProviders = false
            }
        };
        var credentials = new SigningCredentials(key, SecurityAlgorithms.RsaSha256);
        var tokenClaims = claims.ToList();
        if (tokenClaims.All(claim => claim.Type != TikuClaimTypes.Realm))
        {
            tokenClaims.Add(new Claim(
                TikuClaimTypes.Realm,
                realm.ToString().ToLowerInvariant()));
        }

        if (includeStandardClaims)
        {
            tokenClaims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")));
            tokenClaims.Add(new Claim(
                JwtRegisteredClaimNames.Iat,
                DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(),
                ClaimValueTypes.Integer64));
        }

        var token = new JwtSecurityToken(
            Issuer,
            Audience,
            tokenClaims,
            expires: DateTime.UtcNow.AddMinutes(5),
            signingCredentials: credentials);
        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    private static string CreatePrivateKeyPem()
    {
        using var rsa = RSA.Create(2048);
        return rsa.ExportPkcs8PrivateKeyPem();
    }

    private static string CreatePublicKeyPem()
    {
        using var rsa = RSA.Create();
        rsa.ImportFromPem(PrivateKeyPem);
        return rsa.ExportSubjectPublicKeyInfoPem();
    }
}

internal sealed class TestJwtKeyRing : IJwtKeyRing
{
    private static readonly RsaSecurityKey Key = CreateKey();

    public SigningCredentials SigningCredentials { get; } =
        new(Key, SecurityAlgorithms.RsaSha256);

    public IReadOnlyCollection<SecurityKey> ValidationKeys { get; } = [Key];

    private static RsaSecurityKey CreateKey()
    {
        var rsa = RSA.Create();
        rsa.ImportFromPem(TestJwtKeys.PrivateKeyPem);
        return new RsaSecurityKey(rsa)
        {
            KeyId = TestJwtKeys.KeyId,
            CryptoProviderFactory = new CryptoProviderFactory
            {
                CacheSignatureProviders = false
            }
        };
    }
}
