package com.zsElectric.boot.core.aspect;

import cn.hutool.core.util.StrUtil;
import com.zsElectric.boot.common.annotation.ApiRateLimit;
import com.zsElectric.boot.common.util.IPUtils;
import com.zsElectric.boot.core.exception.BusinessException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
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.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * API接口限流切面
 * <p>
 * 基于Redis实现分布式限流，支持IP、全局和用户三种限流策略
 *
 * @author system
 * @since 2025-12-19
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class ApiRateLimitAspect {

    private final RedisTemplate<String, Object> redisTemplate;

    private static final String RATE_LIMIT_KEY_PREFIX = "api:rate_limit:";

    /**
     * 切点：标注了 @ApiRateLimit 注解的方法
     */
    @Pointcut("@annotation(com.zsElectric.boot.common.annotation.ApiRateLimit)")
    public void rateLimitPointcut() {
    }

    /**
     * 环绕通知：执行限流逻辑
     */
    @Around("rateLimitPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        ApiRateLimit rateLimit = method.getAnnotation(ApiRateLimit.class);

        if (rateLimit == null) {
            return joinPoint.proceed();
        }

        // 构建限流key
        String key = buildRateLimitKey(rateLimit, method);

        // 执行限流检查
        if (!tryAcquire(key, rateLimit)) {
            log.warn("接口 [{}] 触发限流，key: {}", method.getName(), key);
            throw new BusinessException(rateLimit.message());
        }

        return joinPoint.proceed();
    }

    /**
     * 构建限流key
     *
     * @param rateLimit 限流注解
     * @param method    方法
     * @return 限流key
     */
    private String buildRateLimitKey(ApiRateLimit rateLimit, Method method) {
        StringBuilder keyBuilder = new StringBuilder(RATE_LIMIT_KEY_PREFIX);

        // 添加自定义前缀
        if (StrUtil.isNotBlank(rateLimit.prefix())) {
            keyBuilder.append(rateLimit.prefix()).append(":");
        } else {
            // 使用方法签名作为默认前缀
            keyBuilder.append(method.getDeclaringClass().getSimpleName())
                    .append(".")
                    .append(method.getName())
                    .append(":");
        }

        // 根据限流类型添加不同的标识
        switch (rateLimit.limitType()) {
            case IP:
                String ip = getClientIp();
                keyBuilder.append("ip:").append(ip);
                break;
            case GLOBAL:
                keyBuilder.append("global");
                break;
            case USER:
                // 这里可以根据实际情况获取用户ID，暂时使用IP作为替代
                String userIp = getClientIp();
                keyBuilder.append("user:").append(userIp);
                break;
            default:
                keyBuilder.append("unknown");
        }

        return keyBuilder.toString();
    }

    /**
     * 尝试获取限流令牌
     *
     * @param key       限流key
     * @param rateLimit 限流注解
     * @return 是否获取成功
     */
    private boolean tryAcquire(String key, ApiRateLimit rateLimit) {
        try {
            // 获取当前计数
            Long count = redisTemplate.opsForValue().increment(key);

            if (count == null) {
                return false;
            }

            // 如果是第一次访问，设置过期时间
            if (count == 1) {
                long timeout = rateLimit.timeUnit().toSeconds(rateLimit.time());
                redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
            }

            // 判断是否超过限流阈值
            if (count > rateLimit.count()) {
                log.warn("限流触发 - key: {}, count: {}, limit: {}", key, count, rateLimit.count());
                return false;
            }

            return true;
        } catch (Exception e) {
            log.error("限流检查异常", e);
            // 异常情况下允许通过，避免影响业务
            return true;
        }
    }

    /**
     * 获取客户端IP地址
     *
     * @return IP地址
     */
    private String getClientIp() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            return IPUtils.getIpAddr(request);
        }
        return "unknown";
    }
}
