package edu.whut.infrastructure.aop;

import edu.whut.types.annotations.DCCValue;
import edu.whut.types.annotations.RateLimiterAccessInterceptor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.redisson.api.RAtomicLong;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 分布式限流切面，基于 Redisson 的 RRateLimiter 和 RAtomicLong 实现
 */
@Aspect
public class RateLimiterAOP {

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

    /**
     * 全局开关：open/close
     */
    @DCCValue("rateLimiterSwitch:open")
    private String rateLimiterSwitch;

    /**
     * Redisson 客户端，注入使用
     */
    @Resource
    private RedissonClient redissonClient;

    @Pointcut("@annotation(edu.whut.types.annotations.RateLimiterAccessInterceptor)")
    public void aopPoint() {}

    @Around("aopPoint() && @annotation(rateLimiterAccessInterceptor)")
    public Object doRouter(ProceedingJoinPoint jp,
                           RateLimiterAccessInterceptor rateLimiterAccessInterceptor) throws Throwable {
        // 0. 全局开关
        if (StringUtils.isBlank(rateLimiterSwitch) || "close".equals(rateLimiterSwitch)) {
            return jp.proceed();
        }

        // 1. 获取限流维度 key
        String key = rateLimiterAccessInterceptor.key();
        if (StringUtils.isBlank(key)) {
            throw new RuntimeException("annotation RateLimiter key is null！");
        }
        String keyAttr = getAttrValue(key, jp.getArgs());
        log.info("[RateLimiter] attr={}, permits={}, blacklistCount={}",
                keyAttr,
                rateLimiterAccessInterceptor.permitsPerSecond(),
                rateLimiterAccessInterceptor.blacklistCount());

        // 2. 黑名单检查（分布式，24h）  rl:ratelimit  bl:blacklist
        // 存储的是 “用户在这一轮限流中被拒绝的次数”，大于blacklistLimit则被视作进入黑名单，等key释放解决黑名单
        double blacklistLimit = rateLimiterAccessInterceptor.blacklistCount();
        if (blacklistLimit > 0) {
            RAtomicLong blCounter = redissonClient.getAtomicLong("rl:bl:" + keyAttr);
            if (blCounter.isExists() && blCounter.get() > blacklistLimit) {
                log.info("[RateLimiter] 黑名单拦截: {}", keyAttr);
                return fallbackMethodResult(jp, rateLimiterAccessInterceptor.fallbackMethod());
            }
        }

        // 3. 获取或创建分布式 RateLimiter
        RRateLimiter limiter = redissonClient.getRateLimiter("rl:limiter:" + keyAttr);
        // 尝试设置速率，每秒放n个令牌 若已设置则返回 false
        limiter.trySetRate(RateType.OVERALL,
                (long) rateLimiterAccessInterceptor.permitsPerSecond(),
                1, RateIntervalUnit.SECONDS);

        // 4. 尝试获取令牌，如果取不到，则返回false
        boolean allowed = limiter.tryAcquire();
        if (!allowed) {
            // 超限后计入黑名单
            if (blacklistLimit > 0) {
                RAtomicLong blCounter = redissonClient.getAtomicLong("rl:bl:" + keyAttr);
                long count = blCounter.incrementAndGet();
                if (count == 1) {
                    blCounter.expire(24, TimeUnit.HOURS);
                }
            }
            log.info("[RateLimiter] 限流拦截: {}", keyAttr);
            return fallbackMethodResult(jp, rateLimiterAccessInterceptor.fallbackMethod());
        }

        // 5. 正常执行
        return jp.proceed();
    }

    /**
     * 调用用户配置的降级方法
     */
    private Object fallbackMethodResult(JoinPoint jp, String fallbackMethod)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Signature sig = jp.getSignature();
        MethodSignature ms = (MethodSignature) sig;
        Method method = jp.getTarget().getClass()
                .getMethod(fallbackMethod, ms.getParameterTypes());
        return method.invoke(jp.getTarget(), jp.getArgs());
    }

    /**
     * 从方法参数中获取 attr 字段值
     */
    private String getAttrValue(String attr, Object[] args) {
        if (args == null || args.length == 0) return null;
        if (args[0] instanceof String) {
            return args[0].toString();
        }
        for (Object arg : args) {
            String val = extractField(arg, attr);
            if (StringUtils.isNotBlank(val)) {
                return val;
            }
        }
        return null;
    }

    private String extractField(Object obj, String name) {
        try {
            Field field = getFieldByName(obj, name);
            if (field == null) return null;
            field.setAccessible(true);
            Object v = field.get(obj);
            field.setAccessible(false);
            return v != null ? v.toString() : null;
        } catch (Exception e) {
            log.warn("[RateLimiter] 提取字段失败 {}", name, e);
            return null;
        }
    }

    private Field getFieldByName(Object obj, String name) {
        Class<?> cls = obj.getClass();
        while (cls != null) {
            try {
                return cls.getDeclaredField(name);
            } catch (NoSuchFieldException e) {
                cls = cls.getSuperclass();
            }
        }
        return null;
    }
}
