package com.ruoyi.system.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.domain.model.LoginUserDto;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author: Zou Tiancong
 * @Date: 2021/12/1 0001 10:37
 * @Annotation:jwtToken
 */
public class JwtUtil {

    // 设置过期时间
    private static final long EXPIRE_DATE = 1296000*1000;
    // token秘钥
    private static final String TOKEN_SECRET = "TEST-AUTH-TOKEN";

    // 实现签名方法
    public static String setToken(String tokenKey,LoginUserDto user) {
        RedisCache redisCache= SpringUtils.getBean(RedisCache.class);

        String token = "";
        try {
            // 这里将用户存入了Token，在下面的解析中，也会有解析的方法可以获取到Token里面的数据
            // Token过期的时间
            Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_DATE);
            // 秘钥及加密算法
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            // 设置头部信息,类型以及签名所用的算法
            Map<String, Object> header = new HashMap<>();
            header.put("typ", "JWT");
            header.put("alg", "HS256");
            // 携带用户信息，存入token，生成签名
            // 生成唯一的JTI，用于防止重放攻击和token撤销
            String jti =tokenKey+user.getUserId()+":"+ IdUtils.fastUUID();
            // 设置 tokenId 到 userDto 中
            if (user != null) {
                user.setTokenId(jti);
            }
            token = JWT.create()
                    .withHeader(header) //设置头部信息Header
                    .withIssuer("TEST") //设置签名是由谁生成
                    .withSubject("AUTH-TOKEN") //设置签名的主题
                    .withAudience(user.getUserName()) //设置签名的观众
                    .withIssuedAt(new Date()) //设置生成签名的时间
                    .withExpiresAt(expireDate) //设置签名过期的时间
                    .withClaim("id", user.getUserId() != null ? String.valueOf(user.getUserId()) : null) //自定义信息，将Long转换为String
                    .withClaim("userName", user.getUserName())//自定义信息
                    .withClaim("provider", user.getProvider())//登录渠道 apple/google/line/phone（三方登录标记）
                    .withJWTId(jti) //jwt的唯一标识符，每个token都有唯一的JTI，用于防止重放攻击和token撤销
                    .sign(algorithm);
            redisCache.setCacheObject(jti, user, (int)EXPIRE_DATE, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return token;
    }

    // 实现签名方法
    public static String token(String id, String userName) {

        String token = "";
        try {
            // 这里将用户存入了Token，在下面的解析中，也会有解析的方法可以获取到Token里面的数据
            // Token过期的时间
            Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_DATE);
            // 秘钥及加密算法
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            // 设置头部信息,类型以及签名所用的算法
            Map<String, Object> header = new HashMap<>();
            header.put("typ", "JWT");
            header.put("alg", "HS256");
            // 携带用户信息，存入token，生成签名
            // 生成唯一的JTI，用于防止重放攻击和token撤销
            String jti = UUID.randomUUID().toString().replace("-", "");
            token = JWT.create()
                    .withHeader(header) //设置头部信息Header
                    .withIssuer("TEST") //设置签名是由谁生成
                    .withSubject("AUTH-TOKEN") //设置签名的主题
                    .withAudience(userName) //设置签名的观众
                    .withIssuedAt(new Date()) //设置生成签名的时间
                    .withExpiresAt(expireDate) //设置签名过期的时间
                    .withClaim("id", id) //自定义信息
                    .withClaim("userName", userName)//自定义信息
                    .withJWTId(jti) //jwt的唯一标识符，每个token都有唯一的JTI，用于防止重放攻击和token撤销
                    .sign(algorithm);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return token;
    }

    // 验证token
    public static boolean verify(String token) {
        /**
         * @desc 验证token，通过返回true
         * @params [token]需要校验的串
         **/
        try {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            String subject = jwt.getSubject();
            List<String> audience = jwt.getAudience();
            Map<String, Claim> claims = jwt.getClaims();
            for (Map.Entry<String, Claim> entry : claims.entrySet()){
                String key = entry.getKey();
                Claim claim = entry.getValue();
                String value;
                // 根据claim类型进行解析：日期类型使用asDate()，数字类型使用asLong()，字符串类型使用asString()
                if (claim.isNull()) {
                    value = "null";
                } else if ("exp".equals(key) || "iat".equals(key) || "nbf".equals(key)) {
                    // 日期类型claim
                    Date dateValue = claim.asDate();
                    value = dateValue != null ? String.valueOf(dateValue.getTime()) : "null";
                } else if ("id".equals(key)) {
                    // 数字类型claim（已转换为String，但兼容处理）
                    value = claim.asString();
                    if (value == null) {
                        Long longValue = claim.asLong();
                        value = longValue != null ? String.valueOf(longValue) : "null";
                    }
                } else {
                    // 字符串类型claim
                    value = claim.asString();
                    if (value == null) {
                        value = "null";
                    }
                }
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Token已过期,需要重新登录");
        }
        return false;
    }

    // 验证token
    public static Map<String,Object> verifyToken(String token) {
        Map<String,Object> result=new HashMap<>();
        result.put("valid",false);
        result.put("jti","");
        /**
         * @desc 验证token，通过返回true
         * @params [token]需要校验的串
         **/
        try {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            String subject = jwt.getSubject();
            String jti= jwt.getId();
            result.put("jti",jti);
            List<String> audience = jwt.getAudience();
            Map<String, Claim> claims = jwt.getClaims();
            for (Map.Entry<String, Claim> entry : claims.entrySet()){
                String key = entry.getKey();
                Claim claim = entry.getValue();
                String value;
                // 根据claim类型进行解析：日期类型使用asDate()，数字类型使用asLong()，字符串类型使用asString()
                if (claim.isNull()) {
                    value = "null";
                } else if ("exp".equals(key) || "iat".equals(key) || "nbf".equals(key)) {
                    // 日期类型claim
                    Date dateValue = claim.asDate();
                    value = dateValue != null ? String.valueOf(dateValue.getTime()) : "null";
                } else if ("id".equals(key)) {
                    // 数字类型claim（已转换为String，但兼容处理）
                    value = claim.asString();
                    if (value == null) {
                        Long longValue = claim.asLong();
                        value = longValue != null ? String.valueOf(longValue) : "null";
                    }
                } else {
                    // 字符串类型claim
                    value = claim.asString();
                    if (value == null) {
                        value = "null";
                    }
                }
                result.put(key, value);
            }
            result.replace("valid",true);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Token已过期,需要重新登录");
        }
        result.replace("valid",false);
        return result;
    }



    // 生成token
    public static String setToken(String id,  String userName) {
        String token = token(id, userName);
        return token;
    }


    // 获取用户id
    public String getusid(String token) {
        String id = null;
        try {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            // 直接从claims中获取用户id，而不是从JTI获取
            Claim idClaim = jwt.getClaim("id");
            if (idClaim != null && !idClaim.isNull()) {
                id = idClaim.asString();
            }
            return id;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Token已过期,需要重新登录");
            return id;
        }
    }

    // 获取JWT的唯一标识符(JTI)
    public String getJti(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            return jwt.getId();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Token已过期,需要重新登录");
            return null;
        }
    }
}
