package cn.qinys.platform.base.aspect;


import cn.qinys.platform.base.annotation.LimitedRate;
import cn.qinys.platform.base.annotation.LimitedRequest;
import cn.qinys.platform.base.exceptions.BizException;
import cn.qinys.platform.base.security.utils.CurrentUtils;
import cn.qinys.platform.base.utils.RedisUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import cn.qinys.platform.base.redis.RedisKeyEnum;



/**
 * Description:
 *
 * @author tanlie
 * Date: 2024/9/2 11:14
 */
@Slf4j
@Aspect
@Component
public class LimitRateAspect {
    @Resource
    private RedisUtils redisUtils;


    @Around("@within(rateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint, LimitedRate rateLimit) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        LimitedRequest limitRequest = AnnotationUtils.findAnnotation(signature.getMethod(), LimitedRequest.class);
        if (limitRequest == null || !limitRequest.value()) {
            return joinPoint.proceed();
        }
        // 获取key
        int limit;
        long timeout;
        String description;
        if (limitRequest.limit() > 0) {
            limit = limitRequest.limit();
            timeout = limitRequest.timeout();
        } else {
            limit = rateLimit.limit();
            timeout = rateLimit.timeout();
        }
        // 描述
        if (StringUtils.hasLength(limitRequest.description())) {
            description = limitRequest.description();
        } else {
            description = rateLimit.description();
        }
        // 确保>=0
        limit = limit <= 0 ? 1 : limit;
        timeout = timeout <= 0 ? 1 : timeout;
        if (!StringUtils.hasLength(description)) {
            description = "该接口每" + timeout + "秒只能访问" + limit + "次";
        }

        String key = this.generateKey();
        String countStr = (String) redisUtils.get(key);
        int count = countStr == null ? 0 : Integer.parseInt(countStr);
        if (count < limit) {
            Long ttl = redisUtils.ttl(key);
            if (ttl == null || ttl <= 0) {
                ttl = timeout;
            }
            redisUtils.set(key, String.valueOf(count + 1));
            redisUtils.expire(key, ttl);
            return joinPoint.proceed();
        } else {
            log.error("请注意，有人接口刷得太快=={}，{}", key, description);
            throw new BizException("您刷新太快啦，请稍后再试");
        }
    }

    private String generateKey() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String uri = request.getRequestURI();
        String token = CurrentUtils.getToken();
        return RedisKeyEnum.USER_REQUEST_LIMIT.getCode() + token + ":" + uri;
    }

}
