package com.ssyf.plugs.logger;


import cn.hutool.core.date.DateUtil;

import cn.hutool.core.util.StrUtil;
import com.ssyf.common.security.domain.ClientInfo;
import com.ssyf.common.security.helper.ClientContextHelper;
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.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;


/**
 * 操作日志拦截器
 * @author huwei
 * @date 2022/10/30 13:43
 */
@Slf4j
@Aspect
@Component
public class LogAop {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 注解拦截处理
     */
    @Around("@annotation(com.ssyf.plugs.logger.Log)")
    public Object handleAnnotationAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        return advice(joinPoint);
    }


    private Object advice(ProceedingJoinPoint joinPoint) throws Throwable {
        long st = System.currentTimeMillis();
        //设置一个LogInfo对象，在本线程上下文可用
        LogInfoHolder.set(new LogInfo());
        //执行
        Object result;
        try{
            result = joinPoint.proceed();
        }catch (Exception e){
            recordLog(st , joinPoint ,e);
            throw e;
        }
        //记录信息
        recordLog(st , joinPoint ,null);
        return result;
    }

    /**
     * 记录日志
     * @param st
     * @param joinPoint
     */
    private void recordLog(long st , ProceedingJoinPoint joinPoint ,Exception ex){
        //手机日志记录信息，下列信息必须在该方法中获取，
        long executionTimeMills = System.currentTimeMillis() - st;
        String endTime = DateUtil.formatDateTime(new Date());
        String startTime = DateUtil.formatDateTime(new Date(st));

        //获取上下文中的logInfo
        LogInfo logInfo = LogInfoHolder.get();
        //清理
        LogInfoHolder.clear();

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log logA = method.getAnnotation(Log.class);
        //标题信息
        logInfo.setTitle(StrUtil.isBlank(logA.title()) ? logA.value() : logA.title());

        //时间信息
        logInfo.setLogEndTime(endTime).setLogStartTime(startTime).setExecutionTimeMills(executionTimeMills);

        //方法，参数信息
        logInfo.setMethod(LogHelper.parseIdentifierAndGet(joinPoint));
        logInfo.setParamJson(LogHelper.parseParametersAndGetJson(joinPoint));


        //用户信息
        if(StrUtil.isBlank(logInfo.getUsername()) && ClientContextHelper.isLogin()){
            ClientInfo clientInfo = ClientContextHelper.getClientInfo();
            logInfo.setUsername(clientInfo.getNickname()).setUserId(clientInfo.getId().toString());
        }
        //请求信息
        HttpServletRequest request = ClientContextHelper.getContext().getRequest();
        logInfo.setUri(request.getRequestURI());
        logInfo.setIp(ClientContextHelper.getClientIp());

        //错误信息
        if(logA.error() && ex != null){
            logInfo.setErrorMessage(ex.getMessage());
            logInfo.setErrorStackTrace(getStackTrace(ex));
        }

        //日志事件；业务分离，不影响业务响应
        this.applicationEventPublisher.publishEvent(new LogEvent(this , logInfo));
    }

    /**
     * 获取错误堆栈
     * @param throwable
     * @return
     */
    private String getStackTrace(Throwable throwable) {
        StringBuilder stackTrace = new StringBuilder();
        for (StackTraceElement element : throwable.getStackTrace()) {
            stackTrace.append(element.toString()).append("\n");
        }
        return stackTrace.toString();
    }
}
