package com.reading.platform.common.aspect;

import com.reading.platform.common.annotation.RateLimit;
import com.reading.platform.common.config.RateLimitProperties;
import com.reading.platform.common.enums.ErrorCode;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.common.util.RateLimiter;
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.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 限流切面
 * 支持配置化限流和注解限流两种方式
 * 注解限流优先级高于配置化限流
 */
@Slf4j
@Aspect
@Component
public class RateLimitAspect {

    private final RateLimiter rateLimiter;
    private final RateLimitProperties rateLimitProperties;

    public RateLimitAspect(RateLimiter rateLimiter, RateLimitProperties rateLimitProperties) {
        this.rateLimiter = rateLimiter;
        this.rateLimitProperties = rateLimitProperties;
    }

    @Around("@annotation(rateLimit)")
    public Object aroundAnnotation(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        // 如果限流未启用，直接放行
        if (!rateLimitProperties.isEnabled() || !rateLimit.enabled()) {
            return joinPoint.proceed();
        }

        // 获取限流键
        String key = buildRateLimitKey(joinPoint, rateLimit);

        // 检查是否允许请求
        boolean allowed = rateLimiter.tryAcquire(
                key,
                rateLimit.maxRequests(),
                rateLimit.time(),
                rateLimit.timeUnit()
        );

        if (!allowed) {
            long remaining = rateLimiter.getRemainingRequests(
                    key,
                    rateLimit.maxRequests(),
                    rateLimit.time(),
                    rateLimit.timeUnit()
            );
            log.warn("请求被限流拦截：key={}, 剩余次数={}, 最大请求数={}", key, remaining, rateLimit.maxRequests());
            throw new BusinessException(ErrorCode.RATE_LIMIT_EXCEEDED, rateLimit.message());
        }

        log.debug("请求通过限流检查：key={}", key);
        return joinPoint.proceed();
    }

    /**
     * 构建限流键
     * 优先级：自定义 keyPrefix > 类名。方法名 > IP 地址。类名。方法名
     */
    private String buildRateLimitKey(ProceedingJoinPoint joinPoint, RateLimit rateLimit) {
        StringBuilder key = new StringBuilder();

        // 添加自定义前缀
        if (StringUtils.hasText(rateLimit.keyPrefix())) {
            key.append(rateLimit.keyPrefix());
        } else {
            // 默认使用 IP 地址 + 类名。方法名
            String clientIp = getClientIp();
            if (StringUtils.hasText(clientIp)) {
                key.append(clientIp).append(":");
            }
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            key.append(signature.getDeclaringTypeName())
                    .append(".")
                    .append(signature.getName());
        }

        return key.toString();
    }

    /**
     * 获取客户端 IP 地址
     */
    private String getClientIp() {
        try {
            ServletRequestAttributes attributes =
                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                String xForwardedFor = request.getHeader("X-Forwarded-For");
                if (StringUtils.hasText(xForwardedFor)) {
                    return xForwardedFor.split(",")[0].trim();
                }
                return request.getRemoteAddr();
            }
        } catch (Exception e) {
            log.debug("获取客户端 IP 失败", e);
        }
        return "unknown";
    }
}
