package com.haha.admin.aspect;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson2.JSON;
import com.haha.admin.utils.IpUtils;
import com.haha.common.annotation.Log;
import com.haha.common.utils.TraceIdUtils;
import com.haha.entity.OperationLog;
import com.haha.service.OperationLogService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 操作日志切面
 * 拦截带有@Log注解的方法，记录操作日志
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class OperationLogAspect {

    private final OperationLogService operationLogService;
    
    /**
     * 记录方法开始时间的ThreadLocal
     */
    private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>();

    /**
     * 切点：所有带有@Log注解的方法
     */
    @Pointcut("@annotation(com.haha.common.annotation.Log)")
    public void logPointcut() {
    }

    /**
     * 方法执行前记录开始时间
     */
    @Before("logPointcut()")
    public void doBefore(JoinPoint joinPoint) {
        START_TIME.set(System.currentTimeMillis());
    }

    /**
     * 方法执行后记录日志
     */
    @AfterReturning(pointcut = "logPointcut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        handleLog(joinPoint, null, result);
    }

    /**
     * 方法抛出异常后记录日志
     */
    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    /**
     * 处理日志记录
     */
    private void handleLog(JoinPoint joinPoint, Exception e, Object result) {
        try {
            // 获取注解信息
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Log logAnnotation = method.getAnnotation(Log.class);
            
            if (logAnnotation == null) {
                return;
            }

            // 创建日志对象
            OperationLog operationLog = new OperationLog();
            
            // 设置TraceId
            String traceId = TraceIdUtils.getTraceId();
            operationLog.setTraceId(traceId);
            
            // 计算处理时长
            Long startTime = START_TIME.get();
            if (startTime != null) {
                long duration = System.currentTimeMillis() - startTime;
                operationLog.setCostTime(duration);
                START_TIME.remove();
            }
            
            // 设置基本信息
            operationLog.setModule(logAnnotation.module());
            operationLog.setOperationType(logAnnotation.operation().getDescription());
            operationLog.setSummary(logAnnotation.summary());
            operationLog.setCreateTime(LocalDateTime.now());
            
            // 获取请求信息
            HttpServletRequest request = getHttpServletRequest();
            if (request != null) {
                operationLog.setRequestUrl(request.getRequestURI());
                operationLog.setRequestMethod(request.getMethod());
                operationLog.setIp(IpUtils.getIpAddr(request));
                operationLog.setAddress(IpUtils.getIpAddress(request));
                
                // 解析User-Agent
                String userAgent = request.getHeader("User-Agent");
                if (userAgent != null) {
                    parseUserAgent(userAgent, operationLog);
                }
                
                // 设置请求参数
                if (logAnnotation.saveParams()) {
                    String params = getRequestParams(joinPoint, request);
                    operationLog.setRequestParams(params);
                }
            }
            
            // 设置操作人信息
            try {
                Object loginId = StpUtil.getLoginIdDefaultNull();
                if (loginId != null) {
                    operationLog.setAdminId(Long.parseLong(loginId.toString()));
                    // 获取用户名 - 尝试从session或token中获取
                    Object username = StpUtil.getSession().get("username");
                    if (username != null) {
                        operationLog.setAdminName(username.toString());
                    }
                }
            } catch (Exception ex) {
                log.debug("获取登录用户信息失败: {}", ex.getMessage());
            }
            
            // 设置操作状态
            if (e != null) {
                operationLog.setStatus(OperationLog.STATUS_FAIL);
                String errorMsg = e.getMessage();
                if (errorMsg != null && errorMsg.length() > 500) {
                    errorMsg = errorMsg.substring(0, 500);
                }
                operationLog.setErrorMsg(errorMsg);
            } else {
                operationLog.setStatus(OperationLog.STATUS_SUCCESS);
            }
            
            // 设置响应结果
            if (logAnnotation.saveResult() && result != null) {
                try {
                    String resultStr = JSON.toJSONString(result);
                    if (resultStr.length() > 2000) {
                        resultStr = resultStr.substring(0, 2000);
                    }
                    operationLog.setResponseResult(resultStr);
                } catch (Exception ex) {
                    log.debug("序列化响应结果失败: {}", ex.getMessage());
                }
            }
            
            // 异步保存日志
            saveLogAsync(operationLog);
            
        } catch (Exception ex) {
            log.error("记录操作日志异常: {}", ex.getMessage(), ex);
        }
    }
    
    /**
     * 异步保存日志
     */
    @Async
    public void saveLogAsync(OperationLog operationLog) {
        try {
            operationLogService.save(operationLog);
        } catch (Exception e) {
            log.error("保存操作日志失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 获取HttpServletRequest对象
     */
    private HttpServletRequest getHttpServletRequest() {
        try {
            jakarta.servlet.http.HttpServletRequest request = 
                (jakarta.servlet.http.HttpServletRequest) org.springframework.web.context.request.RequestContextHolder
                    .currentRequestAttributes()
                    .resolveReference(org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST);
            return request;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取请求参数
     */
    private String getRequestParams(JoinPoint joinPoint, HttpServletRequest request) {
        try {
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length == 0) {
                return null;
            }
            
            Map<String, Object> params = new HashMap<>();
            String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
            
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                String paramName = (paramNames != null && i < paramNames.length) ? paramNames[i] : "arg" + i;
                
                // 过滤不需要的参数
                if (arg instanceof HttpServletRequest 
                    || arg instanceof HttpServletResponse 
                    || arg instanceof MultipartFile) {
                    continue;
                }
                
                try {
                    params.put(paramName, arg);
                } catch (Exception e) {
                    params.put(paramName, arg.getClass().getName());
                }
            }
            
            String paramsStr = JSON.toJSONString(params);
            if (paramsStr.length() > 2000) {
                paramsStr = paramsStr.substring(0, 2000);
            }
            return paramsStr;
        } catch (Exception e) {
            log.debug("获取请求参数失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 解析User-Agent
     */
    private void parseUserAgent(String userAgent, OperationLog operationLog) {
        if (userAgent == null) {
            return;
        }
        
        userAgent = userAgent.toLowerCase();
        
        // 解析操作系统
        if (userAgent.contains("windows")) {
            operationLog.setOs("Windows");
        } else if (userAgent.contains("mac")) {
            operationLog.setOs("Mac OS");
        } else if (userAgent.contains("linux")) {
            operationLog.setOs("Linux");
        } else if (userAgent.contains("android")) {
            operationLog.setOs("Android");
        } else if (userAgent.contains("iphone") || userAgent.contains("ipad")) {
            operationLog.setOs("iOS");
        } else {
            operationLog.setOs("Unknown");
        }
        
        // 解析浏览器
        if (userAgent.contains("edge")) {
            operationLog.setBrowser("Edge");
        } else if (userAgent.contains("chrome")) {
            operationLog.setBrowser("Chrome");
        } else if (userAgent.contains("firefox")) {
            operationLog.setBrowser("Firefox");
        } else if (userAgent.contains("safari") && !userAgent.contains("chrome")) {
            operationLog.setBrowser("Safari");
        } else if (userAgent.contains("opera") || userAgent.contains("opr")) {
            operationLog.setBrowser("Opera");
        } else if (userAgent.contains("msie") || userAgent.contains("trident")) {
            operationLog.setBrowser("IE");
        } else {
            operationLog.setBrowser("Unknown");
        }
    }
}
