package com.writeoff.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.writeoff.common.api.ApiErrorResponse;
import com.writeoff.common.exception.ErrorCodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

@Component
@Order(1)
public class RateLimitFilter implements Filter {

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

    private final RateLimitService rateLimitService;
    private final ObjectMapper objectMapper = new ObjectMapper();

    public RateLimitFilter(RateLimitService rateLimitService) {
        this.rateLimitService = rateLimitService;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
            chain.doFilter(request, response);
            return;
        }
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String path = httpRequest.getRequestURI();
        if (!path.startsWith("/api/")) {
            chain.doFilter(request, response);
            return;
        }

        boolean isAuthPath = path.startsWith("/api/auth/login") || path.startsWith("/api/auth/platform-login");
        RateLimitService.RateLimitDecision decision;
        try {
            decision = rateLimitService.tryAcquire(getClientIp(httpRequest), isAuthPath);
        } catch (Exception ex) {
            log.warn("Rate limit degraded to allow request for path {}", path, ex);
            chain.doFilter(request, response);
            return;
        }

        if (!decision.isAllowed()) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setStatus(429);
            httpResponse.setContentType("application/json;charset=utf-8");
            httpResponse.setHeader("Retry-After", String.valueOf(decision.getRetryAfterSeconds()));
            Map<String, String> errors = new LinkedHashMap<>();
            errors.put("scope", decision.getScope());
            errors.put("limit", String.valueOf(decision.getLimit()));
            errors.put("retryAfterSeconds", String.valueOf(decision.getRetryAfterSeconds()));
            ApiErrorResponse body = ApiErrorResponse.of(ErrorCodes.RATE_LIMITED, "请求过于频繁，请稍后重试", errors);
            httpResponse.getWriter().write(objectMapper.writeValueAsString(body));
            return;
        }
        chain.doFilter(request, response);
    }

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