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

namespace Grand.Api.Jwt
{
    public sealed class JwtTokenBuilder
    {
        private SecurityKey securityKey;
        private bool useissuer;
        private string issuer = "";
        private bool useaudience;
        private string audience = "";
        private Dictionary<string, string> claims = new Dictionary<string, string>();
        private int expiryInMinutes = 5;

        private void EnsureArguments()
        {
            if (this.securityKey == null)
                throw new ArgumentNullException(nameof(securityKey));

            if (this.useissuer && string.IsNullOrEmpty(this.issuer))
                throw new ArgumentNullException(nameof(issuer));

            if (this.useaudience && string.IsNullOrEmpty(this.audience))
                throw new ArgumentNullException(nameof(audience));
        }

        public JwtTokenBuilder AddSecurityKey(SecurityKey securityKey)
        {
            this.securityKey = securityKey;
            return this;
        }

        public JwtTokenBuilder AddIssuer(string issuer)
        {
            this.issuer = issuer;
            this.useissuer = true;
            return this;
        }

        public JwtTokenBuilder AddAudience(string audience)
        {
            this.audience = audience;
            this.useaudience = true;
            return this;
        }

        public JwtTokenBuilder AddClaim(string type, string value)
        {
            this.claims.Add(type, value);
            return this;
        }

        public JwtTokenBuilder AddClaims(Dictionary<string, string> claims)
        {
            foreach (var item in claims)
            {
                if (!this.claims.ContainsKey(item.Key) && !string.IsNullOrEmpty(item.Value))
                {
                    this.claims.Add(item.Key, item.Value);
                }
            }
            return this;
        }

        public JwtTokenBuilder AddExpiry(int expiryInMinutes)
        {
            this.expiryInMinutes = expiryInMinutes;
            return this;
        }

        public JwtToken Build()
        {
            EnsureArguments();

            var claims = new List<Claim>
            {
              new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            }
            .Union(this.claims.Select(item => new Claim(item.Key, item.Value)));

            var token = new JwtSecurityToken(
                              issuer: this.issuer,
                              audience: this.audience,
                              claims: claims,
                              expires: DateTime.UtcNow.AddMinutes(expiryInMinutes),
                              signingCredentials: new SigningCredentials(
                                                        this.securityKey,
                                                        SecurityAlgorithms.HmacSha256));

            return new JwtToken(token);
        }

    }
}
