package com.yanzuoguang.cloud.aop;

import com.yanzuoguang.util.helper.DateHelper;
import com.yanzuoguang.util.helper.StringHelper;
import com.yanzuoguang.util.thread.ThreadNext;
import com.yanzuoguang.util.vo.LogVo;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 当前线程日志编写
 *
 * @author 颜佐光
 */
@Component
public class LogLocal implements ThreadNext.Next, InitializingBean {
    /**
     * 缓存队列
     */
    protected volatile Map<String, Timeout<LogVo>> cache = new Hashtable<>();


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

    /**
     * 1个请求最长时间
     */
    @Value("${yzg.timeout:300000}")
    private int timeout;

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

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

    /**
     * 超时状态
     */
    public static final String MAX_TIME = "MAX_TIME";
    public static final String MAX_TIME_NAME = "执行超时";

    /**
     * 日志基础
     */
    @Autowired
    private LogBase logBase;

    /**
     * Invoked by a BeanFactory after it has set all bean properties supplied
     * (and satisfied BeanFactoryAware and ApplicationContextAware).
     * <p>This method allows the bean instance to perform initialization only
     * possible when all bean properties have been set and to throw an
     * exception in the event of misconfiguration.
     *
     * @throws Exception in the event of misconfiguration (such
     *                   as failure to set an essential property) or if initialization fails.
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        ThreadNext.start(this, "save log error");
    }

    /**
     * 开始记录日志
     *
     * @param actionKey    功能名称
     * @param actionSubKey 子功能名称 = 服务名称 + 地址
     * @param content      请求内容
     * @return
     */
    public LogVo startLog(String actionKey, String actionSubKey, String content) {
        if (isLog(actionKey, actionSubKey)) {
            return null;
        }
        // 生命日志对象爱嗯
        LogVo log = new LogVo();
        log.setLogId(StringHelper.getNewID());
        log.setActionKey(actionKey);
        log.setActionSubKey(actionSubKey);
        log.setContent(content);
        // 声明超时对象
        Timeout<LogVo> time = new Timeout<>(log);
        log.setCreateDate(DateHelper.getDateTimeString(new Date(time.getStart())));
        cache.put(log.getLogId(), time);
        return log;
    }

    /**
     * 写入状态
     *
     * @param status
     * @param result
     * @return
     */
    public void result(LogVo log, String status, String result) {
        this.result(log, StringHelper.EMPTY, status, result);
    }

    /**
     * 写入状态
     *
     * @param status
     * @param result
     * @return
     */
    public void result(LogVo log, String name, String status, String result) {
        // 日志请求不记录,防止死循环递归
        boolean isLog = isLog(name, log.getActionSubKey());
        if (isLog) {
            this.remove(log);
        } else {
            Timeout<LogVo> timeout = cache.get(log.getLogId());
            result(timeout, log, status, result, true);
        }
    }

    /**
     * 写入状态
     *
     * @param status
     * @param result
     * @return
     */
    private void result(Timeout timeout, LogVo log, String status, String result, boolean write) {
        if (timeout != null) {
            long useTime = System.currentTimeMillis() - timeout.getStart();
            log.setUseTime((int) useTime);
        }
        log.setStatus(status);
        log.setResult(result);
        if (!StringHelper.compare(status, MAX_TIME)) {
            cache.remove(log.getLogId());
        }
        if (write) {
            logBase.addLog(log);
        }
    }

    /**
     * 删除请求信息
     *
     * @param log
     */
    private void remove(LogVo log) {
        Timeout<LogVo> timeout = cache.get(log.getLogId());
        result(timeout, log, StringHelper.EMPTY, StringHelper.EMPTY, false);
    }

    /**
     * 记录超时
     *
     * @return
     */
    private void writeTimeout(Timeout<LogVo> timeout) {
        result(timeout, timeout.getData(), MAX_TIME, MAX_TIME_NAME, true);
    }

    /**
     * 是否属于日志服务
     *
     * @param keys
     * @return
     */
    public boolean isLog(String... keys) {
        List<String> list = new ArrayList<>();
        list.add(this.applicationName);
        for (String item : keys) {
            list.add(item);
        }
        // 是否排除
        boolean notFilter = false;
        // 是否过滤
        boolean filter = false;
        for (String item : list) {
            String lower = item.toLowerCase();
            notFilter = notFilter || lower.matches(this.notFilter);
            filter = filter || lower.matches(this.filter);
        }
        if (notFilter) {
            return false;
        }
        return filter;
    }

    /**
     * 执行下一个函数,出现异常会继续执行
     *
     * @return 是否继续执行
     * @throws Exception 异常信息
     */
    @Override
    public boolean next() throws Exception {
        List<String> keys = new ArrayList<>();
        keys.addAll(cache.keySet());
        for (String key : keys) {
            Timeout<LogVo> timeout = cache.get(key);
            if (timeout == null || !timeout.isMaxTime(this.timeout)) {
                continue;
            }
            writeTimeout(timeout);
        }
        return true;
    }

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