package com.cameleer.server.app.interceptor;

import com.cameleer.server.core.admin.AuditCategory;
import com.cameleer.server.core.admin.AuditResult;
import com.cameleer.server.core.admin.AuditService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Safety-net audit interceptor that logs a basic entry for any state-changing
 * request (POST/PUT/DELETE) that was not explicitly audited by the controller.
 * <p>
 * Controllers that call {@link AuditService#log} set the {@code audit.logged}
 * request attribute, which this interceptor checks to avoid double-recording.
 */
@Component
public class AuditInterceptor implements HandlerInterceptor {

    private static final Set<String> AUDITABLE_METHODS = Set.of("POST", "PUT", "DELETE");
    private static final List<String> EXCLUDED_PATH_PATTERNS = List.of(
            "/api/v1/environments/*/executions/search");
    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    private final AuditService auditService;

    public AuditInterceptor(AuditService auditService) {
        this.auditService = auditService;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) {
        if (!AUDITABLE_METHODS.contains(request.getMethod())) {
            return;
        }
        if (Boolean.TRUE.equals(request.getAttribute("audit.logged"))) {
            return;
        }

        String path = request.getRequestURI();
        for (String pattern : EXCLUDED_PATH_PATTERNS) {
            if (PATH_MATCHER.match(pattern, path)) {
                return;
            }
        }
        AuditResult result = response.getStatus() < 400 ? AuditResult.SUCCESS : AuditResult.FAILURE;

        auditService.log(
                "HTTP " + request.getMethod() + " " + path,
                AuditCategory.INFRA,
                path,
                Map.of("status", response.getStatus()),
                result,
                request);
    }
}
