package com.sckw.log.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @desc: controller层接口日志打印
 * @author: yzc
 * @date: 2023-08-21 11:23
 */
@Aspect
@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class LogInfoAspect {

    public static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss:SSS";

    @Pointcut("execution(* com.sckw.*.controller..*.*(..))")
    public void cutController() {
    }

    @Around(value = "cutController()", argNames = "p")
    public Object cutController(ProceedingJoinPoint p) throws Throwable {
        return getResult(p);

    }

    @Pointcut("execution(* com.sckw.*.dubbo..*.*(..))")
    public void cutDubbo() {
    }

    @Around(value = "cutDubbo()", argNames = "p")
    public Object around(ProceedingJoinPoint p) throws Throwable {
        return getResult(p);
    }

    private Object getResult(ProceedingJoinPoint p) throws Throwable {
        Object result = null;
        //开始时间
        Date startTime = new Date();
        String targetName = p.getTarget().getClass().getName();
        String methodName = p.getSignature().getName();
        Object[] args = p.getArgs();
        Stream<?> stream = ArrayUtils.isEmpty(args) ? Stream.empty() : Arrays.stream(args);
        List<Object> logArgs = stream
                .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)) && !(arg instanceof MultipartFile))
                .collect(Collectors.toList());
        //过滤后序列化无异常
        String param = JSON.toJSONString(logArgs);
        String exception = "";
        try {
            //执行结果，返回参数
            result = p.proceed();
        } catch (Throwable e) {
            exception = e.getMessage();
            //异常抛出
            throw e;
        } finally {
            Date endTime = new Date();
            long time = endTime.getTime() - startTime.getTime();
            Boolean slowRequest = (time > 1500L);
            log.info("{}.{}," +
                            "param={}," +
                            "result={}," +
                            "exception={}," +
                            "[{}->{}],slowRequest{}=[{}]", targetName, methodName,
                    param,
                    JSON.toJSONString(result),
                    exception,
                    DateFormatUtils.format(startTime, TIME_PATTERN),
                    DateFormatUtils.format(endTime, TIME_PATTERN),
                    slowRequest,
                    time);
        }
        return result;
    }

    @Pointcut("execution(* com.sckw.*.service..*.*(..))")
    public void cutService() {
    }

    @AfterThrowing(pointcut = "cutService()", throwing = "e")
    public void doAfterThrowing(JoinPoint point, Throwable e) throws Throwable {
        Date startTime = new Date();
        String targetName = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();
        //获取用户请求方法的参数并序列化为JSON格式字符串
        StringBuilder params = new StringBuilder();
        if (point.getArgs() != null && point.getArgs().length > 0) {
            for (int i = 0; i < point.getArgs().length; i++) {
                Object object = point.getArgs()[i];
                if (!(object instanceof MultipartFile) && !(object instanceof HttpServletResponse) && !(object instanceof HttpServletRequest)) {
                    params.append(JSONObject.toJSONString(point.getArgs()[i])).append(";");
                }
            }
        }

        Date endTime = new Date();
        long time = endTime.getTime() - startTime.getTime();
        log.error("{}.{}," +
                        "param={}," +
                        "exception={}," +
                        "[{}->{}]=[{}]", targetName, methodName,
                params,
                e.getMessage(),
                DateFormatUtils.format(startTime, TIME_PATTERN),
                DateFormatUtils.format(endTime, TIME_PATTERN),
                time);
    }

}
