package cn.quant.baa.pay.aspect;

import cn.quant.baa.pay.annotation.BusinessMapping;
import cn.quant.baa.pay.component.Sequencer;
import cn.quant.baa.pay.context.TransactionSession;
import cn.quant.baa.pay.model.BusinessRequest;
import cn.quant.spring.context.ServerApplicationContext;
import cn.quant.spring.util.StringUtils;
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.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * <p>通过在方法上指定 {@link cn.quant.baa.pay.annotation.BusinessMapping} 注解来自动创建 {@link cn.quant.baa.pay.context.TransactionSession}，
 * 获取的方法参看{@link TransactionSession#session()}。
 * </p>
 * Created by Administrator on 2021/8/23 0023.
 */
@Aspect
@Component
public class BusinessMappingAspect {


    @Autowired
    private Sequencer sequencer;


    @Autowired
    private ServerApplicationContext serverApplicationContext;

    public void session(Object arg) {
        if (BusinessRequest.class.isInstance(arg)) {
            TransactionSession.newInstance((BusinessRequest) arg, serverApplicationContext, sequencer);
        }
    }

    @Pointcut("@annotation(cn.quant.baa.pay.annotation.BusinessMapping)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object Around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        BusinessMapping annotation = method.getAnnotation(BusinessMapping.class);
        if (annotation.session() > 0) {
            Object[] args = joinPoint.getArgs();
            if (args.length < annotation.session()) {
                throw new IllegalArgumentException(StringUtils.format("Business mapping session value({}) less than arguments size({}) - {}"
                        , annotation.session(), args.length, method));
            }
            session(args[annotation.session() - 1]);
        }
        try {
            Object object = joinPoint.proceed();
            return object;
        } finally {
            TransactionSession.release();
        }
    }
}
