using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using Oqtane.Models;

namespace Oqtane.Security
{
    public interface IJwtManager
    {
        string GenerateToken(Alias alias, ClaimsIdentity identity, string secret, string issuer, string audience, int lifetime);
        ClaimsIdentity ValidateToken(string token, string secret, string issuer, string audience);
    }

    public class JwtManager : IJwtManager
    {
        public string GenerateToken(Alias alias, ClaimsIdentity identity, string secret, string issuer, string audience, int lifetime)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(PadSecret(secret));
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(identity),
                Issuer = issuer,
                Audience = audience,
                Expires = DateTime.UtcNow.AddMinutes(lifetime),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }

        public ClaimsIdentity ValidateToken(string token, string secret, string issuer, string audience)
        {
            if (!string.IsNullOrEmpty(token))
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(PadSecret(secret));
                try
                {
                    tokenHandler.ValidateToken(token, new TokenValidationParameters
                    {
                        IssuerSigningKey = new SymmetricSecurityKey(key),
                        ValidIssuer = issuer,
                        ValidAudience = audience,
                        ValidateIssuerSigningKey = true,
                        ValidateIssuer = (!string.IsNullOrEmpty(issuer)),
                        ValidateAudience = (!string.IsNullOrEmpty(audience)),
                        ValidateLifetime = true,
                        ClockSkew = TimeSpan.Zero
                    }, out SecurityToken validatedToken);

                    var jwtToken = (JwtSecurityToken)validatedToken;
                    var identity = new ClaimsIdentity();
                    foreach (var claim in jwtToken.Claims)
                    {
                        identity.AddClaim(claim);
                    }
                    return identity;
                }
                catch
                {
                    // error validating token
                }
            }
            return null;
        }

        private string PadSecret(string secret)
        {
            // ensure secret is 256 bits
            return (secret.Length < 32) ? (secret + "????????????????????????????????").Substring(0, 32) : secret;
        }
    }
}
