package com.mosquito.project.web;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
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;

public class RateLimitInterceptor implements HandlerInterceptor {

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

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

    public RateLimitInterceptor(Environment env, StringRedisTemplate redisTemplate) {
        this.perMinuteLimit = Integer.parseInt(env.getProperty("app.rate-limit.per-minute", "100"));
        this.redisTemplate = redisTemplate;
        this.productionMode = isProductionProfile(env);
        checkRedisRequirement();
    }

    private boolean isProductionProfile(Environment env) {
        String[] activeProfiles = env.getActiveProfiles();
        for (String profile : activeProfiles) {
            if ("prod".equalsIgnoreCase(profile) || "production".equalsIgnoreCase(profile)) {
                return true;
            }
        }
        return false;
    }

    private void checkRedisRequirement() {
        if (productionMode && redisTemplate == null) {
            log.error("SECURITY: Rate limiting in production mode REQUIRES Redis! " +
                     "Please configure spring.redis.host in application-prod.properties");
            throw new IllegalStateException(
                "Production mode requires Redis for rate limiting. " +
                "Please set spring.redis.host in your production configuration."
            );
        }
        if (redisTemplate != null) {
            log.info("Rate limiting: Using Redis for distributed rate limiting");
        } else {
            log.warn("Rate limiting: Using local in-memory counters (not suitable for multi-instance deployment)");
        }
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String rawApiKey = request.getHeader("X-API-Key");
        if (rawApiKey == null || rawApiKey.isBlank()) {
            response.setStatus(401);
            return false;
        }
        String prefix = rawApiKey.substring(0, Math.min(12, rawApiKey.length())).trim();
        String minuteKey = DateTimeFormatter.ofPattern("yyyyMMddHHmm").withZone(ZoneOffset.UTC).format(Instant.now());
        String key = "rl:" + prefix + ":" + 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 rate limit error, falling back to deny: {}", e.getMessage());
                response.setStatus(503);
                response.setHeader("Retry-After", "5");
                return false;
            }
        } else {
            if (productionMode) {
                log.error("Redis required in production but not available");
                response.setStatus(503);
                return false;
            }
            var counter = localCounters.computeIfAbsent(key, k -> new java.util.concurrent.atomic.AtomicInteger(0));
            count = counter.incrementAndGet();
        }
        if (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;
    }
}

