package com.mosquito.project.web;

import com.mosquito.project.service.AuditService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * 审计日志拦截器 - 自动记录关键操作
 * 需要审计的URL模式:
 * - POST/PUT/DELETE 操作自动记录
 * - GET 导出操作 (export)
 * - 权限变更操作 (role, permission)
 */
@Component
public class AuditInterceptor implements HandlerInterceptor {

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

    private final AuditService auditService;

    // 需要审计的HTTP方法（包含GET导出操作）
    private static final Set<String> AUDIT_METHODS = Set.of("POST", "PUT", "DELETE");

    // 需要审计的GET方法URL模式（仅导出操作）
    private static final Set<String> AUDIT_GET_PATHS = Set.of("/export");

    // 需要审计的URL模式（正则）
    private static final Pattern AUDIT_URL_PATTERN = Pattern.compile(
        ".*/(export|roles|permissions|users/\\d+/roles|activities|callback|rewards|approve).*",
        Pattern.CASE_INSENSITIVE
    );

    // 导出操作标识
    private static final Pattern EXPORT_PATTERN = Pattern.compile(".*export.*", Pattern.CASE_INSENSITIVE);

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

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 预处理器不记录，留给postHandle统一记录
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        String method = request.getMethod();
        String uri = request.getRequestURI();

        // 检查是否是GET导出操作（特殊处理）
        boolean isGetExport = "GET".equalsIgnoreCase(method) &&
            (uri.contains("/export") || EXPORT_PATTERN.matcher(uri).matches());

        // 只审计需要记录的方法（包含GET导出）
        if (!AUDIT_METHODS.contains(method) && !isGetExport) {
            return;
        }

        // 检查是否是需要审计的URL
        if (!AUDIT_URL_PATTERN.matcher(uri).matches() && !isGetExport) {
            return;
        }

        // 记录审计日志
        try {
            int status = response.getStatus();
            String operation = determineOperation(method, uri);

            Map<String, Object> logData = new java.util.HashMap<>();
            logData.put("userId", getUserId(request));
            logData.put("userName", getUserName(request));
            logData.put("ipAddress", getClientIp(request));
            logData.put("operation", operation);
            logData.put("resource", determineResource(uri));
            logData.put("resourceId", getResourceId(uri));
            logData.put("requestMethod", method);
            logData.put("requestUrl", uri);
            logData.put("requestParams", getRequestParams(request));
            logData.put("responseStatus", status);
            logData.put("details", getOperationDetails(method, uri));

            auditService.recordAuditLog(logData);
            log.debug("审计日志已记录: {} {} -> {}", method, uri, status);
        } catch (Exception e) {
            log.error("记录审计日志失败: {} {}", method, uri, e);
        }
    }

    private String getUserId(HttpServletRequest request) {
        Object userId = request.getAttribute("userId");
        return userId != null ? userId.toString() : "anonymous";
    }

    private String getUserName(HttpServletRequest request) {
        // 统一使用 username 属性名，与 UserAuthInterceptor 保持一致
        Object userName = request.getAttribute("username");
        return userName != null ? userName.toString() : "系统";
    }

    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();
        }
        return ip;
    }

    private String getRequestParams(HttpServletRequest request) {
        StringBuilder params = new StringBuilder();
        request.getParameterMap().forEach((key, values) -> {
            if (params.length() > 0) params.append("&");
            params.append(key).append("=").append(String.join(",", values));
        });
        return params.toString();
    }

    private String getResourceId(String uri) {
        // 从URL中提取资源ID，例如: /api/v1/users/123/roles -> 123
        String[] parts = uri.split("/");
        for (int i = 0; i < parts.length; i++) {
            if (parts[i].matches("\\d+") && i > 0) {
                // 检查前一个是否是资源类型
                String prev = parts[i - 1];
                if (prev.equals("users") || prev.equals("activities") || prev.equals("roles")) {
                    return parts[i];
                }
            }
        }
        return null;
    }

    private String determineOperation(String method, String uri) {
        if (EXPORT_PATTERN.matcher(uri).matches()) {
            return "EXPORT";
        }
        return switch (method) {
            case "POST" -> "CREATE";
            case "PUT", "PATCH" -> "UPDATE";
            case "DELETE" -> "DELETE";
            default -> "ACCESS";
        };
    }

    private String determineResource(String uri) {
        if (uri.contains("/users")) return "USER";
        if (uri.contains("/activities")) return "ACTIVITY";
        if (uri.contains("/roles")) return "ROLE";
        if (uri.contains("/permissions")) return "PERMISSION";
        if (uri.contains("/rewards")) return "REWARD";
        if (uri.contains("/approve")) return "APPROVAL";
        if (uri.contains("/callback")) return "CALLBACK";
        return "UNKNOWN";
    }

    private String getOperationDetails(String method, String uri) {
        return String.format("执行%s操作: %s", method, uri);
    }
}
