package com.njcn.gather.system.config.advice;


import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.gather.system.log.service.ISysLogAuditService;
import com.njcn.web.utils.HttpServletUtil;
import com.njcn.web.utils.ReflectCommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.annotation.Nonnull;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;


/**
 * @author caozehui
 * @data 2024-12-2
 */
@Slf4j
@ControllerAdvice
public class LogAdvice implements ResponseBodyAdvice<Object> {

    @Autowired
    private ISysLogAuditService logService;

    private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            4, 8, 30, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            // 队列满时由主线程执行
            new ThreadPoolExecutor.CallerRunsPolicy()
    );


    /**
     * 无需审计记录的操作，比如根据客户端获取客户端信息
     */

    private final static List<String> UN_LOG_INFO = Collections.singletonList("未知业务");


    /**
     * controller返回的响应体包含的状态码，而非全局异常捕获器处抛出来的状态码
     */
    private final static List<String> FILTER_CODE = Arrays.asList(CommonResponseEnum.SUCCESS.getCode(), CommonResponseEnum.FAIL.getCode(), CommonResponseEnum.NO_DATA.getCode());


    /**
     * 判断下结果，是不是成功
     *
     * @param returnType    返回类型，包含大量信息，controller、method、result等等
     * @param converterType 消息转换器类型，目前配置的是Jackson
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    /**
     * 拦截所有请求成功的操作进行日志入库处理
     * 需要 用户标识、事件描述、事件结果、操作IP、事件类型、事件严重度、操作时间、操作类型
     *
     * @param body                  .
     * @param returnType            .
     * @param selectedContentType   .
     * @param selectedConverterType .
     * @param request               .
     * @param response              .
     * @return .
     */
    @Override
    public Object beforeBodyWrite(Object body, @Nonnull MethodParameter returnType, @Nonnull MediaType selectedContentType, @Nonnull Class selectedConverterType, @Nonnull ServerHttpRequest request, @Nonnull ServerHttpResponse response) {
        if (body instanceof HttpResult) {
            HttpResult<?> httpResult = (HttpResult<?>) body;
            if (FILTER_CODE.contains(httpResult.getCode())) {
                // 传递上下文
                HttpServletRequest httpServletRequest = HttpServletUtil.getRequest();
                String methodDescribe = ReflectCommonUtil.getMethodDescribeByMethod(returnType.getMethod());
                if (!UN_LOG_INFO.contains(methodDescribe)) {
                    Future<?> future = executor.submit(() -> {
                        HttpServletUtil.setRequest(httpServletRequest);
                        logService.recodeAdviceLog(returnType, httpResult, methodDescribe);
                    });
                    try {
                        // 抛出 ExecutionException
                        future.get();
                    } catch (ExecutionException | InterruptedException e) {
                        log.error("保存审计日志异常，异常为："+e.getMessage());
                    }
                }
            }
        }
        return body;
    }
}
