package com.cpop.core.aspect;

import com.cpop.common.utils.StringUtils;
import com.cpop.core.base.R;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.base.exception.UtilException;
import com.cpop.core.utils.RsaUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 自定义前面校验
 * @author DB
 * @version 1.0.0
 * @since 2024-01-16 10:01
 */
@Aspect
@Component
@Slf4j
public class SignatureCheckAspect {

    @Autowired
    private RsaUtils rsaUtils;

    private static final Map<String, String[]> APP_IDS = new HashMap<String, String[]>() {{
        //数币
        put("1688842965582499840", new String[]{"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"});
    }};

    @Pointcut("@annotation(com.cpop.core.annontation.SignatureCheck)")
    public void signatureCheck() {}

    /**
     * 前置通知
     * @author DB
     * @since 2024/1/16
     * @param joinPoint 切入点
     */
    @Before(value = "signatureCheck()")
    public void beforePointcut(JoinPoint joinPoint) throws IllegalAccessException {
        //获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        assert requestAttributes != null;
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        assert request != null;
        Object object = joinPoint.getArgs()[0];
        //验签拼接字段
        List<String> signString = new ArrayList<String>();
        //获取父类（公共类必传属性）
        Field[] fatherFields = object.getClass().getSuperclass().getDeclaredFields();
        String signature = null;
        String appId = null;
        for (Field field : fatherFields) {
            field.setAccessible(true);
            if (StringUtils.equals("signature", field.getName())) {
                signature = (String) field.get(object);
                continue;
            }
            if (StringUtils.equals("appId", field.getName())) {
                String[] strings = APP_IDS.get((String) field.get(object));
                if (null == strings){
                    throw new ServiceException("appId匹配失败,请核实appId");
                }
                appId = strings[0];
            }
            signString.add(field.getName() + "=" + field.get(object));
        }
        if (null == signature){
            throw new ServiceException("签名不存在,请核实签名");
            //return ResultDTO.fail("签名不存在,请核实签名");
        }
        //可能为空参
        Field[] fields = object.getClass().getDeclaredFields();
        if (fields.length > 0) {
            for (Field field : fields) {
                field.setAccessible(true);
                if(null == field.get(object)){
                    signString.add(field.getName() + "=");
                } else {
                    signString.add(field.getName() + "=" + field.get(object));
                }
            }
        }
        //按照字母排序
        String sign = signString.stream().sorted().collect(Collectors.joining("&")) + appId;
        String md5DigestAsHex = DigestUtils.md5DigestAsHex(sign.getBytes());
        if (!StringUtils.equals(md5DigestAsHex,signature)){
            throw new ServiceException("签名匹配失败,请核实签名");
        }
    }

    /**
     * 环绕通知,不做处理
     * @author DB
     * @since 2024/1/16
     * @param joinPoint 请求
     * @return Object
     */
    @Around("signatureCheck()")
    public Object aroundPointcut(ProceedingJoinPoint joinPoint) throws Throwable {
        return joinPoint.proceed(joinPoint.getArgs());
    }

    /**
     * 异常通知：目标方法发生异常的时候执行以下代码，此时返回通知不会再触发
     * value 属性：绑定通知的切入点表达式。可以关联切入点声明，也可以直接设置切入点表达式
     * pointcut 属性：绑定通知的切入点表达式，优先级高于 value，默认为 ""
     * throwing 属性：与方法中的异常参数名称一致，
     *
     * @param ex：捕获的异常对象，名称与 throwing 属性值一致
     */
    @AfterThrowing(pointcut = "signatureCheck()", throwing = "ex")
    public Object aspectAfterThrowing(JoinPoint jp, Exception ex) {
        String methodName = jp.getSignature().getName();
        if (ex instanceof ServiceException) {
            log.error("【异常通知】" + methodName + "方法算术异常（ArithmeticException）：" + ex.getMessage());
            return R.fail(ex.getMessage());
        } else {
            log.error("【异常通知】" + methodName + "方法异常：" + ex.getMessage());
            return R.fail("签名不合法");
        }
    }

}
