package com.zsElectric.boot.common.util.electric.queryToken;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * JWT Token工具类
 * 专门用于第三方接口的Token生成和验证
 */
@Slf4j
@Component
public class JwtTokenUtil {

    private final SecretKey secretKey;
    private final Long expireSeconds;
    private final RedisTemplate<String, Object> redisTemplate;
    
    // Redis key前缀
    private static final String REDIS_TOKEN_PREFIX = "third_party:token:";
    private static final String REDIS_OPERATOR_TOKENS_PREFIX = "third_party:operator_tokens:";

    public JwtTokenUtil(@Value("${third-party.jwt.secret:vgct1TZ4ZikKjaaeIiq3LUwIvpmcgYa6}") String secret,
                       @Value("${third-party.jwt.expire:7200}") Long expireSeconds,
                       RedisTemplate<String, Object> redisTemplate) {
        // 生成安全的密钥
        this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
        this.expireSeconds = expireSeconds;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 生成JWT Token
     */
    public String generateToken(String operatorId) {

        // 先查询Redis中是否已有有效Token
        String existingToken = getExistingValidToken(operatorId);
        if (existingToken != null) {
            return existingToken;
        }
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expireSeconds * 1000);
        String token = Jwts.builder()
                .setSubject(operatorId)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(secretKey, SignatureAlgorithm.HS512)
                .compact();
        // 存储到Redis，支持Token主动撤销
        storeTokenInRedis(token, operatorId);
        return token;
    }

    /**
     * 验证JWT Token有效性
     */
    public boolean validateToken(String token) {
        try {
            log.info("开始验证Token: {}...", token.substring(0, Math.min(20, token.length())));
            // 首先检查Redis中是否存在该token（支持主动撤销）
            if (!isTokenInRedis(token)) {
                log.error("Token在Redis中不存在，可能已被撤销或过期");
                return false;
            }
            log.info("Token在Redis中存在，继续验证JWT签名");
            // 验证JWT签名和过期时间
            Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(token);
            log.info("Token验证通过");
            return true;
        } catch (ExpiredJwtException e) {
            log.error("Token已过期: {}" , e.getMessage());
        } catch (UnsupportedJwtException e) {
            log.error("不支持的Token格式: {}" , e.getMessage());
        } catch (MalformedJwtException e) {
            log.error("Token格式错误: {}" , e.getMessage());
        } catch (SignatureException e) {
            log.error("Token签名验证失败: {}" , e.getMessage());
        } catch (IllegalArgumentException e) {
            log.error("Token参数错误: {}" , e.getMessage());
        }
        return false;
    }

    /**
     * 从Token中提取OperatorID
     */
    public String getOperatorIdFromToken(String token) {
        try {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(secretKey)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
            return claims.getSubject();
        } catch (Exception e) {
            System.out.println("从Token提取OperatorID失败: " + e.getMessage());
            return null;
        }
    }

    /**
     * 存储Token到Redis
     */
    private void storeTokenInRedis(String token, String operatorId) {
        String tokenKey = REDIS_TOKEN_PREFIX + token;
        String operatorTokensKey = REDIS_OPERATOR_TOKENS_PREFIX + operatorId;
        
        // 存储token基本信息，设置过期时间
        redisTemplate.opsForValue().set(tokenKey, operatorId, expireSeconds, TimeUnit.SECONDS);
        
        // 将token添加到operator的token集合中
        redisTemplate.opsForSet().add(operatorTokensKey, token);
        redisTemplate.expire(operatorTokensKey, expireSeconds, TimeUnit.SECONDS);
    }

    /**
     * 检查Token是否在Redis中
     */
    private boolean isTokenInRedis(String token) {
        String tokenKey = REDIS_TOKEN_PREFIX + token;
        Boolean exists = redisTemplate.hasKey(tokenKey);
        log.info("检查Token是否在Redis中, key: {}, exists: {}", tokenKey, exists);
        return Boolean.TRUE.equals(exists);
    }

    /**
     * 获取Token剩余有效时间
     */
    public Long getRemainingTTL(String token) {
        String tokenKey = REDIS_TOKEN_PREFIX + token;
        return redisTemplate.getExpire(tokenKey, TimeUnit.SECONDS);
    }

    /**
     * 撤销Token（使其立即失效）
     */
    public void revokeToken(String token) {
        String operatorId = getOperatorIdFromToken(token);
        if (operatorId != null) {
            String tokenKey = REDIS_TOKEN_PREFIX + token;
            String operatorTokensKey = REDIS_OPERATOR_TOKENS_PREFIX + operatorId;
            
            redisTemplate.delete(tokenKey);
            redisTemplate.opsForSet().remove(operatorTokensKey, token);
        }
    }

    /**
     * 查询操作员现有的有效Token
     * 如果Token存在且剩余过期时间大于1分钟，则返回该Token
     */
    private String getExistingValidToken(String operatorId) {
        String operatorTokensKey = REDIS_OPERATOR_TOKENS_PREFIX + operatorId;
        Set<Object> tokenObjects = redisTemplate.opsForSet().members(operatorTokensKey);
        Set<String> tokens = tokenObjects.stream()
                .filter(Objects::nonNull)
                .map(Object::toString)
                .collect(Collectors.toSet());

        if (tokens != null && !tokens.isEmpty()) {
            for (String token : tokens) {
                String tokenKey = REDIS_TOKEN_PREFIX + token;
                // 检查token是否存在
                if (Boolean.TRUE.equals(redisTemplate.hasKey(tokenKey))) {
                    // 获取剩余过期时间
                    Long remainingTTL = redisTemplate.getExpire(tokenKey, TimeUnit.SECONDS);
                    // 如果剩余时间大于1分钟(60秒)，则返回该token
                    if (remainingTTL != null && remainingTTL > 60) {
                        return token;
                    }
                }
            }
        }
        return null;
    }
}