package cn.gtmap.estateplat.config.core.log;

import cn.gtmap.estateplat.core.support.mybatis.mapper.EntityMapper;
import cn.gtmap.estateplat.model.server.core.BdcXtLog;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.gtis.common.util.UUIDGenerator;
import com.gtis.plat.service.SysUserService;
import com.gtis.plat.vo.PfUserVo;
import com.gtis.plat.vo.UserInfo;
import com.gtis.web.SessionUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author <a href="mailto:dingweiwei@gtmap.cn">dingweiwei</a>
 * @Time 2020/11/16
 * @description aop日志处理
 */
@Aspect
@Component
public class BdcXtLogAspect {

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

    //设置核心池大小
    int corePoolSize = 20;
    //设置线程池最大能接受多少线程
    int maximumPoolSize = 200;
    //当前线程数大于corePoolSize、小于maximumPoolSize时，超出corePoolSize的线程数的生命周期
    long keepActiveTime = 200;
    //设置时间单位，秒
    TimeUnit timeUnit = TimeUnit.SECONDS;
    //设置线程池缓存队列的排队策略为FIFO，并且指定缓存队列大小为300
    BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(300);
    //创建ThreadPoolExecutor线程池对象，并初始化该对象的各种参数
    ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepActiveTime, timeUnit, workQueue);

    @Autowired
    EntityMapper entityMapper;
    @Autowired
    private SysUserService sysUserService;

    @Pointcut("@annotation(cn.gtmap.estateplat.config.core.log.BdcLog)")
    public void doLog() {
    }

    @Around("doLog()")
    public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = getMethod(joinPoint);
        boolean present = method.isAnnotationPresent(RequestMapping.class);
        String[] value = new String[3];
        if (present) {
            //得到requestMapping注释
            RequestMapping annotation = method.getAnnotation(RequestMapping.class);
            value = annotation.value();
        }
        String requestMappingValue = "";
        for (int i = 0; i < value.length; i++) {
            requestMappingValue = requestMappingValue + value[i];
        }
        BdcLog publicMethodLog = method.getAnnotation(BdcLog.class);
        Object resultObj = joinPoint.proceed();
        generateLog(method, joinPoint, publicMethodLog, requestMappingValue, resultObj);
        return resultObj;
    }

    private void generateLog(Method method, JoinPoint joinPoint, BdcLog bdcLog, String requestMappingValue, final Object resultObj) {
        final BdcXtLog bdcXtLog = new BdcXtLog();
        bdcXtLog.setLogid(UUIDGenerator.generate18());
        bdcXtLog.setCzrq(Calendar.getInstance().getTime());
        //如果是异步调用这个方法之前执行这个会获取不到request
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = request.getRemoteAddr();
        bdcXtLog.setIp(ip);
        UserInfo currentUser = SessionUtil.getCurrentUser();
        if (currentUser != null) {
            bdcXtLog.setUsername(currentUser.getUsername());
            bdcXtLog.setUserid(currentUser.getId());
        }
        bdcXtLog.setController(bdcLog.controller());
        dealArguments(method, joinPoint, bdcLog, bdcXtLog);

        try {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        bdcXtLog.setLogid(UUIDGenerator.generate18());
                        entityMapper.saveOrUpdate(bdcXtLog, bdcXtLog.getLogid());
                    } catch (Exception e) {
                        logger.error("errorMsg:", e);
                    }

                }
            });
        } catch (Exception e) {
            logger.error("日志存储错误{}", e);
        }
    }

    /**
     * @return void
     * @Author xiejianan
     * @Description 处理查询条件
     * @Date 16:54 2019/8/27
     * @Param [method, joinPoint, queryXtLog]
     */
    private void dealArguments(Method method, JoinPoint joinPoint, BdcLog bdcLog, BdcXtLog bdcXtLog) {
        Object[] args = joinPoint.getArgs();
        //获取参数名称  性能很低
        String[] paraNameArr = new LocalVariableTableParameterNameDiscoverer().getParameterNames(method);
        List<Object> arguments = Lists.newArrayList();
        Object arg;
        for (int i = 0; i < args.length; i++) {
            Map<String, String> item = Maps.newHashMap();
            arg = args[i];
            if (args[i] instanceof Model || arg instanceof Pageable || arg instanceof RedirectAttributes || arg instanceof HttpServletResponse) {
                continue;
            } else if (args[i] instanceof HttpServletRequest) {
                item.put("parameterType", HttpServletRequest.class.getName());
                item.put("parameterName", paraNameArr[i]);
                arguments.add(item);
            } else {
                item.put("parameterValue", JSONObject.toJSONString(arg));
                item.put("parameterName", paraNameArr[i]);
                arguments.add(item);
            }
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(bdcLog.parmjson(), arguments);
        bdcXtLog.setParmjson(jsonObject.toJSONString());
    }


    /**
     * @param joinPoint 注解点
     * @return
     * @author <a href="mailto:shenjian@gtmap.cn">shenjian</a>
     * @version 1.0, 2016/7/29
     * @description 返回注解的方法对象
     */
    private Method getMethod(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Class[] argTypes = new Class[joinPoint.getArgs().length];
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof HttpServletRequest) {
                argTypes[i] = HttpServletRequest.class;
                continue;
            }
            if (args[i] instanceof HttpServletResponse) {
                argTypes[i] = HttpServletResponse.class;
                continue;
            }
            if (args[i] instanceof Model) {
                argTypes[i] = Model.class;
                continue;
            }
            if (args[i] != null) {
                argTypes[i] = args[i].getClass();
            } else {
                argTypes[i] = String.class;
            }
        }
        Method method = null;
        try {
            method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), argTypes);
        } catch (NoSuchMethodException e) {
            logger.error("errorMsg:", e);
        } catch (SecurityException e) {
            logger.error("errorMsg:", e);
        }
        return method;
    }

}