package com.kexue.skills.interceptor;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kexue.skills.annotation.Log;
import com.kexue.skills.entity.LogRecord;
import com.kexue.skills.mapper.SysLogMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 操作日志拦截器
 * 基于 HandlerInterceptor 实现，捕获请求和响应信息
 * 
 * @author 王志维
 * @since 2026-04-14
 */
public class LogInterceptor implements HandlerInterceptor {
    
    private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();
    
    private SysLogMapper sysLogMapper;
    
    // 使用 ThreadLocal 存储请求开始时间和日志记录
    private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>();
    private static final ThreadLocal<LogRecord> LOG_RECORD = new ThreadLocal<>();
    
    /**
     * 设置 SysLogMapper
     */
    public void setSysLogMapper(SysLogMapper sysLogMapper) {
        this.sysLogMapper = sysLogMapper;
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 只处理方法级别的请求
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        
        // 检查是否有 @Log 注解
        Log methodLog = handlerMethod.getMethodAnnotation(Log.class);
        Log classLog = handlerMethod.getBeanType().getAnnotation(Log.class);
        
        // 如果方法和类都没有 @Log 注解，不记录日志
        if (methodLog == null && classLog == null) {
            return true;
        }
        
        // 如果方法级别设置了 ignore=true，不记录日志
        if (methodLog != null && methodLog.ignore()) {
            return true;
        }
        
        // 记录开始时间
        START_TIME.set(System.currentTimeMillis());
        
        // 构建日志记录对象
        LogRecord logRecord = new LogRecord();
        
        // 设置模块和描述
        String module = "";
        String description = "";
        if (classLog != null) {
            module = classLog.module();
        }
        if (methodLog != null) {
            if (StrUtil.isNotBlank(methodLog.module())) {
                module = methodLog.module();
            }
            description = methodLog.description();
        }
        logRecord.setModule(module);
        logRecord.setDescription(description);
        
        // 设置请求信息
        LogRecord.LogRequest logRequest = new LogRecord.LogRequest();
        logRequest.setMethod(request.getMethod());
        logRequest.setUrl(request.getRequestURL().toString());
        logRequest.setHeaders(getRequestHeaders(request));
        logRequest.setIp(getClientIp(request));
        logRequest.setBrowser(getBrowserInfo(request));
        logRequest.setOs(getOsInfo(request));
        
        // 读取请求体（从包装对象中获取）
        if (request instanceof CachedBodyHttpServletRequest) {
            CachedBodyHttpServletRequest cachedRequest = (CachedBodyHttpServletRequest) request;
            String requestBody = cachedRequest.getCachedBodyString();
            // 限制请求体大小，避免过大
            if (requestBody.length() > 10000) {
                requestBody = requestBody.substring(0, 10000) + "... [truncated]";
            }
            logRequest.setBody(requestBody);
        }
        
        logRecord.setRequest(logRequest);
        logRecord.setTimestamp(Instant.now());
        
        // 存储到 ThreadLocal
        LOG_RECORD.set(logRecord);
        
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                Object handler, Exception ex) {
        // 只处理方法级别的请求
        if (!(handler instanceof HandlerMethod)) {
            return;
        }
        
        // 获取日志记录
        LogRecord logRecord = LOG_RECORD.get();
        Long startTime = START_TIME.get();
        
        if (logRecord == null || startTime == null) {
            return;
        }
        
        try {
            // 计算耗时
            long timeTaken = System.currentTimeMillis() - startTime;
            logRecord.setTimeTaken(timeTaken);
            
            // 设置响应信息
            LogRecord.LogResponse logResponse = new LogRecord.LogResponse();
            logResponse.setStatus(response.getStatus());
            logResponse.setHeaders(getResponseHeaders(response));
            
            // 读取响应体（从包装对象中获取）
            if (response instanceof CachedBodyHttpServletResponse) {
                CachedBodyHttpServletResponse cachedResponse = (CachedBodyHttpServletResponse) response;
                String responseBody = cachedResponse.getCachedBodyString();
                // 限制响应体大小，避免过大
                if (responseBody.length() > 10000) {
                    responseBody = responseBody.substring(0, 10000) + "... [truncated]";
                }
                logResponse.setBody(responseBody);
            }
            
            logRecord.setResponse(logResponse);
            
            // 异步保存日志
            saveLogAsync(logRecord);
            
        } catch (Exception e) {
            logger.error("记录操作日志失败: {}", e.getMessage(), e);
        } finally {
            // 清理 ThreadLocal
            START_TIME.remove();
            LOG_RECORD.remove();
        }
    }
    
    /**
     * 获取请求头
     */
    private Map<String, String> getRequestHeaders(HttpServletRequest request) {
        Map<String, String> headers = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            headers.put(headerName, request.getHeader(headerName));
        }
        return headers;
    }
    
    /**
     * 获取响应头
     */
    private Map<String, String> getResponseHeaders(HttpServletResponse response) {
        Map<String, String> headers = new HashMap<>();
        for (String headerName : response.getHeaderNames()) {
            headers.put(headerName, response.getHeader(headerName));
        }
        return headers;
    }
    
    /**
     * 获取客户端IP
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 多个代理时，第一个IP为真实IP
        if (StrUtil.isNotBlank(ip) && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }
    
    /**
     * 获取浏览器信息
     */
    private String getBrowserInfo(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        if (StrUtil.isBlank(userAgent)) {
            return "Unknown";
        }
        UserAgent ua = UserAgentUtil.parse(userAgent);
        String browserName = ua.getBrowser().getName();
        String version = ua.getVersion();
        // 避免显示 "Unknown null"
        if (StrUtil.isBlank(version) || "null".equals(version)) {
            return StrUtil.blankToDefault(browserName, "Unknown");
        }
        return browserName + " " + version;
    }
    
    /**
     * 获取操作系统信息
     */
    private String getOsInfo(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        if (StrUtil.isBlank(userAgent)) {
            return "Unknown";
        }
        UserAgent ua = UserAgentUtil.parse(userAgent);
        String osName = ua.getOs().getName();
        return StrUtil.blankToDefault(osName, "Unknown");
    }
    
    /**
     * 异步保存日志
     */
    @Async
    public void saveLogAsync(LogRecord logRecord) {
        try {
            // 转换为 SysLog 实体
            com.kexue.skills.entity.SysLog sysLog = new com.kexue.skills.entity.SysLog();
            
            // 设置基本信息
            sysLog.setDescription(StrUtil.blankToDefault(logRecord.getDescription(), ""));
            sysLog.setModule(StrUtil.blankToDefault(logRecord.getModule(), ""));
            sysLog.setTimeTaken(logRecord.getTimeTaken() != null ? logRecord.getTimeTaken() : 0L);
            sysLog.setCreateTime(new java.util.Date());
            sysLog.setUpdateTime(new java.util.Date());
            sysLog.setDeleteFlag(0);
            
            // 设置请求信息
            LogRecord.LogRequest request = logRecord.getRequest();
            if (request != null) {
                sysLog.setRequestMethod(StrUtil.blankToDefault(request.getMethod(), "UNKNOWN"));
                sysLog.setRequestUrl(StrUtil.blankToDefault(request.getUrl(), ""));
                try {
                    sysLog.setRequestHeaders(objectMapper.writeValueAsString(request.getHeaders()));
                } catch (Exception e) {
                    logger.warn("序列化请求头失败: {}", e.getMessage());
                    sysLog.setRequestHeaders("{}");
                }
                sysLog.setRequestBody(StrUtil.blankToDefault(request.getBody(), ""));
                sysLog.setIp(StrUtil.blankToDefault(request.getIp(), "127.0.0.1"));
                sysLog.setAddress(StrUtil.blankToDefault(request.getAddress(), ""));
                sysLog.setBrowser(StrUtil.blankToDefault(request.getBrowser(), "Unknown"));
                sysLog.setOs(StrUtil.blankToDefault(request.getOs(), "Unknown"));
            } else {
                // 设置默认值
                sysLog.setRequestMethod("UNKNOWN");
                sysLog.setRequestUrl("");
                sysLog.setRequestHeaders("{}");
                sysLog.setRequestBody("");
                sysLog.setIp("127.0.0.1");
                sysLog.setAddress("");
                sysLog.setBrowser("Unknown");
                sysLog.setOs("Unknown");
            }
            
            // 设置响应信息
            LogRecord.LogResponse response = logRecord.getResponse();
            if (response != null) {
                sysLog.setStatusCode(response.getStatus() != null ? response.getStatus() : 200);
                try {
                    sysLog.setResponseHeaders(objectMapper.writeValueAsString(response.getHeaders()));
                } catch (Exception e) {
                    logger.warn("序列化响应头失败: {}", e.getMessage());
                    sysLog.setResponseHeaders("{}");
                }
                String responseBody = StrUtil.blankToDefault(response.getBody(), "");
                sysLog.setResponseBody(responseBody);
                
                // 判断成功/失败状态
                Integer statusCode = response.getStatus();
                if (statusCode != null) {
                    sysLog.setStatus(statusCode >= 400 ? 2 : 1);
                } else {
                    sysLog.setStatus(1);
                }
                
                // 解析响应体中的错误信息
                if (StrUtil.isNotBlank(responseBody)) {
                    try {
                        // 尝试解析 JSON 响应体
                        com.fasterxml.jackson.databind.JsonNode jsonNode = objectMapper.readTree(responseBody);
                        
                        // 检查是否有错误标识（status、code、success等字段）
                        boolean hasError = false;
                        String errorMsg = "";
                        
                        // 方式1: 检查 status 字段（如 {"status":500,"message":"密码不正确"}）
                        if (jsonNode.has("status")) {
                            int statusValue = jsonNode.get("status").asInt();
                            if (statusValue >= 400 || statusValue == 0) {
                                hasError = true;
                                if (jsonNode.has("message")) {
                                    errorMsg = jsonNode.get("message").asText();
                                } else if (jsonNode.has("msg")) {
                                    errorMsg = jsonNode.get("msg").asText();
                                } else if (jsonNode.has("error")) {
                                    errorMsg = jsonNode.get("error").asText();
                                }
                            }
                        }
                        
                        // 方式2: 检查 code 字段（如 {"code":500,"message":"密码不正确"}）
                        if (!hasError && jsonNode.has("code")) {
                            int codeValue = jsonNode.get("code").asInt();
                            if (codeValue != 200 && codeValue != 0) {
                                hasError = true;
                                if (jsonNode.has("message")) {
                                    errorMsg = jsonNode.get("message").asText();
                                } else if (jsonNode.has("msg")) {
                                    errorMsg = jsonNode.get("msg").asText();
                                } else if (jsonNode.has("error")) {
                                    errorMsg = jsonNode.get("error").asText();
                                }
                            }
                        }
                        
                        // 方式3: 检查 success 字段（如 {"success":false,"message":"密码不正确"}）
                        if (!hasError && jsonNode.has("success")) {
                            boolean success = jsonNode.get("success").asBoolean();
                            if (!success) {
                                hasError = true;
                                if (jsonNode.has("message")) {
                                    errorMsg = jsonNode.get("message").asText();
                                } else if (jsonNode.has("msg")) {
                                    errorMsg = jsonNode.get("msg").asText();
                                } else if (jsonNode.has("error")) {
                                    errorMsg = jsonNode.get("error").asText();
                                }
                            }
                        }
                        
                        // 如果检测到错误，更新状态和错误信息
                        if (hasError) {
                            sysLog.setStatus(2); // 失败
                            sysLog.setErrorMsg(StrUtil.blankToDefault(errorMsg, "业务操作失败"));
                        }
                        
                    } catch (Exception e) {
                        // 不是 JSON 格式或解析失败，忽略
                        logger.debug("响应体不是JSON格式或解析失败: {}", e.getMessage());
                    }
                }
            } else {
                // 设置默认值
                sysLog.setStatusCode(200);
                sysLog.setResponseHeaders("{}");
                sysLog.setResponseBody("");
                sysLog.setStatus(1);
            }
            
            // 设置错误信息默认值
            sysLog.setErrorMsg("");
            
            // TODO: 从 Token 中解析用户ID
            // 这里需要根据实际的认证框架来实现
            // sysLog.setCreateUser(userId);
            sysLog.setCreateUser(null);
            
            // 插入数据库
            sysLogMapper.insert(sysLog);
            
            logger.debug("操作日志保存成功: {}", sysLog.getDescription());
            
        } catch (Exception e) {
            logger.error("保存操作日志失败: {}", e.getMessage(), e);
        }
    }
}
