package com.lesingle.edu.common.aspect;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.lesingle.edu.common.security.SecurityUtils;
import jakarta.servlet.http.HttpServletRequest;
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 java.lang.reflect.Method;
import java.util.Objects;

/**
 * 请求日志切面
 * 记录所有 Controller 层的请求信息（敏感信息已脱敏）
 */
@Slf4j
@Aspect
@Component
public class RequestLogAspect {

    /**
     * 敏感字段列表（不区分大小写）
     */
    private static final String[] SENSITIVE_FIELDS = {
        "password", "pwd", "secret", "token", "accessToken", "refreshToken",
        "oldPassword", "newPassword", "confirmPassword"
    };

    /**
     * 脱敏掩码
     */
    private static final String MASK = "***";

    /**
     * 定义切点：Controller 层所有方法
     */
    @Pointcut("execution(* com.lesingle.edu.controller..*.*(..))")
    public void controllerPointcut() {
    }

    /**
     * 环绕通知：记录请求日志
     */
    @Around("controllerPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取请求信息
        ServletRequestAttributes attributes =
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取用户信息（可能未登录，需要捕获异常）
        Long userId = null;
        String role = null;
        try {
            userId = SecurityUtils.getCurrentUserId();
            role = SecurityUtils.getCurrentRole();
        } catch (Exception e) {
            // 未登录或 token 无效，不记录用户信息
        }

        // 记录请求开始
        long startTime = System.currentTimeMillis();
        String requestURI = request.getRequestURI();
        String methodType = request.getMethod();
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = method.getName();
        // 请求参数脱敏处理
        String params = desensitize(getRequestParams(joinPoint));

        log.info("===== 请求开始 [userId={}, role={}] =====", userId, role);
        log.info("接口地址：{} {}", methodType, requestURI);
        log.info("请求方法：{}.{}", className, methodName);
        log.info("请求参数：{}", params);

        try {
            // 执行目标方法
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;

            // 记录响应结果
            log.info("响应时间：{}ms", duration);
            log.info("响应结果：{}", desensitize(result));
            log.info("===== 请求结束 [userId={}, role={}] =====", userId, role);

            return result;
        } catch (Throwable e) {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;

            // 记录异常信息
            log.error("===== 请求异常 [userId={}, role={}] =====", userId, role);
            log.error("接口地址：{} {}", methodType, requestURI);
            log.error("执行时间：{}ms", duration);
            log.error("异常信息：{}", e.getMessage());
            log.error("异常类型：{}", e.getClass().getName());
            log.error("堆栈信息：", e);
            log.error("===== 请求异常结束 =====");

            throw e;
        }
    }

    /**
     * 获取请求参数
     */
    private Object getRequestParams(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args.length == 0) {
            return null;
        } else if (args.length == 1) {
            return args[0];
        } else {
            return args;
        }
    }

    /**
     * 请求参数脱敏处理
     * 将敏感字段（如 password、token 等）替换为 ***
     *
     * @param original 原始参数对象
     * @return 脱敏后的 JSON 字符串
     */
    private String desensitize(Object original) {
        if (original == null) {
            return null;
        }
        try {
            // 转换为 JSONObject 以便修改
            String jsonStr = JSON.toJSONString(original);
            JSONObject jsonObject = JSON.parseObject(jsonStr);
            if (jsonObject != null) {
                maskSensitiveFields(jsonObject);
                return jsonObject.toJSONString();
            }
            return jsonStr;
        } catch (Exception e) {
            // 解析失败时返回原始 JSON
            log.warn("请求参数脱敏失败：{}", e.getMessage());
            return JSON.toJSONString(original);
        }
    }

    /**
     * 递归掩码敏感字段
     *
     * @param jsonObject 要处理的 JSON 对象
     */
    private void maskSensitiveFields(JSONObject jsonObject) {
        if (jsonObject == null) {
            return;
        }
        for (String key : jsonObject.keySet()) {
            if (isSensitiveField(key)) {
                jsonObject.put(key, MASK);
            } else {
                // 递归处理嵌套的 JSONObject
                Object value = jsonObject.get(key);
                if (value instanceof JSONObject) {
                    maskSensitiveFields((JSONObject) value);
                }
            }
        }
    }

    /**
     * 判断字段名是否为敏感字段
     *
     * @param fieldName 字段名
     * @return true 如果是敏感字段
     */
    private boolean isSensitiveField(String fieldName) {
        if (fieldName == null) {
            return false;
        }
        String lowerFieldName = fieldName.toLowerCase();
        for (String sensitiveField : SENSITIVE_FIELDS) {
            if (lowerFieldName.equals(sensitiveField.toLowerCase())) {
                return true;
            }
        }
        return false;
    }
}
