package cn.iocoder.foodnexus.module.customerpermission.core.aop;

import cn.iocoder.foodnexus.module.customerpermission.core.annotation.CustomerVisible;
import lombok.Getter;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.MethodClassKey;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * {@link CustomerVisible} 注解的拦截器
 * 1. 在执行方法前，将 @CustomerVisible 注解入栈
 * 2. 在执行方法后，将 @CustomerVisible 注解出栈
 *
 * @author 芋道源码
 */
@CustomerVisible // 该注解，用于 {@link CUSTOMER_PERMISSION_NULL} 的空对象
public class CustomerPermissionAnnotationInterceptor implements MethodInterceptor {

    /**
     * CustomerVisible 空对象，用于方法无 {@link CustomerVisible} 注解时，使用 CUSTOMER_PERMISSION_NULL 进行占位
     */
    static final CustomerVisible CUSTOMER_PERMISSION_NULL = CustomerPermissionAnnotationInterceptor.class.getAnnotation(CustomerVisible.class);

    @Getter
    private final Map<MethodClassKey, CustomerVisible> CustomerVisibleCache = new ConcurrentHashMap<>();

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // 入栈
        CustomerVisible CustomerVisible = this.findAnnotation(methodInvocation);
        if (CustomerVisible != null) {
            CustomerPermissionContextHolder.add(CustomerVisible);
        }
        try {
            // 执行逻辑
            return methodInvocation.proceed();
        } finally {
            // 出栈
            if (CustomerVisible != null) {
                CustomerPermissionContextHolder.remove();
            }
        }
    }

    private CustomerVisible findAnnotation(MethodInvocation methodInvocation) {
        // 1. 从缓存中获取
        Method method = methodInvocation.getMethod();
        Object targetObject = methodInvocation.getThis();
        Class<?> clazz = targetObject != null ? targetObject.getClass() : method.getDeclaringClass();
        MethodClassKey methodClassKey = new MethodClassKey(method, clazz);
        CustomerVisible CustomerVisible = CustomerVisibleCache.get(methodClassKey);
        if (CustomerVisible != null) {
            return CustomerVisible != CUSTOMER_PERMISSION_NULL ? CustomerVisible : null;
        }

        // 2.1 从方法中获取
        CustomerVisible = AnnotationUtils.findAnnotation(method, CustomerVisible.class);
        // 2.2 从类上获取
        if (CustomerVisible == null) {
            CustomerVisible = AnnotationUtils.findAnnotation(clazz, CustomerVisible.class);
        }
        // 2.3 添加到缓存中
        CustomerVisibleCache.put(methodClassKey, CustomerVisible != null ? CustomerVisible : CUSTOMER_PERMISSION_NULL);
        return CustomerVisible;
    }

}
