package com.ema.admin.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * Controller 日志切面
 * 自动拦截所有 Controller 方法并打印 uri + 出入参数
 *
 * @author 升道
 */
@Slf4j
@Aspect
@Component
public class ControllerLogAspect {

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 切点：拦截所有 Controller 的public方法
     * 包含 modules 下的所有 controller 和 websocket.controller
     */
    @Pointcut("execution(public * com.ema.admin.modules..controller..*.*(..)) || " +
              "execution(public * com.ema.admin.websocket.controller..*.*(..))")
    public void controllerPointcut() {
    }

    /**
     * 环绕通知：记录 Controller 方法执行日志
     * 打印 uri + 入参 + 出参 + 执行时间
     *
     * @param joinPoint 连接点
     * @return 方法执行结果
     * @throws Throwable 方法执行异常
     */
    @Around("controllerPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        // 获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes != null ? attributes.getRequest() : null;

        // 获取方法信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String className = signature.getDeclaringType().getSimpleName();
        String methodName = signature.getName();
        String uri = request != null ? request.getRequestURI() : className + "." + methodName;
        String httpMethod = request != null ? request.getMethod() : "";

        // 获取入参
        Object[] args = joinPoint.getArgs();
        String argsJson = getArgsJson(args);

        // 执行方法
        Object result = null;
        Throwable throwable = null;
        try {
            result = joinPoint.proceed();
            return result;
        } catch (Throwable e) {
            throwable = e;
            throw e;
        } finally {
            long costTime = System.currentTimeMillis() - startTime;

            // 获取出参
            String resultJson = throwable != null ? "Exception: " + throwable.getClass().getSimpleName()
                    : getResultJson(result);

            // 打印日志
            log.info("【Controller日志】{} {} | {}#{} | 入参: {} | 出参: {} | 耗时: {}ms",
                    httpMethod, uri, className, methodName, argsJson, resultJson, costTime);
        }
    }

    /**
     * 获取入参 JSON 字符串
     * 过滤掉不可序列化的参数（如 HttpServletRequest, HttpServletResponse 等）
     *
     * @param args 参数数组
     * @return JSON 字符串
     */
    private String getArgsJson(Object[] args) {
        if (args == null || args.length == 0) {
            return "[]";
        }

        StringBuilder sb = new StringBuilder("[");
        boolean first = true;

        for (Object arg : args) {
            if (arg == null) {
                continue;
            }
            // 跳过不可序列化的参数
            if (isExcludeType(arg)) {
                continue;
            }

            if (!first) {
                sb.append(", ");
            }
            first = false;

            try {
                sb.append(objectMapper.writeValueAsString(arg));
            } catch (JsonProcessingException e) {
                sb.append(arg.toString());
            }
        }

        sb.append("]");
        return sb.length() == 2 ? "[]" : sb.toString();
    }

    /**
     * 获取出参 JSON 字符串
     *
     * @param result 返回值
     * @return JSON 字符串
     */
    private String getResultJson(Object result) {
        if (result == null) {
            return "null";
        }

        // 截断过长的返回内容
        try {
            String json = objectMapper.writeValueAsString(result);
            if (json.length() > 500) {
                return json.substring(0, 500) + "...(truncated)";
            }
            return json;
        } catch (JsonProcessingException e) {
            return result.toString();
        }
    }

    /**
     * 判断是否为需要排除的参数类型
     *
     * @param obj 参数对象
     * @return true-需要排除
     */
    private boolean isExcludeType(Object obj) {
        String className = obj.getClass().getName();
        // 排除 Servlet 相关对象
        return className.contains("HttpServletRequest")
                || className.contains("HttpServletResponse")
                || className.contains("HttpSession")
                || className.contains("MultipartFile")
                || className.contains("BindingResult")
                || className.contains("Model")
                || className.contains("RedirectAttributes")
                || className.contains("SessionStatus");
    }
}