package com.xindazhou.framework.security.core.jwt;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 服务间调用的 JWT 工具类
 * 支持生成和验证 JWT Token
 *
 * @author xindazhou
 */
@Slf4j
public class ServiceJwtUtil {

    private static RSAPrivateKey privateKey;
    private static RSAPublicKey publicKey;
    private static Algorithm algorithm;
    private static JWTVerifier verifier;
    private static String serviceName;
    private static Long expireTime = 3600L; // 默认 1 小时

    /**
     * 初始化密钥对
     *
     * @param privateKeyPath 私钥路径（用于生成 Token）
     * @param publicKeyPath  公钥路径（用于验证 Token）
     * @param serviceName    服务名称
     * @param expireTime     过期时间（秒）
     * @param resourceLoader Spring ResourceLoader
     */
    public static void init(String privateKeyPath, String publicKeyPath, String serviceName, Long expireTime, ResourceLoader resourceLoader) {
        try {
            ServiceJwtUtil.serviceName = serviceName;
            if (expireTime != null && expireTime > 0) {
                ServiceJwtUtil.expireTime = expireTime;
            }

            // 加载私钥（用于生成 Token）
            if (StrUtil.isNotEmpty(privateKeyPath)) {
                String privateKeyContent = readKeyFile(privateKeyPath, resourceLoader);
                privateKey = getPrivateKey(privateKeyContent);
            }

            // 加载公钥（用于验证 Token）
            if (StrUtil.isNotEmpty(publicKeyPath)) {
                String publicKeyContent = readKeyFile(publicKeyPath, resourceLoader);
                publicKey = getPublicKey(publicKeyContent);
                algorithm = Algorithm.RSA256(publicKey, privateKey);
                verifier = JWT.require(algorithm).build();
            }

            log.info("服务间 JWT 初始化成功: serviceName={}, expireTime={}s", serviceName, ServiceJwtUtil.expireTime);
        } catch (Exception e) {
            log.error("服务间 JWT 初始化失败: privateKeyPath={}, publicKeyPath={}", privateKeyPath, publicKeyPath, e);
            throw new RuntimeException("服务间 JWT 初始化失败", e);
        }
    }

    /**
     * 生成服务间调用的 JWT Token
     *
     * @param userId   用户ID
     * @param userType 用户类型
     * @return JWT Token
     */
    public static String generateToken(Long userId, Integer userType) {
        return generateToken(userId, userType, null, null);
    }

    /**
     * 生成服务间调用的 JWT Token
     *
     * @param userId   用户ID
     * @param userType 用户类型
     * @param tenantId 租户ID
     * @param scopes   权限范围
     * @return JWT Token
     */
    public static String generateToken(Long userId, Integer userType, Long tenantId, String scopes) {
        if (privateKey == null) {
            log.warn("私钥未初始化，无法生成 JWT Token");
            return null;
        }

        Date now = new Date();
        Date expireTime = new Date(now.getTime() + ServiceJwtUtil.expireTime * 1000);

        Map<String, Object> claims = new HashMap<>();
        claims.put("uid", userId != null ? userId.toString() : null);
        claims.put("userType", userType);
        if (tenantId != null) {
            claims.put("tenantId", tenantId);
        }
        if (StrUtil.isNotEmpty(scopes)) {
            claims.put("scopes", scopes);
        }
        claims.put("service", serviceName); // 标识是哪个服务生成的

        return JWT.create()
                .withIssuer(serviceName) // 发行者
                .withSubject(userId != null ? userId.toString() : "system") // 主题
                .withIssuedAt(now) // 签发时间
                .withExpiresAt(expireTime) // 过期时间
                .withClaim("uid", userId != null ? userId.toString() : null)
                .withClaim("userType", userType)
                .withClaim("tenantId", tenantId != null ? tenantId : 0L)
                .withClaim("scopes", scopes)
                .withClaim("service", serviceName)
                .sign(algorithm);
    }

    /**
     * 验证 JWT Token
     *
     * @param token JWT Token
     * @return 解码后的 JWT，验证失败返回 null
     */
    public static DecodedJWT verifyToken(String token) {
        if (StrUtil.isEmpty(token)) {
            return null;
        }
        if (verifier == null) {
            log.warn("JWT 验证器未初始化");
            return null;
        }
        try {
            return verifier.verify(token);
        } catch (JWTVerificationException e) {
            log.debug("JWT Token 验证失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 从 Token 中获取用户ID
     *
     * @param token JWT Token
     * @return 用户ID，验证失败返回 null
     */
    public static Long getUserId(String token) {
        DecodedJWT jwt = verifyToken(token);
        if (jwt == null) {
            return null;
        }
        String userIdStr = jwt.getClaim("uid").asString();
        if (StrUtil.isEmpty(userIdStr)) {
            return null;
        }
        try {
            return Long.parseLong(userIdStr);
        } catch (NumberFormatException e) {
            log.warn("JWT Token 中的用户ID格式错误: {}", userIdStr);
            return null;
        }
    }

    /**
     * 从 Token 中获取用户类型
     *
     * @param token JWT Token
     * @return 用户类型，验证失败返回 null
     */
    public static Integer getUserType(String token) {
        DecodedJWT jwt = verifyToken(token);
        if (jwt == null) {
            return null;
        }
        return jwt.getClaim("userType").asInt();
    }

    /**
     * 从 Token 中获取租户ID
     *
     * @param token JWT Token
     * @return 租户ID，验证失败返回 null
     */
    public static Long getTenantId(String token) {
        DecodedJWT jwt = verifyToken(token);
        if (jwt == null) {
            return null;
        }
        return jwt.getClaim("tenantId").asLong();
    }

    /**
     * 从 Token 中获取服务名称
     *
     * @param token JWT Token
     * @return 服务名称，验证失败返回 null
     */
    public static String getServiceName(String token) {
        DecodedJWT jwt = verifyToken(token);
        if (jwt == null) {
            return null;
        }
        return jwt.getClaim("service").asString();
    }

    /**
     * 读取密钥文件内容
     */
    private static String readKeyFile(String keyPath, ResourceLoader resourceLoader) throws IOException {
        if (keyPath.startsWith("classpath:")) {
            Resource resource = resourceLoader.getResource(keyPath);
            return new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
        } else {
            return ResourceUtil.readUtf8Str(keyPath);
        }
    }

    /**
     * 从 PEM 格式的私钥字符串中提取 RSA 私钥
     */
    private static RSAPrivateKey getPrivateKey(String privateKeyContent) throws Exception {
        // 移除 PEM 格式的头部和尾部
        String privateKeyPEM = privateKeyContent
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replace("-----BEGIN RSA PRIVATE KEY-----", "")
                .replace("-----END RSA PRIVATE KEY-----", "")
                .replaceAll("\\s", "");

        // Base64 解码
        byte[] keyBytes = Base64.getDecoder().decode(privateKeyPEM);

        // 创建 PKCS8EncodedKeySpec
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);

        // 生成私钥
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return (RSAPrivateKey) keyFactory.generatePrivate(spec);
    }

    /**
     * 从 PEM 格式的公钥字符串中提取 RSA 公钥
     */
    private static RSAPublicKey getPublicKey(String publicKeyContent) throws Exception {
        // 移除 PEM 格式的头部和尾部
        String publicKeyPEM = publicKeyContent
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replaceAll("\\s", "");

        // Base64 解码
        byte[] keyBytes = Base64.getDecoder().decode(publicKeyPEM);

        // 创建 X509EncodedKeySpec
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);

        // 生成公钥
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return (RSAPublicKey) keyFactory.generatePublic(spec);
    }

}

