package com.mosquito.project.web;

import com.mosquito.project.config.AppConfig;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;

import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Optional;

/**
 * 缓存操作专属限流拦截器
 * 对 /api/v1/system/cache/clear 接口进行IP级别的限流
 */
public class AdminCacheRateLimitInterceptor implements HandlerInterceptor {

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

    private final int perMinuteLimit;
    private final StringRedisTemplate redisTemplate;
    private final java.util.concurrent.ConcurrentHashMap<String, java.util.concurrent.atomic.AtomicInteger> localCounters = new java.util.concurrent.ConcurrentHashMap<>();

    public AdminCacheRateLimitInterceptor(AppConfig appConfig, Optional<StringRedisTemplate> redisTemplateOpt) {
        this.perMinuteLimit = appConfig != null ? appConfig.getCache().getRateLimitPerMinute() : 10;
        this.redisTemplate = redisTemplateOpt.orElse(null);
        if (redisTemplate != null) {
            log.info("Admin cache rate limiting: Using Redis");
        } else {
            log.warn("Admin cache rate limiting: Using local in-memory counters");
        }
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 只对清除缓存操作限流
        if (!"POST".equalsIgnoreCase(request.getMethod()) ||
            !request.getRequestURI().endsWith("/cache/clear")) {
            return true;
        }

        String clientIp = getClientIp(request);
        String minuteKey = DateTimeFormatter.ofPattern("yyyyMMddHHmm").withZone(ZoneOffset.UTC).format(Instant.now());
        String key = "rl:admin-cache:" + clientIp + ":" + minuteKey;
        long count;

        if (redisTemplate != null) {
            try {
                Long val = redisTemplate.opsForValue().increment(key);
                if (val != null && val == 1L) {
                    redisTemplate.expire(key, java.time.Duration.ofMinutes(1));
                }
                count = val == null ? 1 : val;
            } catch (Exception e) {
                log.error("Redis admin cache rate limit error, falling back to deny: {}", e.getMessage());
                response.setStatus(503);
                response.setHeader("Retry-After", "5");
                return false;
            }
        } else {
            var counter = localCounters.computeIfAbsent(key, k -> new java.util.concurrent.atomic.AtomicInteger(0));
            count = counter.incrementAndGet();
        }

        if (count > perMinuteLimit) {
            log.warn("Admin cache rate limit exceeded: ip={}, count={}, limit={}", clientIp, count, perMinuteLimit);
            response.setStatus(429);
            response.setHeader("Retry-After", "60");
            response.setHeader("X-RateLimit-Limit", String.valueOf(perMinuteLimit));
            response.setHeader("X-RateLimit-Remaining", "0");
            return false;
        }
        response.setHeader("X-RateLimit-Limit", String.valueOf(perMinuteLimit));
        response.setHeader("X-RateLimit-Remaining", String.valueOf(Math.max(0, perMinuteLimit - count)));
        return true;
    }

    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip != null && ip.contains(",")) {
            ip = ip.trim().split(",")[0];
        }
        return ip;
    }
}
