package cn.superdesk.uniorder.interceptor.logger;

import cn.hutool.core.date.DateUtil;
import cn.superdesk.uniorder.common.json.AnJsonUtils;
import cn.superdesk.uniorder.common.rest.request.IpUtils;
import cn.superdesk.uniorder.common.security.JwtUtil;
import cn.superdesk.uniorder.common.security.dto.LoginSysUser;
import cn.superdesk.uniorder.dto.param.system.logger.SysUserOperationLogParam;
import cn.superdesk.uniorder.dubbo.consumer.sys.logger.SysUserOperationLogDubboConsumerService;
import cn.superdesk.uniorder.interceptor.logger.annotation.ControllerLoggerIgnore;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: 切面-日志
 * @Author: linjinyu
 * @CreateDate: 2020/12/10 下午5:04
 * @UpdateUser: linjinyu
 * @UpdateDate: 2020/12/10 下午5:04
 * @UpdateRemark: 修改内容
 * @Version: 1.0
 */
@Aspect
@Slf4j
public class ControllerLoggerAspect {

    @Autowired
    private SysUserOperationLogDubboConsumerService sysUserOperationLogDubboConsumerService;

    @Value("${spring.application.name}")
    private String MODEL_SPRING_APPLICATION_NAME;


    /**
     * 拦截所有 @RestController TODO 早前可能还有不是用这个的
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@within(org.springframework.web.bind.annotation.RestController)")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {

        // 操作时间
        Date startTime        = DateUtil.date();
        long startMilliSecond = startTime.getTime();

        // 1.获取切入点所在目标对象
        Object targetObj = joinPoint.getTarget();
//		System.out.println(targetObj.getClass().getName());
        // 2.获取切入点方法的名字
        String methodName = joinPoint.getSignature().getName();
//		System.out.println("切入方法名字:" + methodName);
        // 3.获取方法上的注解
        Signature       signature       = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method          method          = methodSignature.getMethod();
        Annotation[]    annotations     = null;
        // 4.获取方法的参数 请求参数 TODO 目录 系统 不统一 有的可能会报错 不用理
        Object[] args    = joinPoint.getArgs();
        Object   proceed = null;

        Integer isLogRequestParam         = 0;
        Integer hasControllerLoggerIgnore = 0;
        if (method != null) {

            // 全部 注解
            annotations = method.getAnnotations();

            // 特殊处理
            ControllerLoggerIgnore controllerLoggerIgnore = method.getAnnotation(ControllerLoggerIgnore.class);
            if (null != controllerLoggerIgnore) {
                hasControllerLoggerIgnore = 1;
            }

        }


        // 如果有这个标记,必定不记录
        if (hasControllerLoggerIgnore.equals(1)) {

            log.info("\n开始捕获用户操作日志 aroundMethod :" +
                            "\n1.获取切入点所在目标对象-{} " +
                            "\n2.获取切入点方法的名字  -{} " +
                            "\n3.获取方法上的注解     -{} " +
                            "\n4.获取方法的参数       -{}" +
                            "\n 排除 ",
                    targetObj.getClass().getName(), methodName, annotations, args
            );

            proceed = joinPoint.proceed();

            return proceed;
        }

//		Arrays.asList(args).forEach(a -> {
//			log.info("{}", a);
//		});
//		Arrays.asList(annotations).forEach(annotation -> {
//			log.info("{}", annotation);
//		});

//		log.info("\n开始捕获用户操作日志 aroundMethod :" +
//						"\n1.获取切入点所在目标对象-{} " +
//						"\n2.获取切入点方法的名字  -{} " +
//						"\n3.获取方法上的注解     -{} " +
//						"\n4.获取方法的参数       -{}" +
//						"\n",
//				targetObj.getClass().getName(), methodName, annotations, args
//		);

        SysUserOperationLogParam userOperation = new SysUserOperationLogParam();


        try {
            // 获取Request对象
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

            LoginSysUser loginSysUser = JwtUtil.getLoginSysUser(request);

            if (null != loginSysUser) {
                // 操作人账号
                userOperation.setUserName(loginSysUser.getSysUserName());
                // 操作人
                userOperation.setNickName(loginSysUser.getFullName());
            } else {
                // 操作人
                userOperation.setUserName("anonymous");
                // 操作人账号
                userOperation.setNickName("anonymous");
            }

            // 操作名称
            userOperation.setMethodName(joinPoint.getSignature().getName());
            // 操作时间
            userOperation.setOperationTime(startTime);
            // 请求开始时间
            userOperation.setRequestStartTime(startTime);
            // 请求IP
            userOperation.setRequestIp(IpUtils.getIpAddr(request));
            // URL
            userOperation.setRequestUrl(request.getRequestURI());
            // 模块
            userOperation.setModel(MODEL_SPRING_APPLICATION_NAME);


            try {

                if (null != args) {
                    if (java.io.Serializable.class.isInstance(args)) {
                        isLogRequestParam = 1;
                    }
                }

                // 过滤不能序列化的
                if (isLogRequestParam.equals(1)) {
                    String param = AnJsonUtils.toJson(args);
                    userOperation.setRequestParam(param);
                }

            } catch (RuntimeException re) {

            } catch (Exception e) {
                log.error("截取参数异常", e);
            }

            // TODO 目前这里的问题 excel 下载有问题
            proceed = joinPoint.proceed();
            userOperation.setResultStatus("success");

            return proceed;

        } catch (Exception e) {

            // 堆栈错误信息
            String stackTrace = Arrays.toString(e.getStackTrace());
            // 错误信息
            String                            message  = e.getMessage();
            ConcurrentHashMap<String, String> errorMsg = new ConcurrentHashMap<>();
            errorMsg.put("stackTrace", stackTrace);
            errorMsg.put("message", e.getClass().getName() + ": " + message);

            userOperation.setErrorInfo(AnJsonUtils.toJson(errorMsg));
            userOperation.setResultStatus("error");

            throw e;

        } finally {

            try {
                if (null != proceed) {
                    // 返回结果
                    // 不记录结果 mark by linjinyu at 20220330
//					userOperation.setResponseResult(AnJsonUtils.toJson(proceed));
                }

                // 请求结束时间
                Date endDate        = DateUtil.date();
                long endMilliSecond = endDate.getTime();

                userOperation.setRequestEndTime(endDate);
                userOperation.setResponseDate(endMilliSecond - startMilliSecond);

            } catch (Exception e) {
                log.error("获取返回结果异常{}", e);
            }

            // 异步将操作日志保存
            CompletableFuture.runAsync(() -> {
                try {
                    // 保存日志,具体方法自定义
                    sysUserOperationLogDubboConsumerService.addSysUserOperationLog(userOperation);
                } catch (Exception e2) {
                    log.error("用户操作日记记录异常:{}", e2);
                }
            });
        }
    }

    /**
     * 分发
     */
    private void dispatchUnifiedOrderLog() {
        log.info("切面-日志-分发");
    }
}
