package com.yanzuoguang.cloud.aop;

import com.alibaba.fastjson.JSON;
import com.yanzuoguang.util.cache.MemoryCache;
import com.yanzuoguang.util.contants.ResultConstants;
import com.yanzuoguang.util.exception.ExceptionHelper;
import com.yanzuoguang.util.helper.JsonHelper;
import com.yanzuoguang.util.helper.StringHelper;
import com.yanzuoguang.util.log.Log;
import com.yanzuoguang.util.thread.ThreadNext;
import com.yanzuoguang.util.vo.LogVo;
import com.yanzuoguang.util.vo.ResponseResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 基本处理拦截类
 *
 * @author 颜佐光
 */
public class BaseRequestAspect implements ThreadNext.Next {


    @Value("${spring.application.name}")
    protected String applicationName;

    @Value("${yzg.logCommon:true}")
    protected boolean logCommon = true;

    @Value("${yzg.logAll:false}")
    protected boolean logAll = false;

    @Value("${yzg.cacheTime:120}")
    protected int cacheTime = 120;

    @Value("${yzg.reqSize:5000}")
    private int reqSize;

    @Value("${yzg.log.notFilter:^.*login.*$}")
    private String notFilter;

    @Value("${yzg.log.filter:^.*log.*$}")
    private String filter;

    @Autowired
    protected ApplicationContext context;

    protected LogFeign logFeign;
    /**
     * 缓存队列
     */
    protected volatile LinkedBlockingQueue<LogVo> cache = new LinkedBlockingQueue<LogVo>();

    public BaseRequestAspect() {
        ThreadNext.start(this, "save log error");
    }

    /**
     * 添加日志到缓存中,并不是立即添加,而是通过线程执行,防止对环境造成影响
     *
     * @param logVo
     */
    public void addLog(LogVo logVo) {
        cache.add(logVo);
    }

    /**
     * 初始化日志Vo
     *
     * @param url            请求路径
     * @param joinPoint      请求参数
     * @param responseResult 返回参数
     * @return
     */
    protected LogVo initLogInterVo(String url, ProceedingJoinPoint joinPoint, ResponseResult responseResult) {
        LogVo logInterVo = new LogVo();
        logInterVo.setLogId(StringHelper.getNewID());
        //平台名
        logInterVo.setLogSources(applicationName);
        //请求URL
        logInterVo.setInterUrl(url);
        Object paraTo = getFirstDataParameter(joinPoint.getArgs());
        //请求参数
        logInterVo.setContent(JSON.toJSONString(paraTo));
        //返回参数
        logInterVo.setResult(JSON.toJSONString(responseResult));
        logInterVo.setStatus(responseResult != null && responseResult.getCode() == ResultConstants.SUCCESS ? 1 : 0);
        return logInterVo;
    }

    /**
     * 获取请求的参数
     *
     * @param args
     * @return
     */
    protected Object getFirstDataParameter(Object[] args) {
        List<Object> para = getDataParameters(args);
        Object paraTo = para;
        if (para.size() == 1) {
            paraTo = para.get(0);
        }
        return paraTo;
    }

    /**
     * 获取数据参数
     *
     * @param args
     * @return
     */
    protected List<Object> getDataParameters(Object[] args) {
        List<Object> para = new ArrayList<>();
        for (Object item : args) {
            if (item instanceof HttpServlet || item instanceof HttpServletResponse || item instanceof HttpServletRequest) {
                continue;
            }
            para.add(item);
        }
        return para;
    }

    /**
     * 获取JSON,当Json过长时,截断
     *
     * @param paraJson
     * @return
     */
    private String getMaxString(String paraJson) {
        if (paraJson != null && paraJson.length() > reqSize) {
            paraJson = paraJson.substring(0, reqSize);
        }
        return paraJson;
    }

    /**
     * 获取是否清空日志的标签
     *
     * @return 返回日志是否清空
     */
    protected boolean requestLogInit() {
        boolean clear = Log.threadCurrent() == null;
        if (clear) {
            Log.threadBegin();
        }
        return clear;
    }

    /**
     * 记录请求日志
     *
     * @param joinPoint
     * @return
     */
    protected long requestLog(String tag, ProceedingJoinPoint joinPoint) {
        long start = System.currentTimeMillis();
        try {
            String name = joinPoint.getSignature().getName();
            Log.info(joinPoint.getSignature().getDeclaringType(), " %s [ %s ] request: %s",
                    tag, name, this.getMaxString(JsonHelper.serialize(getFirstDataParameter(joinPoint.getArgs()))));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return start;
    }

    /**
     * 保存日志
     *
     * @param url
     * @param joinPoint
     * @param result
     * @param resultEx
     * @param start
     */
    protected void responseLog(boolean clear, String tag, String url, ProceedingJoinPoint joinPoint, long start, Object result, Exception resultEx) {
        try {
            String name = joinPoint.getSignature().getName();
            if (StringHelper.isEmpty(url)) {
                url = String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(), name);
            }
            // 处理结果
            ResponseResult responseResult;
            if (result instanceof ResponseResult) {
                responseResult = (ResponseResult) result;
            } else {
                responseResult = ResponseResult.result(result);
            }

            long time = System.currentTimeMillis() - start;
            if (resultEx != null) {
                responseResult = ExceptionHelper.getError(resultEx);
                Log.error(joinPoint.getSignature().getDeclaringType(), resultEx, "%s [ %s ] time %d ms, result: %s",
                        tag, name, time, getMaxString(JsonHelper.serialize(responseResult)));
                resultEx.printStackTrace();
            } else if (logCommon) {
                Log.info(joinPoint.getSignature().getDeclaringType(), "%s [ %s ] time %d ms, result: %s",
                        tag, name, time, getMaxString(JsonHelper.serialize(responseResult)));
            }

            // 日志请求不记录,防止死循环递归
            boolean isLog = isLog(name, url);
            if (isLog) {
                return;
            }

            // 正常请求不记录
            if (!logAll
                    && responseResult != null
                    && responseResult.getCode() == ResultConstants.SUCCESS) {
                return;
            }
            LogVo logVo = initLogInterVo(url, joinPoint, responseResult);
            logVo.setUseTime((int) time);
            addLog(logVo);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (clear) {
                Log.threadCommit();
            }
        }
    }

    /**
     * 是否属于日志服务
     *
     * @param name
     * @param url
     * @return
     */
    private boolean isLog(String name, String url) {
        boolean noFilter = applicationName.matches(notFilter) || name.matches(notFilter) || url.matches(notFilter);
        if (noFilter) {
            return false;
        }

        boolean isLog = applicationName.matches(filter) || name.matches(filter) || url.matches(filter);
        return isLog;
    }

    /**
     * 执行下一个函数,出现异常会继续执行
     *
     * @return 是否继续执行
     */
    @Override
    public boolean next() {
        while (cache.size() > 0) {
            LogVo item = cache.poll();
            if (item != null) {
                try {
                    if (logFeign == null) {
                        logFeign = context.getBean(LogFeign.class);
                    }
                    logFeign.save(item);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
        return true;
    }

    /**
     * 沉睡时间
     *
     * @return
     */
    @Override
    public int getNextTime() {
        return 1000;
    }
}