/* (C) 2021 YiRing, Inc. */
package com.yiring.common.aspect;

import cn.hutool.core.util.StrUtil;
import com.yiring.common.annotation.Times;
import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
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.stereotype.Component;

/**
 * 耗时计算注解切面
 *
 * @author ifzm
 * @version 0.1
 */

@Slf4j
@Aspect
@Component
public class TimesAspect {

    @Pointcut("@annotation(com.yiring.common.annotation.Times)")
    public void times() {}

    @Around("times()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = point.proceed();
        long end = System.currentTimeMillis();
        long times = end - start;

        // 计算是否超过阈值，打印日志
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Times annotation = method.getAnnotation(Times.class);
        String name = StrUtil.isEmpty(annotation.value()) ? method.getName() : annotation.value();
        if (annotation.threshold() > 0 && times > annotation.threshold()) {
            log.warn("[Times] {}: {} ms", name, times);
        } else {
            log.info("[Times] {}: {} ms", name, times);
        }

        return result;
    }

    public String getTimesValue(JoinPoint joinPoint) {
        // 获取切入点的目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 获取切入方法名
        String methodName = joinPoint.getSignature().getName();
        // 获取切入方法参数
        Object[] arguments = joinPoint.getArgs();
        // 获取目标类的所有方法
        Method[] methods = targetClass.getMethods();

        String timesValue = methodName;
        for (Method method : methods) {
            // 方法名相同、包含目标注解、方法参数个数相同（避免有重载）
            if (
                method.getName().equals(methodName) &&
                method.isAnnotationPresent(Times.class) &&
                method.getParameterTypes().length == arguments.length
            ) {
                String value = method.getAnnotation(Times.class).value();
                if (StrUtil.isNotEmpty(value)) {
                    timesValue = value;
                }
            }
        }

        return timesValue;
    }
}
