/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.youchain.aspect;

import cn.hutool.json.JSONUtil;
import com.youchain.domain.OperLog;
import com.youchain.repository.OperLogRepository;
import com.youchain.service.OperLogService;
import com.youchain.utils.RequestHolder;
import com.youchain.utils.SecurityUtils;
import com.youchain.utils.StringUtils;
import com.youchain.utils.ThrowableUtil;
import lombok.extern.slf4j.Slf4j;
import com.youchain.domain.Log;
import com.youchain.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Liu Xue
 * @date 2018-11-24
 */
@Component
@Aspect
@Slf4j
public class LogAspect {

    private final LogService logService;
    private final OperLogService operLogService;

    ThreadLocal<Long> currentTime = new ThreadLocal<>();

    public LogAspect(LogService logService, OperLogService operLogService) {
        this.logService = logService;
        this.operLogService = operLogService;
    }

    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.youchain.annotation.Log)")
    public void logPointcut() {
        // 该方法无方法体,主要为了让同类中其他方法使用此切入点
    }

    /**
     * 配置环绕通知,使用在方法logPointcut()上注册的切入点
     *
     * @param joinPoint join point for advice
     */
    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        currentTime.set(System.currentTimeMillis());
        result = joinPoint.proceed();
        String result_data = handlerResult(result);
        //log.error("----结束了---"+result_data);
        Log log_data = new Log("INFO",System.currentTimeMillis() - currentTime.get());
        currentTime.remove();
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        String url=handlerUrl(request);
        String parm=handleParm(request);
        if(parm!=null&&parm.length()>0){
            url=url+"?"+parm;
        }

        logService.save(url,result_data,getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request),joinPoint, log_data);
        return result;
    }

    private String handleParm( HttpServletRequest request) {
        if (request == null) {
            return "request为空";
        }
        String parm="";
        try {
            parm=request.getQueryString();
        } catch (Exception e) {
            parm="parm获取失败";
            log.error("parm获取失败", e);
        }
        return parm;
    }

    private String handlerUrl( HttpServletRequest request) {
        if (request == null) {
            return "request为空";
        }
        String url="";
        try {
            url=request.getRequestURL().toString();
        } catch (Exception e) {
            url="url获取失败";
            log.error("url获取失败", e);
        }
        return url;
    }


    private String handlerResult(Object result) {
        if (result == null) {
            return null;
        }
        String resultStr;
        try {
            if (result instanceof String) {
                resultStr = (String) result;
            } else {
                resultStr = JSONUtil.toJsonStr(result);// 如果返回结果非String类型，转换成JSON格式的字符串
            }

            if (resultStr.length() > 10000) {
                resultStr = resultStr.substring(0, 10000);
            }
        } catch (Exception e) {
            resultStr = result.toString();
            log.error("接口出入参日志打印切面处理返回参数异常", e);
        }
        return resultStr;
    }

    @AfterReturning(returning = "returnValue", pointcut = "logPointcut()")
    public void doAfterReturning(JoinPoint joinPoint,Object returnValue) throws Throwable {
        // 处理完请求，返回内容
      /*  log.info("类名:"+joinPoint.getSignature().getDeclaringTypeName()+
                "、方法名: "+joinPoint.getSignature().getName()+"()、====>@Before：请求参数为：{}", Arrays.toString(joinPoint.getArgs()));
*/
    }



    /**
     * 配置异常通知
     *
     * @param joinPoint join point for advice
     * @param e exception
     */
    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        String errDetail=ThrowableUtil.getStackTrace(e)+"";
        log.error("拦截到异常消息", errDetail);
        Log log_data = new Log("ERROR",System.currentTimeMillis() - currentTime.get());
        OperLog oper_log_data = new OperLog("ERROR",System.currentTimeMillis() - currentTime.get());
        currentTime.remove();


        HttpServletRequest request = RequestHolder.getHttpServletRequest();

        String url=handlerUrl(request);
        String parm=handleParm(request);
        if(parm!=null&&parm.length()>0){
            url=url+"?"+parm;
        }
        log_data.setExceptionDetail(errDetail);

        logService.save(url,"",getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request), (ProceedingJoinPoint)joinPoint, log_data);
        String operErrDetail="";
        Pattern pattern = Pattern.compile("com\\.youchain\\.exception\\.OperBadRequestException: .*");
        Matcher matcher = pattern.matcher(errDetail);
        if (matcher.find()) {
            operErrDetail = matcher.group(0); // com.youchain.exception.BadRequestException: 拆分数量不能大于原数量
            oper_log_data.setExceptionDetail(operErrDetail.substring(operErrDetail.indexOf(":")+1));
            operLogService.save(url,"",getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request), (ProceedingJoinPoint)joinPoint, oper_log_data);

        }
    }

    public String getUsername() {
        try {
            return SecurityUtils.getCurrentUsername();
        }catch (Exception e){
            return "";
        }
    }
}
