/**
 * Copyright (c) 2016-2019 人人开源 All rights reserved.
 * <p>
 * https://www.renren.io
 * <p>
 * 版权所有，侵权必究！
 */

package com.chushang.common.log.aspect;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson2.JSONObject;
import com.chushang.common.core.constant.CommonConstants;
import com.chushang.common.core.exception.ResultException;
import com.chushang.common.core.jackson.JacksonUtils;
import com.chushang.common.core.util.IPUtils;
import com.chushang.common.core.util.ServletUtils;
import com.chushang.common.log.annotation.SysLog;
import com.chushang.common.log.entity.SysLogEntity;
import com.chushang.common.log.enums.BusinessType;
import com.chushang.common.log.enums.LogTypeEnum;
import com.chushang.common.log.service.SysLogService;
import com.chushang.security.utils.SecurityUtils;
import com.chushang.security.entity.vo.LoginUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


/**
 * 系统日志，切面处理类
 *
 * @author Mark sunlightcs@gmail.com
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class SysLogAspect {

    final SysLogService sysLogService;

    @Pointcut("@annotation(com.chushang.common.log.annotation.SysLog)")
    public void logPointCut() {
    }

    /**
     * 不主动拦截get 请求
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PutMapping)")
    public void exceptionLogPointCut() {
    }

    @AfterReturning("logPointCut()")
    public void around(JoinPoint point) {
        long beginTime = System.currentTimeMillis();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
        saveSysLog(point, LogTypeEnum.NORMAL, null);
    }

    /**
     * 异常返回通知，用于拦截异常日志信息 连接点抛出异常后执行
     * --> 未执行, 因为 有全局捕捉一长
     * @param joinPoint 切入点
     * @param ex         异常信息
     */
    @AfterThrowing(pointcut = "exceptionLogPointCut()", throwing = "ex")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable ex) {
        long beginTime = System.currentTimeMillis();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        saveSysLog(joinPoint,LogTypeEnum.ERROR, ex.getMessage());
    }

    private void saveSysLog(JoinPoint joinPoint,LogTypeEnum type, String ex) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            // 获取到的所有参数
            Object[] args = joinPoint.getArgs();
            SysLogEntity sysLogEntity = new SysLogEntity();
            SysLog syslog =
                    method.getAnnotation(SysLog.class);

            if (syslog != null) {
                String value = syslog.value();
                BusinessType businessType = syslog.businessType();
                if (null != businessType){
                    value = businessType.getDesc() + value;
                    sysLogEntity.setOperationType(businessType.getCode());
                }
                sysLogEntity.setOperation(value);
                if (null != args && args.length > 0) {
                    // 将导出的response 过滤掉
                    List<Object> argList =
                            Arrays.stream(args)
                                    .filter(arg -> !(arg instanceof MultipartFile) && !(arg instanceof HttpServletResponse) && !(arg instanceof HttpServletRequest))
                                    .collect(Collectors.toList());
                    sysLogEntity.setParams(JacksonUtils.toJSONString(argList));
                }
            }

            //获取request
            HttpServletRequest request = ServletUtils.getRequest();
            String ipAddr = IPUtils.clientIp(request);
            //请求的方法名
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = signature.getName();
            sysLogEntity.setMethod(className + "." + methodName + "()");
            //设置IP地址
            sysLogEntity.setRemoteAddr(ipAddr);
            // 请求uri
            String uri = URLUtil.getPath(request.getRequestURI());
            sysLogEntity.setRequestUri(uri);
            // 请求代理
            sysLogEntity.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
            assert StrUtil.isNotEmpty(uri);
            String[] split = uri.split("/");
            if (split.length > 0 ){
                sysLogEntity.setApplicationName(split[1]);
            }

            LoginUser loginUser = SecurityUtils.getLoginUser();
            // 请求账号信息
            if (null != loginUser) {
                Long userId = loginUser.getUserId();
                //用户名
                sysLogEntity.setUserId(userId);
                String username = loginUser.getUsername();
                sysLogEntity.setUsername(username);
                //保存系统日志
            } else {
                if (!ipAddr.equals("localhost") && !ipAddr.equals("127.0.0.1")) {
                    throw new ResultException("没有权限");
                }
                sysLogEntity.setUserId(1L);
                sysLogEntity.setUsername("error");
            }
            String startTime = request.getHeader(CommonConstants.START_TIME);
            // 执行时间
            sysLogEntity.setTime(startTime != null ? System.currentTimeMillis() - Convert.toLong(startTime) : null);
            // 日志类型
            sysLogEntity.setType(type);
            sysLogEntity.setException(ex);
            assert syslog != null;
            if (syslog.isDatabase()){
                sysLogService.save(sysLogEntity);
            }else {
                log.info("log {}", JSONObject.toJSONString(sysLogEntity));
            }
        } catch (Exception e) {
            HttpServletRequest request = ServletUtils.getRequest();
            // 保存日志失败 不应当影响 正常的操作
            log.error("uri : {}, save log error", URLUtil.getPath(request.getRequestURI()), e);
        }
    }
}
