package vc.thinker.config.interceptor;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.RateLimiter;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import vc.thinker.exception.CurrentLimitException;
import vc.thinker.utils.IpUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author : xieshaojun
 * @date : 2023/1/5 10:32
 */
@Order(1)
@Aspect
@Configuration
public class LimitAspect {

    @Autowired(required = false)
    HttpServletRequest request;
    @Autowired
    private LimitConfig limitConfig;

    @Autowired
    private LoadingCache<String, RateLimiter> caches;

    @Bean
    public LoadingCache<String, RateLimiter> loadingCache() {
        LoadingCache<String, RateLimiter> caches = CacheBuilder.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(1, TimeUnit.DAYS)
                .build(new CacheLoader<>() {
                    @Override
                    public RateLimiter load(String s) {
                        if (ObjectUtils.isEmpty(limitConfig.getSecondLimit())) {
                            return RateLimiter.create(1);
                        }else {
                            return RateLimiter.create(limitConfig.getSecondLimit());
                        }
                    }
                });
        return caches;
    }

    @Pointcut("@annotation(vc.thinker.config.interceptor.ServiceLimit)")
    public void serviceAspect(){

    }

    @Around("serviceAspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        ServiceLimit serviceLimit = method.getAnnotation(ServiceLimit.class);
        ServiceLimit.LimitType limitType = serviceLimit.limitType();
        String key = serviceLimit.key();
        Object object;
        if (Objects.equals(limitType, ServiceLimit.LimitType.IP)) {
            key = IpUtils.getIpAddr(request);
        }
        RateLimiter rateLimiter = caches.get(key);
        boolean acquire = rateLimiter.tryAcquire();
        if (acquire) {
            object = joinPoint.proceed();
        } else {
            throw new CurrentLimitException("请求太频繁");
        }
        return object;
    }
}