package com.ssyf.common.security.aop;

import cn.hutool.core.util.StrUtil;
import com.ssyf.common.core.util.AssertUtil;
import com.ssyf.common.core.util.StringUtil;
import com.ssyf.common.security.domain.Certificate;
import com.ssyf.common.security.handler.CertificateSecretHandler;
import com.ssyf.common.security.helper.CertificateHelper;
import com.ssyf.common.security.helper.ClientContextHelper;
import com.ssyf.common.web.apicode.ApiCodeUser;
import com.ssyf.common.web.error.ServiceRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


/**
 * 证书校验拦截
 * @author huwei
 * @date 2023/11/4 15:57
 */
@Slf4j
@Aspect
@Component
public class CertificateValidAop {

    @Autowired
    private CertificateSecretHandler certificateSecretHandler;

    @Pointcut("@annotation(CertificateValid)")
    public void pointcut(){}

    @Around("pointcut()")
    public Object valid(ProceedingJoinPoint joinPoint) throws Throwable {
        //证书
        String certificateStr = ClientContextHelper.getCertificate();
        AssertUtil.isTrue(StrUtil.isNotBlank(certificateStr) ,ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR.getCode() ,"证书为空"));

        //解析CertificateValid
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        CertificateValid certificateValid = method.getAnnotation(CertificateValid.class);
        Certificate certificate = parseAndGet(joinPoint ,certificateValid);
        //获取证书秘钥
        String secret = certificateSecretHandler.getSecret(ClientContextHelper.getClientInfo());
        //校验证书
        if(!CertificateHelper.valid(secret ,certificate,certificateStr)){
            throw ServiceRuntimeException.instance(ApiCodeUser.PARAMETER_ERROR.getCode() ,"无效证书");
        }
        return joinPoint.proceed();
    }

    /**
     * 系欸小证书注解并获取预期证书信息
     * @param joinPoint
     * @param certificateValid
     * @return
     */
    private Certificate parseAndGet(ProceedingJoinPoint joinPoint , CertificateValid certificateValid){
        Certificate certificate = new Certificate();
        certificate.setType(certificateValid.type()).setEvent(certificateValid.event()).setContent(certificateValid.content());

        Map<String ,Object> varMap = null;
        //动态event
        if(StrUtil.isBlank(certificate.getEvent()) && StrUtil.isNotBlank(certificateValid.eventVar())){
            varMap = getParameterMap(joinPoint);
            String var = new StringBuilder("${").append(certificateValid.eventVar()).append("}").toString();
            //获取动态event
            Object obj = StringUtil.replacePlaceholder(var ,varMap);
            String event = obj != null ? obj.toString() : null;
            certificate.setEvent(event);
        }
        //动态内容
        Boolean isEmptyContent = certificate.getContent() == null || StrUtil.isBlank(certificate.getContent().toString());
        if(isEmptyContent && StrUtil.isNotBlank(certificateValid.contentVar())){
            varMap = varMap == null ? getParameterMap(joinPoint) : varMap;
            String var = new StringBuilder("${").append(certificateValid.contentVar()).append("}").toString();
            //获取动态event
            Object obj = StringUtil.replacePlaceholder(var ,varMap);
            String content = obj != null ? obj.toString() : null;
            certificate.setContent(content);
        }

        return certificate;
    }

    /**
     * 获取指定的参数值
     * @param joinPoint
     * @return
     */
    private Map<String ,Object> getParameterMap(ProceedingJoinPoint joinPoint){
        CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
        String[] parameterNames = codeSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        Map<String ,Object> map = new HashMap<>(args.length);
        for (int i = 0; i < parameterNames.length; i++) {
            map.put(parameterNames[i] ,args[i]);
        }
        return map;
    }
}
