using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace FrontendWebApi.Jwt
{
    public interface IJwtHelpers
    {
        TnToken GenerateToken(JwtLogin login);
    }
    public class JwtHelpers: IJwtHelpers
    {
        
        private readonly IConfiguration _configuration;
        public JwtHelpers(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        public TnToken GenerateToken(JwtLogin login)
        {
            var issuer = _configuration.GetValue<string>("JwtSettings:Issuer");
            var signKey = _configuration.GetValue<string>("JwtSettings:SignKey");
            var lifeseconds = _configuration.GetValue<int>("JwtSettings:JwtLifeSeconds");
            // 設定要加入到 JWT Token 中的聲明資訊(Claims)
            var claims = new List<Claim>();

            // 在 RFC 7519 規格中(Section#4),總共定義了 7 個預設的 Claims,我們應該只用的到兩種!
            claims.Add(new Claim(JwtRegisteredClaimNames.Iss, issuer));
            //claims.Add(new Claim(JwtRegisteredClaimNames.NameId, login.CustomerNo.ToString()));
            //claims.Add(new Claim(JwtRegisteredClaimNames.Sub, login.Username)); // User.Identity.Name
            //claims.Add(new Claim(JwtRegisteredClaimNames.Aud, "The Audience"));
            claims.Add(new Claim(JwtRegisteredClaimNames.Exp, DateTimeOffset.UtcNow.AddSeconds(lifeseconds).ToUnixTimeSeconds().ToString()));
            claims.Add(new Claim(JwtRegisteredClaimNames.Nbf, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())); // 必須為數字
            //claims.Add(new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())); // 必須為數字
            //claims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())); // JWT ID

            // 網路上常看到的這個 NameId 設定是多餘的
            //claims.Add(new Claim(JwtRegisteredClaimNames.NameId, userName));

            // 這個 Claim 也以直接被 JwtRegisteredClaimNames.Sub 取代,所以也是多餘的
            //claims.Add(new Claim(ClaimTypes.Name, userName));

            // 你可以自行擴充 "roles" 加入登入者該有的角色
            //claims.Add(new Claim("roles", "Users"));
            //claims.Add(new Claim("groupid", login.AreaCode));
            claims.Add(new Claim("userinfo_guid", login.userinfo_guid));
            claims.Add(new Claim("account", login.account));
            claims.Add(new Claim("full_name", login.full_name));
            claims.Add(new Claim("email", login.email));
            var userClaimsIdentity = new ClaimsIdentity(claims);

            // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signKey));

            // HmacSha256 有要求必須要大於 128 bits,所以 key 不能太短,至少要 16 字元以上
            // https://stackoverflow.com/questions/47279947/idx10603-the-algorithm-hs256-requires-the-securitykey-keysize-to-be-greater
            var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);

            var now = DateTime.Now;
            var expires = DateTime.Now.AddSeconds(lifeseconds);

            // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式)
            var tokenHandler = new JwtSecurityTokenHandler();

            var jst = new JwtSecurityToken(
                    issuer: issuer,//Token釋出者
                    //audience: _options.Value.Audience,//Token接受者
                    claims: claims,//攜帶的負載
                    notBefore: now,//當前時間token生成時間
                    expires: expires,//過期時間
                    signingCredentials: signingCredentials
                );
            //var jst = new SecurityTokenDescriptor
            //{
            //    Issuer = issuer,//Token釋出者
            //    //Claims = claims,//攜帶的負載
            //    //audience: _options.Value.Audience,//Token接受者
            //    NotBefore = now,//當前時間token生成時間
            //    Expires = expires,//過期時間
            //    SigningCredentials = signingCredentials,
            //    Subject = userClaimsIdentity
            //};

            var serializeToken = tokenHandler.WriteToken(jst);
            
            var data = new TnToken();
            data.token = serializeToken;
            data.type = "bearer";
            data.expires= lifeseconds;

            return data;
        }
        
    }
}