using DigitalData.Core.Abstraction.Application;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;

namespace DigitalData.Core.Application;

/// <summary>
/// Implements the <see cref="IJWTService{TClaimValue}"/> interface to manage JWT operations for claims of type <typeparamref name="TClaimValue"/>.
/// </summary>
public class JWTService<TClaimValue> : IJWTService<TClaimValue>
{
    private readonly Func<TClaimValue, SecurityTokenDescriptor> _factory;

    /// <summary>
    /// Initializes a new instance of the <see cref="JWTService{TClaimValue}"/> class.
    /// </summary>
    /// <param name="tokenDescriptorFactory">A factory function to produce <see cref="SecurityTokenDescriptor"/> based on the claim value.</param>
    public JWTService(Func<TClaimValue, SecurityTokenDescriptor> tokenDescriptorFactory)
    {
        _factory = tokenDescriptorFactory;
    }

    /// <summary>
    /// Generates a symmetric security key with the specified byte size.
    /// </summary>
    /// <param name="byteSize">The size of the security key in bytes. Default is 32 bytes.</param>
    /// <returns>A new instance of <see cref="SymmetricSecurityKey"/>.</returns>
    public static SymmetricSecurityKey GenerateSecurityKey(int byteSize = 32)
    {
        using var rng = RandomNumberGenerator.Create();
        var randomBytes = new byte[byteSize];
        rng.GetBytes(randomBytes);
        var securityKey = new SymmetricSecurityKey(randomBytes);

        return securityKey;
    }

    /// <summary>
    /// Generates a JWT for the specified claim value.
    /// </summary>
    /// <param name="claimValue">The claim value to encode in the JWT.</param>
    /// <returns>A JWT as a string.</returns>
    public string GenerateToken(TClaimValue claimValue)
    {
        var tokenDescriptor = _factory(claimValue);

        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(token);
    }

    /// <summary>
    /// Reads and validates a security token from a string representation.
    /// </summary>
    /// <param name="token">The JWT to read.</param>
    /// <returns>A <see cref="JwtSecurityToken"/> if the token is valid; otherwise, null.</returns>
    public JwtSecurityToken? ReadSecurityToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        return tokenHandler.CanReadToken(token) ? tokenHandler.ReadToken(token) as JwtSecurityToken : null;
    }
}