package com.ruoyi.credit.interceptor;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import com.ruoyi.common.utils.StringUtils;

/**
 * API签名验证拦截器
 * 
 * 实现API请求的数字签名验证，确保请求的完整性和来源可信
 * 
 * @author ruoyi
 * @date 2025-12-19
 */
@Component
public class ApiSignatureInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(ApiSignatureInterceptor.class);

    @Value("${api.signature.enabled:true}")
    private Boolean signatureEnabled;

    @Value("${api.signature.secretKey:default_secret_key}")
    private String secretKey;

    @Value("${api.signature.timestampTolerance:300}")
    private long timestampToleranceSeconds;

    // 需要签名验证的API路径前缀
    private static final String[] SIGNATURE_REQUIRED_PATHS = {
        "/api/credit/financing",
        "/api/payment",
        "/api/blockchain"
    };

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 安全优先：如果配置未正确加载，默认启用签名验证
        boolean isSignatureEnabled = (signatureEnabled != null) ? signatureEnabled : true;
        
        if (!isSignatureEnabled) {
            return true;
        }

        String requestPath = request.getRequestURI();
        
        // 检查是否需要签名验证
        if (!requiresSignatureVerification(requestPath)) {
            return true;
        }

        try {
            if (!verifySignature(request)) {
                logger.warn("API签名验证失败: URI={}, IP={}", requestPath, getClientIp(request));
                
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().write("{\"code\":401,\"msg\":\"签名验证失败\"}");
                
                return false;
            }
        } catch (Exception e) {
            logger.error("API签名验证异常: URI={}, Error={}", requestPath, e.getMessage());
            
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":400,\"msg\":\"签名格式错误\"}");
            
            return false;
        }

        return true;
    }

    /**
     * 检查请求路径是否需要签名验证
     */
    private boolean requiresSignatureVerification(String requestPath) {
        return Arrays.stream(SIGNATURE_REQUIRED_PATHS)
                .anyMatch(requestPath::startsWith);
    }

    /**
     * 验证API签名
     */
    private boolean verifySignature(HttpServletRequest request) throws Exception {
        // 获取签名相关的请求头
        String signature = request.getHeader("X-Signature");
        String timestamp = request.getHeader("X-Timestamp");
        String nonce = request.getHeader("X-Nonce");
        String appId = request.getHeader("X-App-Id");

        if (StringUtils.isEmpty(signature) || StringUtils.isEmpty(timestamp) || 
            StringUtils.isEmpty(nonce) || StringUtils.isEmpty(appId)) {
            logger.warn("缺少必要的签名参数");
            return false;
        }

        // 验证时间戳（防重放攻击）
        long requestTime = Long.parseLong(timestamp);
        long currentTime = System.currentTimeMillis() / 1000;
        
        if (Math.abs(currentTime - requestTime) > timestampToleranceSeconds) {
            logger.warn("请求时间戳超出允许范围: requestTime={}, currentTime={}", requestTime, currentTime);
            return false;
        }

        // 构建签名字符串
        String signatureString = buildSignatureString(request, timestamp, nonce, appId);
        
        // 计算期望的签名
        String expectedSignature = calculateSignature(signatureString);
        
        // 比较签名
        return signature.equals(expectedSignature);
    }

    /**
     * 构建签名字符串
     */
    private String buildSignatureString(HttpServletRequest request, String timestamp, String nonce, String appId) {
        StringBuilder sb = new StringBuilder();
        
        // 添加HTTP方法
        sb.append(request.getMethod()).append("\n");
        
        // 添加请求路径
        sb.append(request.getRequestURI()).append("\n");
        
        // 添加查询参数（按字母顺序排序）
        Map<String, String> sortedParams = new TreeMap<>();
        if (request.getQueryString() != null) {
            String[] pairs = request.getQueryString().split("&");
            for (String pair : pairs) {
                String[] keyValue = pair.split("=", 2);
                if (keyValue.length == 2) {
                    sortedParams.put(keyValue[0], keyValue[1]);
                }
            }
        }
        
        for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
            sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        
        // 移除最后的&符号
        if (sb.length() > 0 && sb.charAt(sb.length() - 1) == '&') {
            sb.setLength(sb.length() - 1);
        }
        sb.append("\n");
        
        // 添加时间戳、随机数和应用ID
        sb.append(timestamp).append("\n");
        sb.append(nonce).append("\n");
        sb.append(appId);
        
        return sb.toString();
    }

    /**
     * 计算签名
     */
    private String calculateSignature(String signatureString) throws Exception {
        // 使用HMAC-SHA256算法计算签名
        String dataToSign = signatureString + "\n" + secretKey;
        
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(dataToSign.getBytes(StandardCharsets.UTF_8));
        
        // 转换为十六进制字符串
        StringBuilder hexString = new StringBuilder();
        for (byte b : hash) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        
        return hexString.toString();
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotEmpty(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }

        String xRealIp = request.getHeader("X-Real-IP");
        if (StringUtils.isNotEmpty(xRealIp)) {
            return xRealIp;
        }

        return request.getRemoteAddr();
    }
}