package com.simuwang.deploy.components;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.simuwang.base.common.util.ServletUtils;
import com.simuwang.logging.Logging;
import com.simuwang.logging.LoggingService;
import com.simuwang.logging.SystemLog;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.util.StopWatch;

import java.util.Iterator;
import java.util.List;
import java.util.Objects;

/**
 * @author wangzaijun
 * @date 2024/9/14 14:07
 * @description 系统日志拦截切面
 */
@Order(2)
@Aspect
public class LoggingAspect {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final LoggingService service;

    public LoggingAspect(LoggingService service) {
        this.service = service;
    }

    @Around("@annotation(annotation)")
    public Object around(ProceedingJoinPoint joinPoint, SystemLog annotation) throws Throwable {
        Class<?> clazz = AopUtils.getTargetClass(joinPoint.getTarget());
        SystemLog systemLog = clazz.getAnnotation(SystemLog.class);
        String title = annotation.value();
        if (systemLog != null && StrUtil.isNotBlank(systemLog.value())) {
            title = systemLog.value() + "-" + title;
        }
        HttpServletRequest request = ServletUtils.getRequest();
        Logging.Builder builder = Logging.builder().title(title).type(annotation.type().getValue()).method(request.getMethod())
                .requestUri(URLUtil.getPath(request.getRequestURI())).remoteAddr(ServletUtils.getIpAddr(request));
        builder.params(this.getParams(request, joinPoint.getArgs()));
        StopWatch watch = new StopWatch();
        watch.start();
        Object result;
        try {
            result = joinPoint.proceed();
            if (annotation.saveResult()) {
                builder.result(JSONUtil.toJsonStr(result));
            }
        } catch (Throwable throwable) {
            builder.exception(ExceptionUtil.stacktraceToString(throwable));
            builder.hasException(true);
            throw throwable;
        } finally {
            watch.stop();
            builder.executeTime(watch.getTotalTimeMillis());
            Logging logging = builder.build();
            try {
                this.service.asyncSave(logging);
            } catch (Exception e) {
                this.logger.warn("请求{} 日志保存报错\n{}", logging, ExceptionUtil.stacktraceToString(e));
            }
        }
        return result;
    }

    private String getParams(HttpServletRequest request, Object[] args) {
        if (args == null) {
            return null;
        }
        boolean uploadRequest = isUploadRequest(request);
        StringBuilder params;
        if (request.getMethod().equalsIgnoreCase(HttpMethod.GET.name()) && !uploadRequest) {
            List<String> tempList = ListUtil.list(false);
            Iterator<String> paramNames = request.getParameterNames().asIterator();
            while (paramNames.hasNext()) {
                String param = paramNames.next();
                String value = request.getParameter(param);
                tempList.add(param + "=" + value);
            }
            params = new StringBuilder(String.join(",", tempList));
        } else {
            params = new StringBuilder();
            for (Object arg : args) {
                if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
                    continue;
                }
                String param;
                try {
                    param = JSONUtil.toJsonStr(arg);
                } catch (Exception ignored) {
                    param = Objects.toString(arg);
                }
                params.append(param);
            }
        }
        return params.toString();
    }

    private boolean isUploadRequest(HttpServletRequest request) {
        String method = request.getMethod();
        String contentType = request.getContentType();

        return HttpMethod.POST.name().equalsIgnoreCase(method)
                && contentType != null && contentType.startsWith("multipart/form-data");
    }
}
