package com.CUST.brain.common.utils;

import com.CUST.brain.common.constant.RedisKeyConstants;
import com.CUST.brain.common.exception.BusinessException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.Base64;

/**
 * JWT Token工具类
 */
@Slf4j
@Component
public class JwtTokenUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration;

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Resource
    private RedisUtils redisUtils;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 生成token
     *
     * @param userId 用户ID
     * @param username 用户名
     * @return token
     */
    public String generateToken(String userId, String username) {
        log.info("生成token用的密钥片段: {}...{}", secret.substring(0, 8), secret.substring(secret.length() - 8));
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("username", username);
        return generateToken(claims);
    }

    /**
     * 从token中获取用户ID
     *
     * @param token token
     * @return 用户ID
     */
    public String getUserIdFromToken(String token) {
        String userId;
        try {
            Claims claims = getClaimsFromToken(token);
            userId = claims.get("userId", String.class);
        } catch (Exception e) {
            log.error("从token中获取用户ID失败: {}", e.getMessage());
            throw new BusinessException(401, "无效的token");
        }
        return userId;
    }

    /**
     * 从token中获取用户名
     *
     * @param token token
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.get("username", String.class);
        } catch (Exception e) {
            log.error("从token中获取用户名失败: {}", e.getMessage());
            throw new BusinessException(401, "无效的token");
        }
        return username;
    }

    /**
     * 验证token是否有效
     *
     * @param token token
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        try {
            // 1. 检查token是否在黑名单中
            String blacklistKey = RedisKeyConstants.TOKEN_BLACKLIST + token;
            if (redisUtils.hasKey(blacklistKey)) {
                log.warn("token在黑名单中: {}", token);
                return false;
            }

            // 2. 检查token是否过期
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            if (expiration.before(new Date())) {
                log.warn("token已过期: {}", token);
                return false;
            }

            // 3. 检查token是否在Redis中存在
            String userId = claims.get("userId", String.class);
            String tokenKey = String.format(RedisKeyConstants.USER_TOKEN, userId);
            Object cachedTokenObj = redisUtils.get(tokenKey);
            if (cachedTokenObj == null) {
                log.warn("token不存在: {}", token);
                return false;
            }
            String cachedToken = cachedTokenObj.toString();
            log.info("校验token: 传入token={}, Redis中token={}", token, cachedToken);
            if (!cachedToken.equals(token)) {
                log.warn("token已更新: {}", token);
                return false;
            }

            return true;
        } catch (Exception e) {
            log.error("验证token失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 刷新token
     *
     * @param token 原token
     * @return 新token
     */
    public String refreshToken(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            claims.put("created", new Date());
            String newToken = generateToken(claims);
            
            // 更新Redis中的token
            String userId = claims.get("userId", String.class);
            String tokenKey = String.format(RedisKeyConstants.USER_TOKEN, userId);
            // 将毫秒转换为秒
            long expireSeconds = TimeUnit.MILLISECONDS.toSeconds(expiration);
            redisUtils.set(tokenKey, newToken, expireSeconds);
            
            return newToken;
        } catch (Exception e) {
            log.error("刷新token失败: {}", e.getMessage());
            throw new BusinessException(401, "刷新token失败");
        }
    }

    /**
     * 获取Base64解码后的密钥字节数组
     */
    private byte[] getSecretKeyBytes() {
        log.info("JWT密钥片段: {}...{}", secret.substring(0, 8), secret.substring(secret.length() - 8));
        return Base64.getDecoder().decode(secret);
    }

    /**
     * 从token中获取JWT中的负载
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            log.info("校验token:  {}", token);
            log.info("校验token用的密钥片段: {}...{}", secret.substring(0, 8), secret.substring(secret.length() - 8));
            claims = Jwts.parser()
                    .setSigningKey(getSecretKeyBytes())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.error("JWT格式验证失败: {}", e.getMessage());
            throw new BusinessException(401, "无效的token");
        }
        return claims;
    }

    /**
     * 根据负载生成JWT token
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, getSecretKeyBytes())
                .compact();
    }

    /**
     * 生成token的过期时间
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    /**
     * 获取token的完整格式（带前缀）
     */
    public String getTokenHeader() {
        return tokenHeader;
    }

    /**
     * 获取token的前缀
     */
    public String getTokenHead() {
        return tokenHead;
    }
} 