using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.IdentityModel.Tokens;

namespace adas_core.Domain.Utils;

/// <summary>
/// Serves as an abstract base class for providing helper functionality related to JSON Web Token (JWT) operations.
/// </summary>
/// <remarks>
/// This class is intended to be inherited by concrete implementations that define specific JWT processing behaviors.
/// </remarks>
public abstract class JwtHelper
{
    /// <summary>
    /// Creates a new JWT security token for the specified user, signing it with the provided secret using HMAC-SHA256.
    /// The token includes standard subject, name, name identifier, and unique token identifier (JTI) claims, and optionally merges any additional claims supplied.
    /// </summary>
    /// <param name="username">The subject identifier used to populate the token's Sub, Name, and NameIdentifier claims.</param>
    /// <param name="secret">The symmetric secret used to derive the signing key for the token.</param>
    /// <param name="issuer">The issuer (iss claim) to associate with the token.</param>
    /// <param name="audience">The audience (aud claim) to associate with the token.</param>
    /// <param name="expiration">The token lifetime in minutes, added to the current UTC time to compute the expiration date.</param>
    /// <param name="additionalClaims">Optional extra claims to merge into the token alongside the standard claims. If null, only the default claims are included.</param>
    /// <returns>A <see cref="JwtSecurityToken"/> signed with HMAC-SHA256 and configured with the supplied issuer, audience, expiration, and claims.</returns>
    public static JwtSecurityToken GetJwtToken(
            string username,
            string secret,
            string issuer,
            string audience,
            int expiration,
            Claim[]? additionalClaims = null
        )
        {
            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, username),
                new Claim(ClaimTypes.Name, username),
                new Claim(ClaimTypes.NameIdentifier, username),
                // this guarantees the token is unique
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            };
    
            if (additionalClaims != null)
            {
                var claimList = new List<Claim>(claims);
                claimList.AddRange(additionalClaims);
                claims = claimList.ToArray();
            }
    
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
    
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
            var expireDate = DateTime.UtcNow.AddMinutes(expiration);
    
            return new JwtSecurityToken(
                issuer,
                audience,
                expires: expireDate,
                claims: claims,
                signingCredentials: creds
            );
        }


    /// <summary>
    /// Generates a cryptographically secure refresh token by producing 64 random bytes using <see cref="RandomNumberGenerator"/> and returning the value as a Base64-encoded string.
    /// </summary>
    /// <returns>A Base64-encoded string representation of a 64-byte cryptographically random sequence suitable for use as a refresh token.</returns>
    public static string GenerateRefreshToken()
        {
            var randomNumber = new byte[64];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }

    /// <summary>
    ///     return username from claim
    /// </summary>
    /// <param name="principal"></param>
    /// <returns></returns>
    public static string? GetUsernameFromPrincipal(ClaimsPrincipal principal)
    {
        var nameClaim = principal.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.NameIdentifier));
        return nameClaim?.Value;
    }
}