package com.tzld.piaoquan.api.aop;

import com.google.common.base.Strings;
import com.tzld.piaoquan.api.annotation.RateLimit;
import com.tzld.piaoquan.api.common.enums.ExceptionEnum;
import com.tzld.piaoquan.api.common.exception.CommonException;
import com.tzld.piaoquan.api.model.config.LoginUserContext;
import com.tzld.piaoquan.api.model.po.contentplatform.ContentPlatformAccount;
import com.tzld.piaoquan.growth.common.utils.IpUtil;
import com.tzld.piaoquan.growth.common.utils.RedisUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

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

/**
 * 限流 AOP 切面，支持按账号或 IP 维度
 */
@Aspect
@Component
public class RateLimitAop {

    private static final Logger log = LoggerFactory.getLogger(RateLimitAop.class);

    @Autowired
    private RedisUtils redisUtils;

    @Pointcut("@annotation(com.tzld.piaoquan.api.annotation.RateLimit)")
    public void rateLimit() {
    }

    @Around("rateLimit()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        RateLimit rateLimit = method.getAnnotation(RateLimit.class);
        long timeWindow = rateLimit.timeWindow();
        long maxRequests = rateLimit.maxRequests();
        String message = rateLimit.message();
        boolean limitByAccount = rateLimit.limitByAccount();

        String methodName = method.getName();
        String limitKey;

        if (limitByAccount) {
            ContentPlatformAccount user = LoginUserContext.getUser();
            if (user == null || user.getId() == null) {
                log.warn("Rate limit - limitByAccount=true but no login user, fallback to IP");
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
                limitKey = IpUtil.getIpAddr(request);
            } else {
                limitKey = String.valueOf(user.getId());
            }
        } else {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
            limitKey = IpUtil.getIpAddr(request);
        }

        if (Strings.isNullOrEmpty(limitKey)) {
            log.warn("Rate limit - limit key is empty");
            return joinPoint.proceed();
        }

        String redisKey = String.format("rate_limit:%s:%s", methodName, limitKey);

        // 原子递增并返回递增后的值，避免 get + set 的竞态问题
        Long currentRequests = redisUtils.incrWithExpire(redisKey, timeWindow);

        if (currentRequests == null) {
            log.warn("Rate limit - incrWithExpire returned null");
            return joinPoint.proceed();
        }

        if (currentRequests > maxRequests) {
            // 回滚本次递增
            redisUtils.setDecrementValue(redisKey, 1);
            log.warn("Rate limit exceeded - Key: {}, Method: {}, Requests: {}, Max: {}",
                    limitKey, methodName, currentRequests, maxRequests);
            throw new CommonException(ExceptionEnum.PARAM_ERROR.getCode(), message);
        }

        log.debug("Rate limit - Key: {}, Method: {}, Current Requests: {}, Max: {}",
                limitKey, methodName, currentRequests, maxRequests);

        return joinPoint.proceed();
    }
}
