/**
 * All rights Reserved, Designed By www.freemud.cn
 *
 * @Title: LogUtil
 * @Package cn.freemud.utils
 * @Description:
 * @author: liming.guo
 * @date: 2018/7/3 12:01
 * @version V1.0
 * @Copyright: 2018 www.freemud.cn Inc. All rights reserved.
 * 注意：本内容仅限于上海非码科技内部传阅，禁止外泄以及用于其他的商业目
 */
package cn.freemud.utils;

import cn.freemud.base.ApplicationContextWare;
import cn.freemud.base.util.DateUtil;
import cn.freemud.base.util.IpUtil;
import cn.freemud.base.util.JsonUtil;
import cn.freemud.base.util.RequestUtils;
import cn.freemud.entities.vo.ThirdPartLogVo;
import cn.freemud.service.thirdparty.CouponOnlineClient;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.freemud.application.sdk.api.log.LogThreadLocal;
import com.google.common.base.Throwables;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;

public class LogUtil {

    private static Logger logger = LoggerFactory.getLogger(LogUtil.class);

    public LogUtil() {
    }

    public static void debug(String message, Object requestParams, Object responseParams) {
        logger.debug("createAt:{}, trackingNo:{} ,message : {} ,requestParams :{}, responseParams :{}",
                DateUtil.convert2String(Calendar.getInstance().getTime(), "yyyy-MM-dd HH:mm:ss"),
                LogThreadLocal.getTrackingNo(),
                message,
                requestParams instanceof String ? requestParams : JSONObject.toJSONString(requestParams),
                responseParams instanceof String ? responseParams : JSONObject.toJSONString(responseParams));
    }

    public static void info(String message, Object requestParams, Object responseParams) {
        logger.info("createAt:{}, trackingNo:{} ,message : {} ,requestParams :{}, responseParams :{} , requestTime :{}",
                DateUtil.convert2String(Calendar.getInstance().getTime(), "yyyy-MM-dd HH:mm:ss"),
                LogThreadLocal.getTrackingNo(),
                message,
                requestParams,
                responseParams,
                DateUtil.convert2String(new Date(), "yyyy-MM-dd HH:mm:ss"));
    }

    public static void info(String trackingId, String message, Object requestParams, Object responseParams) {
        logger.info("createAt:{}, trackingNo:{} ,message : {} ,requestParams :{}, responseParams :{} , requestTime :{}",
                DateUtil.convert2String(Calendar.getInstance().getTime(), "yyyy-MM-dd HH:mm:ss"),
                trackingId,
                message,
                requestParams,
                responseParams,
                DateUtil.convert2String(new Date(), "yyyy-MM-dd HH:mm:ss"));
    }

    public static void error(String message, Object requestParams, Object responseParams) {
        logger.error("createAt:{}, message : {} ,requestParams :{}, responseParams :{} , requestTime :{}",
                DateUtil.convert2String(Calendar.getInstance().getTime(), "yyyy-MM-dd HH:mm:ss"),
                message, requestParams, responseParams, DateUtil.convert2String(new Date(), "yyyy-MM-dd HH:mm:ss"));
    }

    public static void error(String message, String requestParams, String responseParams, Exception e) {
        logger.error("createAt:{}, trackingNo:{},message: {} ,requestParams :{}, responseParams :{} ,requestTime :{},e:{}",
                DateUtil.convert2String(Calendar.getInstance().getTime(), "yyyy-MM-dd HH:mm:ss"),
                LogThreadLocal.getTrackingNo(),
                message,
                requestParams,
                responseParams,
                DateUtil.convert2String(new Date(), "yyyy-MM-dd HH:mm:ss"), e);
    }

    public static void error(String trackingId, String message, String requestParams, String responseParams, Exception e) {
        logger.error("createAt:{}, trackingNo:{},message: {} ,requestParams :{}, responseParams :{} ,requestTime :{},e:{}",
                DateUtil.convert2String(Calendar.getInstance().getTime(), "yyyy-MM-dd HH:mm:ss"),
                trackingId,
                message,
                requestParams,
                responseParams,
                DateUtil.convert2String(new Date(), "yyyy-MM-dd HH:mm:ss"), JSONObject.toJSONString(Throwables.getStackTraceAsString(e)));
    }

    private static volatile String appName = null;

    public static void localInfo(HttpServletRequest request, long startTime, long endTime, Object requestData, Object responseData) {
        try {
            Object responseDataConvert = null;
            if (responseData instanceof String) {
                responseDataConvert = responseData;
            } else if (responseData != null) {
                responseDataConvert = JSON.toJSONString(responseData);
            }
            if (appName == null) {
                appName = ApplicationContextWare.getAppName();
                if (appName == null) {
                    appName = "NO_APP_NAME";
                }
            }
            String tracking = LogThreadLocal.getTrackingNo();
            if (tracking == null) {
                tracking = "NO_TRACKING_NO";
            }
            Object requestDataConvert = null;
            if (requestData instanceof String) {
                requestDataConvert = requestData;
            } else if (requestData != null) {
                requestDataConvert = JSON.toJSONString(requestData);
            }
            logger.info("createAt:{} appName:{} trackingNo:{} startTime:{} endTime:{} timeConsumed:{} url:{} userAgent:{} " +
                    "requestIp:{} serverIp:{} request:{} response:{}", new Object[]{DateUtil.convert2String(Calendar.getInstance().getTime()
                    , "yyyy-MM-dd HH:mm:ss"), appName, tracking, startTime, endTime, endTime - startTime
                    , RequestUtils.getUrl(request), RequestUtils.getUserAgent(request), RequestUtils.getRequestIp(request)
                    , IpUtil.getLocalIP(), requestDataConvert, responseDataConvert});
        } catch (Exception e) {
            logger.error("LogUtilError stackTrack:{}", e);
        }
    }

    public static void localError(HttpServletRequest request, long startTime, long endTime, Object requestBody, Object errorObject) {
        try {
            if (appName == null) {
                appName = ApplicationContextWare.getAppName();
                if (appName == null) {
                    appName = "NO_APP_NAME";
                }
            }
            String tracking = LogThreadLocal.getTrackingNo();
            if (tracking == null) {
                tracking = "NO_TRACKING_NO";
            }
            Object requestDataConvert = null;
            if (requestBody instanceof String) {
                requestDataConvert = requestBody;
            } else if (requestBody != null) {
                requestDataConvert = JSON.toJSONString(requestBody);
            }
            logger.error("createAt:{} appName:{} trackingNo:{} timeConsumed:{} url:{} userAgent:{} " +
                    "requestIp:{} serverIp:{} request:{} stackTrack:{}", new Object[]{DateUtil.convert2String(Calendar.getInstance().getTime()
                    , "yyyy-MM-dd HH:mm:ss"), appName, tracking, endTime - startTime
                    , RequestUtils.getUrl(request), RequestUtils.getUserAgent(request), RequestUtils.getRequestIp(request)
                    , IpUtil.getLocalIP(), requestDataConvert, JSONObject.toJSONString(errorObject)});
        } catch (Exception e) {
            logger.error("LogUtilError stackTrack:{}", e);
        }
    }

    public static void thirdPartLog(long startTime, long endTime, ThirdPartLogVo thirdPartLogVo, Object responseData) {
        try {
            Object responseDataConvert = null;
            if (responseData instanceof String) {
                responseDataConvert = responseData;
            } else {
                responseDataConvert = JSON.toJSONString(responseData);
            }
            if (appName == null) {
                appName = ApplicationContextWare.getAppName();
                if (appName == null) {
                    appName = "NO_APP_NAME";
                }
            }
            String tracking = LogThreadLocal.getTrackingNo();
            if (tracking == null) {
                tracking = "NO_TRACKING_NO";
            }
            String thirdPartyName = null;
            String url = null;
            String methodName = null;
            String requestData = null;
            if(thirdPartLogVo != null) {
                thirdPartyName = thirdPartLogVo.getThirdPartName();
                url = thirdPartLogVo.getUri();
                methodName = thirdPartLogVo.getMethodName();
                requestData = thirdPartLogVo.getRequestBody();
            }
            logger.info("createAt:{} appName:{} trackingNo:{} startTime:{} endTime:{} timeConsumed:{} thirdPartyName:{} url:{} methodName:{} " +
                    "request:{} response:{} ", new Object[]{DateUtil.convert2String(Calendar.getInstance().getTime(), "yyyy-MM-dd HH:mm:ss")
                    , appName, tracking, startTime, endTime, endTime - startTime, thirdPartyName, url, methodName, JsonUtil.toJSONString(requestData), responseDataConvert});
        } catch (Exception e) {
            logger.error("LogUtilError stackTrack:{}", e);
        }
    }

    public static void thirdPartError(long startTime, long endTime, ThirdPartLogVo thirdPartLogVo, Object errorObject) {
        try {
            if (appName == null) {
                appName = ApplicationContextWare.getAppName();
                if (appName == null) {
                    appName = "NO_APP_NAME";
                }
            }
            String tracking = LogThreadLocal.getTrackingNo();
            if (tracking == null) {
                tracking = "NO_TRACKING_NO";
            }
            Object requestDataConvert = null;
            String thirdPartyName = null;
            String url = null;
            String methodName = null;
            if(thirdPartLogVo != null) {
                requestDataConvert = thirdPartLogVo.getRequestBody();
                thirdPartyName = thirdPartLogVo.getThirdPartName();
                url = thirdPartLogVo.getUri();
                methodName = thirdPartLogVo.getMethodName();
            }
            logger.error("createAt:{} appName:{} trackingNo:{} thirdPartyName:{} timeConsumed:{} url:{} methodName:{} request:{} stackTrack:{}", new Object[]{DateUtil.convert2String(Calendar.getInstance().getTime()
                    , "yyyy-MM-dd HH:mm:ss"), appName, tracking, thirdPartyName, endTime - startTime, url, methodName
                    , requestDataConvert, JSONObject.toJSONString(errorObject)});
        } catch (Exception e) {
            logger.error("LogUtilError stackTrack:{}", e);
        }
    }

    public static ThirdPartLogVo createThirdPartLogVo(ProceedingJoinPoint joinPoint) {
        try {
            Object[] args = joinPoint.getArgs();
            Object[] arguments  = new Object[args.length];
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
                    //ServletRequest不能序列化，从入参里排除，否则报异常：java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                    //ServletResponse不能序列化 从入参里排除，否则报异常：java.lang.IllegalStateException: getOutputStream() has already been called for this response
                    continue;
                }
                arguments[i] = args[i];
            }
            String requestBody = "";
            if (arguments != null) {
                try {
                    requestBody = JSON.toJSONString(arguments);
                } catch (Exception e) {
                    requestBody = arguments.toString();
                }
            }
            Method method = null;
            Class targetClass = joinPoint.getSignature().getDeclaringType();
            FeignClient feignClient = AnnotationUtils.findAnnotation(targetClass, FeignClient.class);
            RequestMapping clazzRequestMapping = AnnotationUtils.findAnnotation(targetClass, RequestMapping.class);
            String uriPre = "";
            if (clazzRequestMapping != null && clazzRequestMapping.path() != null && clazzRequestMapping.path().length > 0) {
                uriPre = clazzRequestMapping.path()[0];
            }
            String serviceName = feignClient.name();
            String methodName = null;
            String uri = null;
            try {
                Class<?>[] argTypes = new Class[joinPoint.getArgs().length];
                for (int i = 0; i < args.length; i++) {
                    if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
                        //ServletRequest不能序列化，从入参里排除，否则报异常：java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                        //ServletResponse不能序列化 从入参里排除，否则报异常：java.lang.IllegalStateException: getOutputStream() has already been called for this response
                        continue;
                    }
                    argTypes[i] = args[i].getClass();
                }
                // 剔除特殊类
                if(!targetClass.equals(CouponOnlineClient.class)) {
                    method = targetClass.getMethod(joinPoint.getSignature().getName(), argTypes);
                }
            } catch (NoSuchMethodException e) {
                LogUtil.error("", serviceName, JSON.toJSONString(joinPoint.getArgs()), e);
                e.printStackTrace();
            }
            if(method != null) {
                methodName = method.getName();
            }
            if (StringUtils.isEmpty(uri) && method != null) {
                PostMapping postMapping = AnnotationUtils.getAnnotation(method, PostMapping.class);
                uri = uriPre + (!Objects.equals(postMapping, null) && !Objects.equals(postMapping.path(), null) && postMapping.path().length > 0 ? postMapping.path()[0] : "");
                if (StringUtils.isEmpty(uri)) {
                    GetMapping getMapping = AnnotationUtils.getAnnotation(method, GetMapping.class);
                    uri = uriPre + (!Objects.equals(getMapping, null) && !Objects.equals(getMapping.path(), null) && getMapping.path().length > 0 ? getMapping.path()[0] : "");
                }
                if (StringUtils.isEmpty(uri)) {
                    RequestMapping methodRequestMapping = AnnotationUtils.getAnnotation(method, RequestMapping.class);
                    uri = uriPre + (!Objects.equals(methodRequestMapping, null) && !Objects.equals(methodRequestMapping.path(), null) && methodRequestMapping.path().length > 0 ? methodRequestMapping.path()[0] : "");
                }
            }
            ThirdPartLogVo thirdPartLogVo = new ThirdPartLogVo();
            thirdPartLogVo.setThirdPartName(serviceName);
            thirdPartLogVo.setUri(uri);
            thirdPartLogVo.setMethodName(methodName);
            thirdPartLogVo.setRequestBody(requestBody);
            return thirdPartLogVo;
        } catch (Exception e) {
            logger.error("LogUtilError stackTrack:{}", e);
            return null;
        }
    }

    public static String createLocalRequestBody(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Object[] arguments  = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
                //ServletRequest不能序列化，从入参里排除，否则报异常：java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                //ServletResponse不能序列化 从入参里排除，否则报异常：java.lang.IllegalStateException: getOutputStream() has already been called for this response
                continue;
            }
            arguments[i] = args[i];
        }
        String paramter = "";
        if (arguments != null) {
            try {
                paramter = JSON.toJSONString(arguments);
            } catch (Exception e) {
                paramter = arguments.toString();
            }
        }
        return paramter;
    }

}
