package xtools.boot.log;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.extern.slf4j.Slf4j;
import xtools.base.config.BaseParams;
import xtools.base.exception.CommonException;
import xtools.base.exception.ExceptionUtils;
import xtools.boot.api.enums.ThreadType;
import xtools.boot.api.model.dto.log.LogTrack;
import xtools.boot.core.utils.SpringContextUtils;
import xtools.boot.log.config.LogBusConfig;
import xtools.boot.log.config.RunInfoConfig;
import xtools.boot.log.holder.LogTrackHolder;
import xtools.boot.log.interfaces.LogBusInterface;
import xtools.boot.log.interfaces.LogBusType;
import xtools.boot.log.model.dto.LogBody;
import xtools.boot.log.model.dto.RunInfo;
import xtools.core.ArrUtils;
import xtools.core.CollectionUtils;
import xtools.core.StringUtils;
import xtools.core.enums.LogLevel;
import xtools.core.enums.TimePattern;
import xtools.core.sys.SysBaseInfoUtils;
import xtools.core.time.InstantUtils;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * <p>Title : LogBus</p>
 * <p>Description : LogBus</p>
 * <p>DevelopTools : Idea_x64_v2026.1</p>
 * <p>DevelopSystem : macOS Sequoia 15.7.5</p>
 * <p>Company : org.xujun</p>
 *
 * @author : XuJun
 * @version : 5.0.0
 * @date : 2026/2/3 09:33
 */
@Slf4j
public class LogBus implements BaseParams {

    /**
     * 运行信息
     */
    private static RunInfo runInfo;

    /**
     * 日志体
     */
    private final LogBody logBody;

    /**
     * 异常信息
     */
    private Throwable throwable;

    /**
     * 是否在控制台打印
     */
    private Boolean print;

    /**
     * 构造方法
     *
     * @param level    日志级别
     * @param type     日志类型
     * @param logTrack 日志信息
     */
    private LogBus(LogLevel level, LogBusType type, LogTrack logTrack) {
        // 参数处理
        if (Objects.isNull(level)) {
            level = LogLevel.INFO;
        }
        if (Objects.isNull(logTrack)) {
            logTrack = LogTrackHolder.get();
        }
        // 构建日志信息
        logBody = new LogBody();
        logBody.setLevel(level);
        logBody.setType(type.desc());
        logBody.setLogTrack(logTrack);
        logBody.setRunInfo(getRunInfo());
    }

    /**
     * 初始化
     *
     * @param type 日志类型
     * @return 当前对象
     */
    public static LogBus init(LogBusType type) {
        return new LogBus(null, type, null);
    }

    /**
     * 初始化
     *
     * @param level 日志级别
     * @param type  日志类型
     * @return 当前对象
     */
    public static LogBus init(LogLevel level, LogBusType type) {
        return new LogBus(level, type, null);
    }

    /**
     * 初始化
     *
     * @param level    日志级别
     * @param type     日志类型
     * @param logTrack 日志信息
     * @return 当前对象
     */
    public static LogBus init(LogLevel level, LogBusType type, LogTrack logTrack) {
        return new LogBus(level, type, logTrack);
    }

    /**
     * 格式化日志
     *
     * @param logBody 日志信息
     * @return 日志信息
     */
    private static StringBuilder fmtLog(LogBody logBody) {
        // 日志标题
        String title = logBody.getTitle();
        if (StringUtils.isBlank(title)) {
            title = "日志信息";
        }
        LogTrack logTrack = logBody.getLogTrack();
        // 日志时间
        String time = InstantUtils.format(InstantUtils.from(logTrack.time()), TimePattern.ALL);
        // 线程类型
        ThreadType threadType = logTrack.type();
        // 堆栈信息
        JSONArray stackTraceInfo = logBody.getStackTrace();
        // 日志数据
        JSONObject data = logBody.getLogData();
        // 构建打印日志
        StringBuilder sb = new StringBuilder();
        sb.append(CP_NEWLINE);
        sb.append("┏━━━ ").append(title).append(" Start ━━━").append(CP_NEWLINE);
        sb.append("┠ ").append("日志ID: ").append(logTrack.id()).append(CP_NEWLINE);
        sb.append("┠ ").append("日志时间: ").append(time).append(CP_NEWLINE);
        sb.append("┠ ").append("追踪ID: ").append(logTrack.traceId()).append(CP_NEWLINE);
        sb.append("┠ ").append("线程类型: ").append(threadType.desc()).append(CP_NEWLINE);
        if (!ThreadType.MAIN.equals(threadType)) {
            sb.append("┠ ").append("父线程ID: ").append(logTrack.parentId()).append(CP_NEWLINE);
        }
        sb.append("┠ ").append("日志级别: ").append(logBody.getLevel().level()).append(CP_NEWLINE);
        sb.append("┠ ").append("日志类型: ").append(logBody.getType()).append(CP_NEWLINE);
        sb.append("┠ ").append("运行环境: ").append(CP_NEWLINE).append(JSONObject.toJSONString(logBody.getRunInfo(), JSONWriter.Feature.PrettyFormat)).append(CP_NEWLINE);
        if (CollectionUtils.isNotEmpty(stackTraceInfo)) {
            sb.append("┠ ").append("堆栈信息: ").append(CP_NEWLINE).append(stackTraceInfo.toJSONString(JSONWriter.Feature.PrettyFormat)).append(CP_NEWLINE);
        }
        if (CollectionUtils.isNotEmpty(data)) {
            sb.append("┠ ").append("日志数据: ").append(CP_NEWLINE).append(data.toJSONString(JSONWriter.Feature.PrettyFormat)).append(CP_NEWLINE);
        }
        sb.append("┗━━━ ").append(title).append(" End ━━━").append(CP_NEWLINE);
        return sb;
    }

    /**
     * 格式化堆栈信息
     *
     * @param stackTraceInfo    堆栈信息
     * @param includeStackTrace 包含的堆栈信息(开始的包名)
     * @return 堆栈信息
     */
    private static JSONArray fmtStackTrace(StackTraceElement[] stackTraceInfo, List<String> includeStackTrace) {
        // 获取当前线程的调用栈
        if (ArrUtils.isEmpty(stackTraceInfo)) {
            return null;
        }
        JSONArray result = new JSONArray();
        if (CollectionUtils.isEmpty(includeStackTrace)) {
            // 获取所有堆栈数据
            Arrays.stream(stackTraceInfo).toList().forEach((item) -> result.add(item.toString()));
            return result;
        }

        // 获取关于本工程堆栈信息
        for (StackTraceElement item : stackTraceInfo) {
            String className = item.getClassName();

            boolean flag = false;
            for (String include : includeStackTrace) {
                if (className.startsWith(include)) {
                    flag = true;
                    break;
                }
            }
            if (flag) {
                result.add(item.toString());
            }
        }
        return result;
    }

    /**
     * 日志标题
     *
     * @param title 标题
     * @return 当前对象
     */
    public LogBus title(String title) {
        if (StringUtils.isNotBlank(title)) {
            logBody.setTitle(title);
        }
        return this;
    }

    /**
     * 是否打印
     *
     * @param print 是否打印
     * @return 当前对象
     */
    public LogBus print(boolean print) {
        this.print = print;
        return this;
    }

    /**
     * 设置日志信息
     *
     * @param log 日志信息
     * @return 当前对象
     */
    public LogBus data(final Object log) {
        if (Objects.isNull(log)) {
            return this;
        }
        if (log instanceof JSONObject jsonLog) {
            logBody.setLogData(jsonLog);
        } else {
            JSONObject logData;
            try {
                logData = JSONObject.from(log);
            } catch (Exception e) {
                logData = JSONObject.of("data", log);
            }
            logBody.setLogData(logData);
        }
        return this;
    }

    /**
     * 设置异常信息
     *
     * @param throwable 异常信息
     * @return 当前对象
     */
    public LogBus error(Throwable throwable) {
        this.throwable = throwable;
        return this;
    }

    /**
     * 保存日志
     */
    public void save() {
        // 获取堆栈信息
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        ExecutorService service = Executors.newVirtualThreadPerTaskExecutor();
        try {
            // 创建子线程,并异步提交
            CompletableFuture.runAsync(() -> {
                try {
                    doSave(logBody, throwable, stackTrace);
                } catch (Exception e) {
                    log.error("保存日志异常", e);
                }
            }, service);
        } finally {
            // 关闭线程池
            service.shutdown();
        }
    }

    /**
     * 获取运行信息
     *
     * @return 运行信息
     */
    private RunInfo getRunInfo() {
        if (runInfo != null) {
            return runInfo;
        }
        RunInfoConfig sysRunInfo = SpringContextUtils.getBean(RunInfoConfig.class);
        RunInfo info = new RunInfo();
        info.setLocalIp(SysBaseInfoUtils.getLocalIp());
        info.setAppName(sysRunInfo.getName());
        info.setPort(sysRunInfo.getPort());
        runInfo = info;
        return runInfo;
    }

    /**
     * 执行日志发送
     *
     * @param logBody    日志内容
     * @param throwable  异常
     * @param stackTrace 堆栈信息
     */
    private void doSave(LogBody logBody, Throwable throwable, StackTraceElement[] stackTrace) {
        // 获取配置文件
        LogBusConfig config = SpringContextUtils.getBean(LogBusConfig.class);

        // 计算日志级
        boolean geWarn = logBody.getLevel().code() > LogLevel.INFO.code();
        // 处理堆栈信息
        logBody.setStackTrace(fmtStackTrace(stackTrace, geWarn ? null : config.getIncludeStackTrace()));

        // 是否打印日志
        if (Objects.nonNull(print)) {
            if (print) {
                printLog(logBody);
            }
        } else {
            if (geWarn || config.getPrint()) {
                printLog(logBody);
            }
        }
        // 异常日志不做忽略
        if (!geWarn) {
            // 不保存日志
            if (!logBody.getLogTrack().save()) {
                return;
            }
        }
        // 处理日志内容超长
        JSONObject data = logBody.getLogData();
        if (CollectionUtils.isNotEmpty(data)) {
            Integer logDataItemMax = config.getLogDataMaxLength();
            if (!Objects.equals(logDataItemMax, CP_NEGATIVE1)) {
                String dataStr = data.toString();
                if (dataStr.length() > logDataItemMax) {
                    JSONObject newLogData = JSONObject.of(
                            "msg", "日志内容超长",
                            "log", dataStr.substring(CP_NUM0, logDataItemMax) + "..."
                    );
                    logBody.setLogData(newLogData);
                }
            }
        }
        // 处理异常信息
        if (Objects.nonNull(throwable)) {
            if (throwable instanceof CommonException commonException) {
                String message = commonException.getMessage();
                Throwable cause = commonException.getCause();
                String errMsg = ExceptionUtils.getErrMsg(Objects.nonNull(cause) ? cause : throwable);
                logBody.setErr(message + CP_NEWLINE + errMsg);
            } else {
                logBody.setErr(ExceptionUtils.getErrMsg(throwable));
            }
        }

        // 保存日志信息
        Collection<LogBusInterface> beanList = SpringContextUtils.getBeanList(LogBusInterface.class);
        if (CollectionUtils.isNotEmpty(beanList)) {
            beanList.forEach(item -> item.save(logBody));
        }
    }

    /**
     * 打印日志信息
     *
     * @param logBody 日志信息
     */
    private void printLog(LogBody logBody) {
        // 格式化日志
        StringBuilder logContext = fmtLog(logBody);
        // 异常数据
        if (Objects.isNull(throwable)) {
            logContext.append(CP_NEWLINE);
            log.info(logContext.toString());
        } else {
            logContext.append("━━━━ ↓↓↓ exception ↓↓↓ ━━━━").append(CP_NEWLINE);
            log.error(logContext.toString(), throwable);
        }
    }

}
