package com.wteam.framework.modules.system.aspect.interceptor;


import com.wteam.framework.common.security.AuthUser;
import com.wteam.framework.common.security.context.UserContext;
import com.wteam.framework.common.utils.IpHelper;
import com.wteam.framework.common.utils.IpUtils;
import com.wteam.framework.common.utils.SpelUtil;
import com.wteam.framework.common.utils.ThreadPoolUtil;
import com.wteam.framework.modules.permission.entity.vo.SystemLogVO;
import com.wteam.framework.modules.permission.service.SystemLogService;
import com.wteam.framework.modules.system.aspect.annotation.SystemLogPoint;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Spring AOP实现日志管理
 *
 * @author Chopper
 */
@Aspect
@Component
@Slf4j
public class SystemLogAspect {

    /**
     * 启动线程异步记录日志
     */
    private static final ThreadLocal<Date> BEGIN_TIME_THREAD_LOCAL = new NamedThreadLocal<>("SYSTEM-LOG");

    @Autowired
    private SystemLogService systemLogService;

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private IpHelper ipHelper;

    /**
     * Controller层切点,注解方式
     */
    @Pointcut("@annotation(com.wteam.framework.modules.system.aspect.annotation.SystemLogPoint)")
    public void controllerAspect() {

    }

    /**
     * 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
     */
    @Before("controllerAspect()")
    public void doBefore() {
        BEGIN_TIME_THREAD_LOCAL.set(new Date());
    }


    /**
     * 后置通知(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作
     *
     * @param joinPoint 切点
     */
    @AfterReturning(returning = "rvt", pointcut = "controllerAspect()")
    public void after(JoinPoint joinPoint, Object rvt) {
        try {
            Map map = spelFormat(joinPoint, rvt);
            String description = map.get("description").toString();
            String customerLog = map.get("customerLog").toString();

            Map<String, String[]> logParams = request.getParameterMap();
            AuthUser authUser = UserContext.getCurrentUser();
            SystemLogVO systemLogVO = new SystemLogVO();

            if (authUser == null) {
                //如果是商家则记录商家id，否则记录-1，代表平台id
                systemLogVO.setStoreId(-2L);
                //请求用户
                systemLogVO.setUsername("游客");
            } else {
                //如果是商家则记录商家id，否则记录-1，代表平台id
//                systemLogVO.setStoreId(authUser.getRole().equals(UserEnums.STORE) ? Long.parseLong(authUser.getStoreId()) : -1);
                //请求用户
                systemLogVO.setUsername(authUser.getUsername());
            }

            //日志标题
            systemLogVO.setName(description);
            //日志请求url
            systemLogVO.setRequestUrl(request.getRequestURI());
            //请求方式
            systemLogVO.setRequestType(request.getMethod());
            //请求参数
            systemLogVO.setMapToParams(logParams);
            //响应参数 此处数据太大了，所以先注释掉
//           systemLogVO.setResponseBody(JSONUtil.toJsonStr(rvt));
            //请求IP
            systemLogVO.setIp(IpUtils.getIpAddress(request));
            //IP地址
            systemLogVO.setIpInfo(ipHelper.getIpCity(request));
            //写入自定义日志内容
            systemLogVO.setCustomerLog(customerLog);
            //请求开始时间
            long beginTime = BEGIN_TIME_THREAD_LOCAL.get().getTime();
            long endTime = System.currentTimeMillis();
            //请求耗时
            Long usedTime = endTime - beginTime;
            systemLogVO.setCostTime(usedTime.intValue());
            //调用线程保存
            ThreadPoolUtil.getPool().execute(new SaveSystemLogThread(systemLogVO, systemLogService));


            BEGIN_TIME_THREAD_LOCAL.remove();
        } catch (Exception e) {
            log.error("系统日志保存异常", e);
        }
    }

    /**
     * 保存日志
     */
    private static class SaveSystemLogThread implements Runnable {
        @Autowired
        private SystemLogVO systemLogVO;
        @Autowired
        private SystemLogService systemLogService;

        public SaveSystemLogThread(SystemLogVO systemLogVO, SystemLogService systemLogService) {
            this.systemLogVO = systemLogVO;
            this.systemLogService = systemLogService;
        }

        @Override
        public void run() {
            try {
                systemLogService.saveLog(systemLogVO);
            } catch (Exception e) {
                log.error("系统日志保存异常,内容{}：", systemLogVO, e);
            }
        }
    }


    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    private static Map<String, String> spelFormat(JoinPoint joinPoint, Object rvt) {

        Map<String, String> result = new HashMap<>(2);
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        SystemLogPoint systemLogPoint = signature.getMethod().getAnnotation(SystemLogPoint.class);
        String description = systemLogPoint.description();
        String customerLog = SpelUtil.compileParams(joinPoint, rvt, systemLogPoint.customerLog());

        result.put("description", description);
        result.put("customerLog", customerLog);
        return result;
    }

}
