package cn.reghao.tnb.account.app.util;

import cn.reghao.jutil.jdk.string.IDObfuscation;
import cn.reghao.tnb.account.app.model.vo.RefreshPayload;
import cn.reghao.tnb.account.app.security.form.AccountAuthToken;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * JWT 令牌
 *
 * @author reghao
 * @date 2019-11-17 23:10:58
 */
public class JwtUtil {
    public static final String JWT_PREFIX = "Bearer ";
    public static final String AUTH_HEADER = "Authorization";

    /**
     * 生成一个 token
     *
     * @param
     * @return
     * @date 2019-11-21 下午4:39
     */
    public static String createAccessToken(AccountAuthToken authToken, String userIdStr, long expireAt, String signKey) {
        String jti = UUID.randomUUID().toString().replace("-", "");
        return Jwts.builder()
                .claim("plat", authToken.getPlat())
                .claim("loginId", authToken.getLoginId())
                .claim("loginType", authToken.getLoginType())
                .claim("authorities", authToken.getAuthorities().stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toList())
                        .toString())
                .setSubject(userIdStr)
                .setExpiration(new Date(expireAt))
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setId(jti)
                .compact();
    }

    public static String createAccessToken(AccountAuthToken authToken, String userIdStr, long expireAt, RSAPrivateKey privateKey) {
        // 根据 org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter 中的 WELL_KNOWN_AUTHORITIES_CLAIM_NAMES 字段
        // 将用户的 authorities 设置到 scope claim
        // TODO authorities claim 待删除
        String jti = UUID.randomUUID().toString().replace("-", "");
        return Jwts.builder()
                .claim("plat", authToken.getPlat())
                .claim("loginId", authToken.getLoginId())
                .claim("loginType", authToken.getLoginType())
                .claim("scope", authToken.getAuthorities().stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toList()))
                .claim("authorities", authToken.getAuthorities().stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toList())
                        .toString())
                .setSubject(userIdStr)
                .setExpiration(new Date(expireAt))
                .signWith(SignatureAlgorithm.RS256, privateKey)
                .setId(jti)
                .compact();
    }

    /**
     * 从 jwt token 中解析出已认证的 Authentication 对象
     * 过期的 token 会抛出 ExpiredJwtException 异常
     *
     * @param
     * @return
     * @date 2023-02-17 17:36:34
     */
    public static AccountAuthToken getAuthentication(String token, String signKey) {
        Claims claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token).getBody();
        Integer plat = (Integer) claims.get("plat");
        String loginId = (String) claims.get("loginId");
        int loginType = (int) claims.get("loginType");
        String userId = claims.getSubject();
        // TODO userId 是系统分配且固定的，但需要检查用户的 roles 是否发生变化
        String roles = (String) claims.get("authorities");
        long expireAt = claims.getExpiration().getTime();
        if (plat == null || loginId == null || userId == null || roles == null) {
            return null;
        }

        List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(roles);
        return new AccountAuthToken(plat, loginId, loginType, userId, authorities);
    }

    public static AccountAuthToken getAuthentication1(String token, RSAPublicKey signKey, IDObfuscation userIdObfuscation) {
        Claims claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token).getBody();
        Integer plat = (Integer) claims.get("plat");
        String loginId = (String) claims.get("loginId");
        int loginType = (int) claims.get("loginType");
        String userIdStr = claims.getSubject();
        // TODO userId 是系统分配且固定的，但需要检查用户的 roles 是否发生变化
        String roles = (String) claims.get("authorities");
        long expireAt = claims.getExpiration().getTime();
        if (plat == null || loginId == null || userIdStr == null || roles == null) {
            return null;
        }

        List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(roles);
        long userId = userIdObfuscation.restore(userIdStr);
        return new AccountAuthToken(plat, loginId, loginType, userId+"", authorities);
    }

    /**
     * 创建刷新令牌
     *
     * @param
     * @return
     * @date 2023-02-17 15:20:33
     */
    public static String createRefreshToken(AccountAuthToken authToken, long expireAt, String signKey) {
        return Jwts.builder()
                .claim("plat", authToken.getPlat())
                .claim("loginId", authToken.getLoginId())
                .setSubject(String.valueOf(authToken.getUserId()))
                .setExpiration(new Date(expireAt))
                .signWith(SignatureAlgorithm.HS256, signKey)
                .compact();
    }

    /**
     * 解析刷新令牌
     *
     * @param
     * @return
     * @date 2023-02-17 15:22:54
     */
    public static RefreshPayload parseRefreshToken(String token, String signKey) {
        Claims claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token).getBody();
        long userId = Long.parseLong(claims.getSubject());
        int plat = (Integer) claims.get("plat");
        String loginId = (String) claims.get("loginId");
        return new RefreshPayload(userId, plat, loginId);
    }
}
