/**
 * 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.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.ApiLog;
import com.freemud.application.sdk.api.log.ErrorLog;
import com.freemud.application.sdk.api.log.LogThreadLocal;
import com.freemud.application.sdk.api.ordercenter.util.LogUtil;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
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 java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Calendar;
import java.util.Objects;

@Component
public class AppLogUtil implements BeanFactoryAware {
    private static Logger logger = LoggerFactory.getLogger(AppLogUtil.class);

    @Autowired
    private LogUtil logUtil;

    private static BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        AppLogUtil.beanFactory = beanFactory;
    }

    private static volatile String appName = "NO_APP_NAME";

    public void debug(String message, Object... params) {
        if (logger.isDebugEnabled()) {
            logUtil.info(message, params);
        }
    }
    public void info(String message, Object... params) {
        logUtil.info(message,params);
    }
    public void error(String message, Exception e, String... params) {
        logUtil.error(message, e, params);
    }
    public void error(String message, Object... params) {
        ErrorLog.errorDev(message, params);
    }


    public void errorSe(String message, Exception e, String... params) {
        logUtil.errorSe(message, e, params);
    }

    private static LogUtil getLogUtil() {
        return beanFactory.getBean("logUtil", LogUtil.class);
    }

    public static void debugLog(String message, Object requestParams, Object responseParams) {
        if (requestParams != null) {
            message = message.concat(" requestParams:{}");
        }
        if (responseParams == null) {
            message = message.concat(" responseParams:{}");
        }
        getLogUtil().debug(message, requestParams, responseParams);
    }

    public static void debugLotsParams(String message, Object... params) {
        if (logger.isDebugEnabled()) {
            getLogUtil().debug(message, params);
        }
    }

    public static void infoLog(String message, Object requestParams, Object responseParams) {
        getLogUtil().info(message.concat(" requestParams:{} responseParams:{}"),requestParams,responseParams);
    }

    public static void infoLotsParams(String message, Object... params) {
        getLogUtil().info(message,params);
    }
    public static void infoLog(String trackingId, String message, Object requestParams, Object responseParams) {
        getLogUtil().info(message.concat(" requestParams:{} responseParams:{}"),requestParams,responseParams);
    }

    public static void errorLog(String message, Object requestParams, Object responseParams) {
        getLogUtil().error(message.concat(" requestParams:{} responseParams:{}"),null,requestParams,responseParams);
    }

    public static void errorLog(String message, String requestParams, String responseParams, Exception e) {
        getLogUtil().error(message.concat(" requestParams:{} responseParams:{}"),e,requestParams,responseParams);
    }

    public static void errorLog(String trackingId, String message, String requestParams, String responseParams, Exception e) {
        getLogUtil().error(message.concat(" requestParams:{} responseParams:{}"),e,requestParams,responseParams);
    }



    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();
            }
            getLogUtil().error("createAt:{} appName:{} trackingNo:{} thirdPartyName:{} timeConsumed:{} url:{} methodName:{} request:{} stackTrack:{}",null, 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) {
            getLogUtil().error("LogUtilError stackTrack",e,null);
        }
    }

    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) {
                AppLogUtil.errorLog("", serviceName, JSON.toJSONString(joinPoint.getArgs()), e);
            }
            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) {
            getLogUtil().error("LogUtilError stackTrack:{}", e);
            return null;
        }
    }


}
