package com.kym.common.aspect;

import com.kym.common.annotation.DynamicCache;
import com.kym.common.cache.DynamicDataCache;
import com.kym.common.utils.CommUtil;
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.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author skyline
 * @description 数据缓存切面
 * @date 2023-11-08 22:27
 */
@Component
@Aspect
public class DynamicCacheAspect {
    private DynamicDataCache cache = DynamicDataCache.INSTANCE;
    private SpelExpressionParser parserSpel = new SpelExpressionParser();
    private DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    @Pointcut("@annotation(com.kym.common.annotation.DynamicCache)")
    public void cachePointCut() {
    }

    @Around("cachePointCut()")
    Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        DynamicCache dynamicCache = method.getAnnotation(DynamicCache.class);

        //获取运行时参数的值
        String spelParamValue = generateKeyBySpEL(dynamicCache.spel(), joinPoint);

        // 获取方法名
        var methodName = signature.getName();
        // 获取注解上的缓存时间
        var timeout = dynamicCache.timeout();
        // 获取注解上的缓存key
        var key = !CommUtil.isEmptyOrNull(spelParamValue) ? methodName+":"+spelParamValue : dynamicCache.key();
        var data = cache.get(CommUtil.isEmptyOrNull(key) ? methodName : key);
        if (data != null) {
            return data;
        }
        // 缓存无数据则执行原方法，缓存新数据
        Object result = joinPoint.proceed();
        // 缓存数据
        cache.put(CommUtil.isEmptyOrNull(key) ? methodName : key, result, timeout >= 0 ? timeout : 5 * 60 * 1000);
        return result;
    }

    /**
     * 生成缓存key
     *
     * @param key
     * @param pjp
     * @return
     */
    public String generateKeyBySpEL(String key, ProceedingJoinPoint pjp) {
        if (CommUtil.isEmptyOrNull(key)) {
            return null;
        }
        Expression expression = parserSpel.parseExpression(key);
        EvaluationContext context = new StandardEvaluationContext();
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Object[] args = pjp.getArgs();
        String[] paramNames = parameterNameDiscoverer.getParameterNames(methodSignature.getMethod());
        for (int i = 0; i < args.length; i++) {
            assert paramNames != null;
            context.setVariable(paramNames[i], args[i]);
        }
        return Objects.requireNonNull(expression.getValue(context)).toString();
    }

}
