package cn.datax.common.log.aspectj;

import java.lang.reflect.Method;

import cn.datax.common.log.annotation.LogAop;
import cn.datax.common.log.async.AsyncTask;
import cn.datax.common.utils.RequestHolder;
import cn.datax.common.utils.SecurityUtil;
import cn.datax.service.system.api.dto.LogDto;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;

@Slf4j
@Aspect
public class LogAspect {

    @Autowired
    private AsyncTask asyncTask;

    // 配置织入点
    @Pointcut("@annotation(cn.datax.common.log.annotation.LogAop)")
    public void logPointCut() {}

    /**
     * 通知方法会将目标方法封装起来
     *
     * @param joinPoint 切点
     */
    @Around(value = "logPointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        LogDto logDto = getLog();
        logDto.setTime(String.valueOf(endTime - startTime));
        handleLog(joinPoint, logDto);
        return result;
    }

    /**
     * 通知方法会在目标方法抛出异常后执行
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        LogDto logDto = getLog();
        logDto.setExCode(e.getClass().getSimpleName()).setExMsg(e.getMessage());
        handleLog(joinPoint, logDto);
    }

    private LogDto getLog(){
        LogDto log = new LogDto();
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        log.setUserId(SecurityUtil.getUserId());
        log.setUserName(SecurityUtil.getUserName());
        log.setRemoteAddr(ServletUtil.getClientIP(request));
        log.setRequestUri(URLUtil.getPath(request.getRequestURI()));
        UserAgent ua = UserAgentUtil.parse(request.getHeader("User-Agent"));
        log.setBrowser(ua.getBrowser().toString());
        log.setOs(ua.getOs().toString());
        log.setParams(HttpUtil.toParams(request.getParameterMap()));
        return log;
    }

    protected void handleLog(final JoinPoint joinPoint, LogDto logDto) {
        // 获得注解
        LogAop logAop = getAnnotationLog(joinPoint);
        if(null == logAop) {
            return;
        }
        // 设置方法名称
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        logDto.setModule(logAop.module()).setTitle(logAop.value())
                .setClassName(className).setMethodName(methodName);
        // 异步保存数据库
        asyncTask.doTask(logDto);
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private LogAop getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if(method != null) {
            return method.getAnnotation(LogAop.class);
        }
        return null;
    }
}

