package com.example.demo.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 限流切面 - 演示 AOP 实现横切关注点
 * 
 * 学习点：
 * - @Order 控制切面执行顺序
 * - AOP 实现非功能性需求
 * - 横切关注点与业务逻辑分离
 */
@Aspect
@Component
@Order(1)  // 数字越小优先级越高
public class RateLimitAspect {
    
    // 简单的限流计数器（实际项目用 Redis + Token Bucket）
    private final Map<String, AtomicLong> requestCounts = new ConcurrentHashMap<>();
    private final Map<String, Long> lastResetTime = new ConcurrentHashMap<>();
    
    // 每分钟最大请求数
    private static final long MAX_REQUESTS_PER_MINUTE = 100;
    
    /**
     * 限流检查
     */
    @Around("@annotation(com.example.demo.aop.RateLimited)")
    public Object enforceRateLimit(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        
        long currentTime = System.currentTimeMillis();
        Long lastReset = lastResetTime.get(methodName);
        
        // 每分钟重置计数器
        if (lastReset == null || currentTime - lastReset > 60000) {
            requestCounts.put(methodName, new AtomicLong(0));
            lastResetTime.put(methodName, currentTime);
        }
        
        AtomicLong count = requestCounts.get(methodName);
        long currentCount = count.incrementAndGet();
        
        if (currentCount > MAX_REQUESTS_PER_MINUTE) {
            throw new RuntimeException("请求过于频繁，请稍后再试 (Rate Limit)");
        }
        
        System.out.println("[AOP-RateLimit] " + methodName + 
            " 当前计数: " + currentCount + "/" + MAX_REQUESTS_PER_MINUTE);
        
        return joinPoint.proceed();
    }
    
    /**
     * 获取限流状态
     */
    public Map<String, Long> getRateLimitStatus() {
        Map<String, Long> status = new ConcurrentHashMap<>();
        requestCounts.forEach((k, v) -> status.put(k, v.get()));
        return status;
    }
}