package com.foreverwin.mesnac.common.util;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.foreverwin.mesnac.common.model.WebServiceLog;
import com.foreverwin.mesnac.common.service.WebServiceLogService;
import com.foreverwin.modular.core.util.SpringUtil;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import javax.jws.WebParam;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author Ervin Chen
 * @date 2021/1/11 13:32
 */
public class WebServiceUtil {
    private static final Logger logger = LoggerFactory.getLogger(WebServiceUtil.class);

    public static  <T> T getProxy(Object target, Class<T> intf){
        ObjectMapper objectMapper = SpringUtil.getBean(ObjectMapper.class);
        WebServiceLogService webServiceLogService = SpringUtil.getBean(WebServiceLogService.class);
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addInterface(intf);
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setAdvice(new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                Object[] args = invocation.getArguments();
                Method method = invocation.getMethod();
                Annotation[][] annotations = method.getParameterAnnotations();
                Parameter[] parameters =  method.getParameters();
                List<String> paramList = new ArrayList<>();
                LocalDateTime startDate = LocalDateTime.now();

                Object result = null;
                try{
                    result = invocation.proceed();
                }finally {
                    WebServiceLog webServiceLog = new WebServiceLog();
                    for (int i = 0; i < args.length; i++) {
                        String paramName = null;
                        String paramValue = null;
                        Annotation[] paramAnnotation = annotations[i];
                        for(Annotation annotation : paramAnnotation){
                            if(annotation instanceof WebParam){
                                paramName = ((WebParam) annotation).name();
                            }
                        }
                        if(StrUtil.isBlank(paramName)){
                            paramName = parameters[i].getName();
                        }
                        try{
                            paramValue = objectMapper.writeValueAsString(args[i]);
                        }catch (Exception e){
                            paramValue = args[i].getClass().getCanonicalName() + " parse error";
                        }
                        paramList.add(paramName + " ==> " + paramValue);

                        Class c = WebServiceLog.class;
                        Field paramNameField = c.getDeclaredField("paramName" + (i + 1));
                        Field paramValueField = c.getDeclaredField("paramValue" + (i + 1));
                        paramNameField.setAccessible(true);
                        paramValueField.setAccessible(true);
                        if(paramNameField != null){
                            paramNameField.set(webServiceLog, paramName);
                        }
                        if(paramValueField != null){
                            paramValueField.set(webServiceLog, paramValue != null && paramValue.length() > 5000 ? paramValue.substring(0,5000) : paramValue);
                        }
                    }
                    logger.info("***********************************************************************************>>");
                    logger.info("ClassMethod ==> " + method.getDeclaringClass().getName() + "." + method.getName());
                    logger.info("ParamList   ==> " + objectMapper.writeValueAsString(paramList));
                    logger.info("Result      ==> " + objectMapper.writeValueAsString(result));
                    logger.info("<<***********************************************************************************");

                    LocalDateTime endDate = LocalDateTime.now();
                    Duration duration = Duration.between(startDate, endDate);
                    long runTime = duration.toMillis();
                    webServiceLog.setHandle(UUID.randomUUID().toString());
                    webServiceLog.setClazz(method.getDeclaringClass().getName());
                    webServiceLog.setMethod(method.getName());
                    webServiceLog.setResult(objectMapper.writeValueAsString(result));
                    webServiceLog.setStartDateTime(startDate);
                    webServiceLog.setEndDateTime(endDate);
                    webServiceLog.setRunTime(runTime);
                    webServiceLogService.save(webServiceLog);
                }

                return result;
            }
        });
        proxyFactory.addAdvisors(advisor);

        T proxy = (T) proxyFactory.getProxy();
        return proxy;
    }
}
