package com.ghb.base.config.sign.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import com.ghb.base.config.sign.annotation.SignatureCheck;
import com.ghb.base.config.sign.interceptor.SignAuthInterceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 基于AOP的签名验证切面
 * 复用SignAuthInterceptor的成熟签名验证逻辑
 * 
 * @author GitHub Copilot
 * @since 2025-12-15
 */
@Aspect
@Slf4j
@Component("signatureCheckAspect")
public class SignatureCheckAspect {
    
    /**
     * 复用SignAuthInterceptor的签名验证逻辑
     */
    private final SignAuthInterceptor signAuthInterceptor = new SignAuthInterceptor();

    /**
     * 验签切点：拦截所有标记了@SignatureCheck注解的方法
     */
    @Pointcut("@annotation(com.ghb.base.config.sign.annotation.SignatureCheck)")
    private void signatureCheckPointCut() {
    }

    /**
     * 开始验签
     */
    @Before("signatureCheckPointCut()")
    public void doSignatureValidation(JoinPoint point) throws Exception {
        // 获取方法上的注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        SignatureCheck signatureCheck = method.getAnnotation(SignatureCheck.class);
        
        log.info("AOP签名验证: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName());
        
        // 如果注解被禁用，直接返回
        if (!signatureCheck.enabled()) {
            log.info("签名验证已禁用，跳过");
            return;
        }

        // update-begin---author:sjlei---date:20260115 for: 查找带有@RequestBody注解的参数，解决签名校验时读取请求体为空的问题
        Object bodyParam = null;
        Object[] args = point.getArgs();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            Annotation[] annotations = parameterAnnotations[i];
            boolean hasRequestBodyAnnotation = Arrays.stream(annotations).anyMatch(annotation -> annotation.annotationType().equals(RequestBody.class));
            if (hasRequestBodyAnnotation) {
                // 捕获携带@RequestBody注解的参数，供签名校验使用
                bodyParam = arg;
            }
        }
        // update-end-----author:sjlei---date:20260115 for: 查找带有@RequestBody注解的参数，解决签名校验时读取请求体为空的问题

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            log.error("无法获取请求上下文");
            throw new IllegalArgumentException("无法获取请求上下文");
        }
        
        HttpServletRequest request = attributes.getRequest();
        log.info("X-SIGN: {}, X-TIMESTAMP: {}", request.getHeader("X-SIGN"), request.getHeader("X-TIMESTAMP"));
        
        try {
            // 直接调用SignAuthInterceptor的验证逻辑
            signAuthInterceptor.validateSignature(request, bodyParam);
            log.info("AOP签名验证通过");
            
        } catch (IllegalArgumentException e) {
            // 使用注解中配置的错误消息，或者保留原始错误消息
            String errorMessage = signatureCheck.errorMessage();
            log.error("AOP签名验证失败: {}", e.getMessage());
            
            if ("Sign签名校验失败！".equals(errorMessage)) {
                // 如果是默认错误消息，使用原始的详细错误信息
                throw e;
            } else {
                // 如果是自定义错误消息，使用自定义消息
                throw new IllegalArgumentException(errorMessage, e);
            }
        } catch (Exception e) {
            // 包装其他异常
            String errorMessage = signatureCheck.errorMessage();
            log.error("AOP签名验证异常: {}", e.getMessage());
            throw new IllegalArgumentException(errorMessage, e);
        }
    }
}
