package com.jay.monitor.data.client.interceptor;

import com.jay.monitor.data.client.MonitorSendProducer;
import com.jay.monitor.data.client.api.DefaultIdSupplier;
import com.jay.monitor.data.client.api.DefaultIndexData;
import com.jay.monitor.data.client.api.MonitorIndexCallback;
import com.jay.monitor.data.client.api.MonitorIndexCallbackAdaptor;
import com.jay.monitor.data.core.model.serializable.URLDataDTO;
import com.jay.monitor.data.core.utils.JsonHelper;
import com.jay.monitor.data.core.utils.ObjectHelper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;

/**
 * 负责拦截rabbitMq相关的参数
 *
 * @author liukx
 * @date 2022/7/1 16:49
 */
@Aspect
public class JayRestAspect {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired(required = false)
    private MonitorIndexCallback monitorIndexCallback = new MonitorIndexCallbackAdaptor();

    @Autowired(required = false)
    private Supplier<String> supplier = new DefaultIdSupplier();

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)|| @annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping) || @annotation( org.springframework.web.bind.annotation.DeleteMapping)")
    public void invokeRest() {

    }

    @Around("invokeRest()")
    public Object consumerAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object value = null;
        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            value = proceedingJoinPoint.proceed();
            stopWatch.stop();
            invokeSuccessMetric(proceedingJoinPoint, stopWatch, value);
        } catch (Throwable e) {
            invokeErrorMetric(proceedingJoinPoint, e);
            throw e;
        }
        return value;
    }

    private void invokeErrorMetric(ProceedingJoinPoint invocation, Throwable e) {
        URLDataDTO urlDataDTO = new URLDataDTO();
        Map<String, Object> jsonContent = new LinkedHashMap<>();
        try {
            urlDataDTO.setStatus(-1);
            urlDataDTO.setErrorMessage(e.getMessage());
            urlDataDTO.setErrorStackTrace(ObjectHelper.errorToString(e));
            urlDataDTO.setLogId(supplier.get());
            urlDataDTO.setRequestTime(-1L);
            urlDataDTO.setCreateDate(new Date());
            Object[] args = invocation.getArgs();
            jsonContent.put("request", args);
            ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            urlDataDTO.setUrl(request.getRequestURI());
            urlDataDTO.setClientIp(request.getRemoteHost());
            callback(urlDataDTO, request);
            String fullName = getMethodFullName(invocation);
            urlDataDTO.setGroupKeyName(fullName);
        } catch (Exception ex) {
            logger.warn("url 构建数据异常:{}", ex.getMessage());
            jsonContent.put("parseError", ex.getMessage());
            jsonContent.put("parseErrorStack", ObjectHelper.errorToString(e));
        } finally {
            urlDataDTO.setJsonContent(JsonHelper.toJsonString(jsonContent));
            MonitorSendProducer.sendMsg(urlDataDTO);
        }
    }

    private void callback(URLDataDTO urlDataDTO, HttpServletRequest request) {
        DefaultIndexData defaultIndexData = monitorIndexCallback.urlCallback(request);
        // 建议方法
        urlDataDTO.setGroupName(defaultIndexData.getGroupName());
        // 建议token
        urlDataDTO.setGroupKeyName(defaultIndexData.getGroupKeyName());
        // 建议userId
        urlDataDTO.setDataId(defaultIndexData.getDataId());

        String urlUserId = monitorIndexCallback.getUrlUserId(request);
        urlDataDTO.setUserId(urlUserId);
    }

    private String getMethodFullName(ProceedingJoinPoint invocation) {
        MethodSignature methodSignature = (MethodSignature)invocation.getSignature();
        Method signature = methodSignature.getMethod();
        String methodName = signature.getName();
        String className = invocation.getThis().getClass().getName();
        return className + "#" + methodName;
    }

    private void invokeSuccessMetric(ProceedingJoinPoint invocation, StopWatch stopWatch, Object value) {
        URLDataDTO urlDataDTO = new URLDataDTO();
        Map<String, Object> jsonContent = new LinkedHashMap<>();
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            Object[] args = invocation.getArgs();
            urlDataDTO.setStatus(1);
            urlDataDTO.setRequestTime(stopWatch.getTotalTimeMillis());
            urlDataDTO.setLogId(supplier.get());
            urlDataDTO.setUrl(request.getRequestURI());
            urlDataDTO.setClientIp(request.getRemoteHost());
            urlDataDTO.setCreateDate(new Date());
            jsonContent.put("request", args);
            jsonContent.put("response", value);
            String methodFullName = getMethodFullName(invocation);
            urlDataDTO.setGroupKeyName(methodFullName);
            callback(urlDataDTO, request);
            // 这里把感觉、有可能会出现解析问题的放在下面执行，一旦出现问题，也能多一些数据排查线索。
            urlDataDTO.setJsonContent(JsonHelper.toJsonString(jsonContent));
        } catch (Exception e) {
            logger.warn("url 构建数据异常:{}", e.getMessage());
            urlDataDTO.setErrorMessage(e.getMessage());
            urlDataDTO.setErrorStackTrace(ObjectHelper.errorToString(e));
        } finally {
            MonitorSendProducer.sendMsg(urlDataDTO);
        }
    }

}