/**
 * @ProjectName: 智慧评标-主观分助手
 * @Package: com.zhpb.gdyd_zhpb_zgf.interceptor
 * @ClassName: ApiLogInterceptor
 * @Description: API日志拦截器，拦截所有API请求并记录详细的调用信息到数据库
 * @Author: 张志锋
 * @Date: 2025-10-31
 * @Version: 1.0
 * @Copyright: © 2025 智慧评标-主观分助手
 */
package com.zhpb.gdyd_zhpb_zgf.interceptor;

import com.alibaba.fastjson2.JSON;
import com.zhpb.gdyd_zhpb_zgf.entity.mysql.ApiLog;
import com.zhpb.gdyd_zhpb_zgf.repository.mysql.ApiLogRepository;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * API调用日志拦截器
 */
@Component
public class ApiLogInterceptor implements HandlerInterceptor {

    @Autowired
    private ApiLogRepository apiLogRepository;

    // 接口名称映射
    private static final Map<String, String> API_NAME_MAP = new HashMap<String, String>() {{
        put("/api/v1/sso/login", "SSO登录接口");
        put("/api/v1/statistics/total", "获取API总访问次数");
        put("/api/v1/statistics/count", "获取特定接口访问次数");
        put("/api/v1/files/uploadzip", "上传ZIP压缩包接口");
        put("/health", "健康检查");
        put("/actuator/health", "健康检查");
        put("/", "首页");
        put("/swagger-ui.html", "Swagger文档");
        put("/api-docs", "API文档JSON");
    }};

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 不做处理
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        long startTime = (Long) request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;

        String path = request.getRequestURI();
        String method = request.getMethod();

        // 排除健康检查页面发起的请求（避免产生大量无意义的日志）
        // 健康检查页面通常使用HEAD/OPTIONS请求来检查接口状态，不应该记录日志
        if ("HEAD".equalsIgnoreCase(method) || "OPTIONS".equalsIgnoreCase(method)) {
            return;
        }

        // 只记录API接口（以/api/开头的路径）和健康检查接口，排除静态资源
        if (!path.startsWith("/api/") && 
            !path.equals("/health") && 
            !path.equals("/actuator/health") &&
            !path.startsWith("/swagger") &&
            !path.startsWith("/webjars") &&
            !path.startsWith("/v3/api-docs")) {
            return;
        }

        // 在异步执行前，同步获取所有需要的信息（避免响应对象被回收）
        int statusCode = response.getStatus();
        String clientIp = getClientIp(request);
        String apiName = getApiName(path);
        String requestParams = getRequestParams(request);

        // 获取请求体和响应体的内容（需要在响应对象被回收前获取）
        String requestBody = null;
        String responseBody = null;
        
        if (request instanceof ContentCachingRequestWrapper) {
            ContentCachingRequestWrapper wrappedRequest = (ContentCachingRequestWrapper) request;
            requestBody = getRequestBody(wrappedRequest);
        }
        
        if (response instanceof ContentCachingResponseWrapper) {
            ContentCachingResponseWrapper wrappedResponse = (ContentCachingResponseWrapper) response;
            try {
                responseBody = getResponseBody(wrappedResponse);
            } catch (Exception e) {
                // 如果读取响应体失败，只记录警告，不阻塞
                System.err.println("读取响应体失败: " + e.getMessage());
            }
        }

        // 获取异常信息
        String errorMsg = ex != null ? ex.getMessage() : null;

        // 使用获取到的值创建最终变量（避免在异步线程中访问响应对象）
        final int finalStatusCode = statusCode;
        final String finalClientIp = clientIp;
        final String finalApiName = apiName;
        final String finalRequestParams = requestParams;
        final String finalRequestBody = requestBody;
        final String finalResponseBody = responseBody;
        final String finalErrorMsg = errorMsg;
        final String finalPath = path;
        final String finalMethod = method;
        final long finalDuration = duration;

        // 异步保存日志，避免阻塞请求
        CompletableFuture.runAsync(() -> {
            try {
                ApiLog apiLog = new ApiLog();
                // 从请求参数中提取userId和userName（处理不同接口的参数命名差异）
                String userId = null;
                String userName = null;
                if (finalRequestParams != null) {
                    try {
                        Map<String, Object> params = JSON.parseObject(finalRequestParams, Map.class);
                        // 尝试多种参数命名方式
                        userId = (String) params.get("userId");
                        if (userId == null) {
                            userId = (String) params.get("user_id");
                        }
                        if (userId == null) {
                            userId = (String) params.get("userld");
                        }
                        
                        userName = (String) params.get("userName");
                        if (userName == null) {
                            userName = (String) params.get("user_name");
                        }
                    } catch (Exception e) {
                        // 解析失败忽略
                    }
                }
                
                apiLog.setApiPath(finalPath);
                apiLog.setApiName(finalApiName);
                apiLog.setHttpMethod(finalMethod);
                apiLog.setStatusCode(finalStatusCode);
                apiLog.setDuration(finalDuration);
                apiLog.setClientIp(finalClientIp);
                apiLog.setUserId(userId);
                apiLog.setUserName(userName);
                apiLog.setCreateTime(LocalDateTime.now());
                apiLog.setRequestParams(finalRequestParams);
                apiLog.setRequestBody(finalRequestBody);
                apiLog.setResponseBody(finalResponseBody);
                apiLog.setErrorMsg(finalErrorMsg);

                // 保存到数据库
                apiLogRepository.save(apiLog);
            } catch (Exception e) {
                // 记录日志失败不影响主流程，只打印错误
                System.err.println("保存API日志失败: " + e.getMessage());
                e.printStackTrace();
            }
        });
    }

    /**
     * 获取接口中文名称
     */
    private String getApiName(String path) {
        return API_NAME_MAP.getOrDefault(path, path);
    }

    /**
     * 获取请求参数
     */
    private String getRequestParams(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (parameterMap.isEmpty()) {
            return null;
        }
        
        Map<String, Object> params = new HashMap<>();
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String[] values = entry.getValue();
            if (values.length == 1) {
                params.put(entry.getKey(), values[0]);
            } else {
                params.put(entry.getKey(), values);
            }
        }
        return params.isEmpty() ? null : JSON.toJSONString(params);
    }

    /**
     * 获取请求体
     */
    private String getRequestBody(ContentCachingRequestWrapper request) {
        byte[] content = request.getContentAsByteArray();
        if (content.length == 0) {
            return null;
        }
        String body = new String(content, StandardCharsets.UTF_8);
        // 限制请求体长度，避免存储过大的数据
        return body.length() > 10000 ? body.substring(0, 10000) + "...(truncated)" : body;
    }

    /**
     * 获取响应体
     */
    private String getResponseBody(ContentCachingResponseWrapper response) {
        byte[] content = response.getContentAsByteArray();
        if (content.length == 0) {
            return null;
        }
        String body = new String(content, StandardCharsets.UTF_8);
        // 限制响应体长度，避免存储过大的数据
        return body.length() > 10000 ? body.substring(0, 10000) + "...(truncated)" : body;
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

