using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Ropin.Inspection.Model;
using Ropin.Inspection.Model.ViewModel;

namespace Ropin.Inspection.Api.Common.Token
{
    public enum TokenType
    {
        AccessToken = 1,
        RefreshToken = 2
    }
    public class TokenHelper : ITokenHelper
    {
        private readonly IOptions<JWTConfig> _options;
        public TokenHelper(IOptions<JWTConfig> options)
        {
            _options = options;
        }

        public Token CreateAccessToken(TsysUserDetailViewModel user)
        {
            Claim[] claims = { new Claim(ClaimTypes.NameIdentifier, user.C_UserID.ToString()), new Claim(ClaimTypes.Name, user.C_Name), new Claim(ClaimTypes.Role, user.RoleIds) ,new Claim(ClaimTypes.PrimaryGroupSid, user.C_OrgCode.ToString()) };

            return CreateToken(claims, TokenType.AccessToken);
        }

        public ComplexToken CreateToken(TsysUserDetailViewModel user)
        {
            Claim[] claims = { 
                new Claim(ClaimTypes.NameIdentifier, user.C_UserID.ToString()), 
                new Claim(ClaimTypes.Name, user.C_Name),
                new Claim(ClaimTypes.Role, user.RoleNames??""),
                new Claim(ClaimTypes.Rsa, user.C_LicenseCode),
                new Claim(ClaimTypes.Sid, user.LicenseTypeCode),
                new Claim(ClaimTypes.Actor, user.OrgTypeCode),
                new Claim(ClaimTypes.PrimaryGroupSid, user.C_OrgCode.ToString())};


            return new ComplexToken
            {
                AccessToken = CreateToken(claims, TokenType.AccessToken),
                RefreshToken = CreateToken(claims, TokenType.RefreshToken),
                User = user
            };
        }

        /// <summary>
        /// 用于创建AccessToken和RefreshToken。
        /// 这里AccessToken和RefreshToken只是过期时间不同
        /// 因为RefreshToken只是用于刷新AccessToken,其内容可以简单一些。
        /// 而AccessToken可能会附加一些其他的Claim。
        /// </summary>
        /// <param name="claims"></param>
        /// <param name="tokenType"></param>
        /// <returns></returns>
        private Token CreateToken(Claim[] claims, TokenType tokenType)
        {
            claims = claims.Append(new Claim("TokenType", tokenType.ToString())).ToArray();
            var now = DateTime.Now;
            var expires = now.Add(TimeSpan.FromMinutes(tokenType.Equals(TokenType.AccessToken) ? _options.Value.AccessTokenExpiresMinutes : _options.Value.RefreshTokenExpiresMinutes));
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken(
                issuer: _options.Value.Issuer,
                audience: _options.Value.Audience,
                claims: claims,
                notBefore: now,
                expires: expires,
                //签名证书
                signingCredentials: creds);
        //signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)), SecurityAlgorithms.HmacSha256));
            return new Token { TokenContent = new JwtSecurityTokenHandler().WriteToken(token), Expires = expires };
        }

        public ComplexToken RefreshTokenByClaimsPrincipal(ClaimsPrincipal claimsPrincipal)
        {
            if (claimsPrincipal!=null)
            {
                //claimsPrincipal.Claims.Any(m => m.Type.Equals("TokenType") && m.Value.Equals(TokenType.RefreshToken.ToString()))
                if (claimsPrincipal.Claims.Any(m => m.Type == ("TokenType") && m.Value == (TokenType.RefreshToken.ToString())))
                {
                    var code = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.NameIdentifier));
                    var name = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Name));
                    var RoleNames = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Role));
                    var License = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Rsa));
                    var LicenseTypeCode = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Sid));
                    var OrgTypeCode = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Actor));
                    var PrimaryGroupSid = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.PrimaryGroupSid));
                    if (null != code)
                    {
                        //return CreateAccessToken(new TsysUserDetailViewModel { C_UserID = Guid.Parse(code.Value) , C_Name = name.Value ,RoleNames = RoleNames.Value});
                        return  CreateToken(new TsysUserDetailViewModel
                        {
                            C_UserID = Guid.Parse(code.Value),
                            C_Name = name.Value,
                            RoleNames = RoleNames.Value,
                            C_OrgCode = Guid.Parse(PrimaryGroupSid.Value),
                            C_LicenseCode = License.Value,
                            LicenseTypeCode = LicenseTypeCode.Value,
                            OrgTypeCode = OrgTypeCode.Value
                        });
                    }
                }
            }
            return null;
        }
        public ComplexToken RefreshToken(string refreshToken)
        {
            ClaimsPrincipal claimsPrincipal = GetClaimsPrincipalByToken(refreshToken);
            return RefreshTokenByClaimsPrincipal(claimsPrincipal);
        }

        private ClaimsPrincipal GetClaimsPrincipalByToken(string token)
        {
            try
            {
                var tokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)),
                    ClockSkew = TimeSpan.Zero,
                    ValidateLifetime = false // 不验证过期时间!!!
                };

                var jwtTokenHandler = new JwtSecurityTokenHandler();

                var claimsPrincipal =
                    jwtTokenHandler.ValidateToken(token, tokenValidationParameters, out var validatedToken);

                var validatedSecurityAlgorithm = validatedToken is JwtSecurityToken jwtSecurityToken
                                                 && jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,
                                                     StringComparison.InvariantCultureIgnoreCase);

                return validatedSecurityAlgorithm ? claimsPrincipal : null;
            }
            catch
            {
                return null;
            }
        }

        public Token RefreshToken(ClaimsPrincipal claimsPrincipal)
        {
            if (claimsPrincipal.Claims.Any(m => m.Type.Equals("TokenType") && m.Value.Equals(TokenType.RefreshToken.ToString())))
            {
                var code = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.NameIdentifier));
                var name = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Name));
                var RoleNames = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Role));
                var PrimaryGroupSid = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.PrimaryGroupSid));
                var LicenseCode = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Rsa));
                var LicenseTypeCode = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Sid));
                var OrgTypeCode = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.Actor));
                if (null != code)
                {
                    return CreateAccessToken(new TsysUserDetailViewModel { C_UserID = Guid.Parse(code.Value) ,C_LicenseCode = LicenseCode.Value, LicenseTypeCode = LicenseTypeCode.Value, C_Name = name.Value ,RoleNames = RoleNames.Value, C_OrgCode = Guid.Parse(PrimaryGroupSid.Value),OrgTypeCode = OrgTypeCode.Value });
                }
            }

            return null;
        }
    }
}