/**
 * @ProjectName: 智慧评标-主观分助手
 * @Package: com.zhpb.gdyd_zhpb_zgf.config
 * @ClassName: DatabaseLogAppender
 * @Description: Logback自定义Appender，将所有日志输出保存到数据库
 * @Author: 张志锋
 * @Date: 2025-10-31
 * @Version: 1.0
 * @Copyright: © 2025 智慧评标-主观分助手
 */
package com.zhpb.gdyd_zhpb_zgf.config;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import com.zhpb.gdyd_zhpb_zgf.entity.mysql.SysLog;
import com.zhpb.gdyd_zhpb_zgf.repository.mysql.SysLogRepository;
import org.springframework.context.ApplicationContext;

import java.time.LocalDateTime;

/**
 * 数据库日志Appender
 */
public class DatabaseLogAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {

    private static ApplicationContext applicationContext;
    private static SysLogRepository sysLogRepository;

    public static void setApplicationContext(ApplicationContext context) {
        DatabaseLogAppender.applicationContext = context;
        if (context != null) {
            try {
                sysLogRepository = context.getBean(SysLogRepository.class);
            } catch (Exception e) {
                // Bean未准备好，稍后重试
            }
        }
    }

    @Override
    protected void append(ILoggingEvent event) {
        // 过滤掉不需要记录的日志
        if (shouldSkip(event)) {
            return;
        }
        
        if (sysLogRepository == null && applicationContext != null) {
            try {
                sysLogRepository = applicationContext.getBean(SysLogRepository.class);
            } catch (Exception e) {
                // Bean未准备好，忽略
                return;
            }
        }

        if (sysLogRepository == null) {
            return;
        }

        try {
            SysLog sysLog = new SysLog();
            sysLog.setLogLevel(event.getLevel().toString());
            sysLog.setLoggerName(event.getLoggerName());
            
            // 格式化日志消息
            String formattedMessage = event.getFormattedMessage();
            // 将多行内容合并为一行
            String singleLineContent = formattedMessage.replace("\n", " ").replace("\r", " ").trim();
            sysLog.setLogContent(singleLineContent);
            sysLog.setFullMessage(getFullMessage(event).replace("\n", " ").replace("\r", " "));
            
            // 异常堆栈（保留多行格式）
            if (event.getThrowableProxy() != null) {
                sysLog.setExceptionStack(getStackTrace(event));
            }
            
            sysLog.setThreadName(event.getThreadName());
            sysLog.setCreateTime(LocalDateTime.now());
            sysLog.setApplicationName("gdyd_zhpb_zgf");
            
            // 获取调用位置信息
            if (event.getCallerData() != null && event.getCallerData().length > 0) {
                StackTraceElement caller = event.getCallerData()[0];
                sysLog.setClassName(caller.getClassName());
                sysLog.setMethodName(caller.getMethodName());
                sysLog.setLineNumber(caller.getLineNumber());
            }

            // 异步保存（使用新线程，避免阻塞）
            new Thread(() -> {
                try {
                    sysLogRepository.save(sysLog);
                } catch (Exception e) {
                    // 忽略保存失败，避免循环日志
                }
            }).start();
        } catch (Exception e) {
            // 忽略异常，避免影响正常日志输出
        }
    }

    /**
     * 判断是否应该跳过该日志
     */
    private boolean shouldSkip(ILoggingEvent event) {
        String loggerName = event.getLoggerName().toLowerCase();
        String message = event.getFormattedMessage().toLowerCase();
        
        // 跳过SQL相关日志
        if (loggerName.contains("org.hibernate.sql") || 
            loggerName.contains("org.hibernate.type") ||
            message.contains("hibernate:") ||
            message.contains("select ") ||
            message.contains("insert ") ||
            message.contains("update ") ||
            message.contains("delete ") ||
            message.contains("create table") ||
            message.contains("alter table")) {
            return true;
        }
        
        // 跳过Spring Boot启动相关日志
        if (loggerName.contains("org.springframework.boot") ||
            loggerName.contains("org.springframework.context") ||
            loggerName.contains("org.springframework.beans") ||
            loggerName.contains("org.hibernate.boot") ||
            loggerName.contains("org.hibernate.jpa") ||
            loggerName.contains("org.springframework.orm") ||
            message.contains("started application") ||
            message.contains("beanfactory") ||
            message.contains("obtaining singleton") ||
            message.contains("autowired annotation") ||
            message.contains("initialized jpa")) {
            return true;
        }
        
        // 跳过Hibernate初始化日志
        if ((loggerName.contains("hibernate") || loggerName.contains("org.hibernate")) &&
            (message.contains("dialect") || 
             message.contains("sessionfactory") ||
             message.contains("entitymanagerfactory") ||
             message.contains("hibernate is in classpath"))) {
            return true;
        }
        
        return false;
    }

    private String getFullMessage(ILoggingEvent event) {
        StringBuilder sb = new StringBuilder();
        sb.append("[").append(event.getLevel()).append("] ");
        sb.append("[").append(event.getThreadName()).append("] ");
        sb.append(event.getLoggerName()).append(" - ");
        sb.append(event.getFormattedMessage());
        return sb.toString();
    }

    private String getStackTrace(ILoggingEvent event) {
        if (event.getThrowableProxy() == null) {
            return null;
        }
        
        StringBuilder sb = new StringBuilder();
        sb.append(event.getThrowableProxy().getClassName()).append(": ")
          .append(event.getThrowableProxy().getMessage()).append("\n");
        
        if (event.getThrowableProxy().getStackTraceElementProxyArray() != null) {
            for (int i = 0; i < event.getThrowableProxy().getStackTraceElementProxyArray().length; i++) {
                sb.append("  at ").append(event.getThrowableProxy().getStackTraceElementProxyArray()[i])
                  .append("\n");
            }
        }
        
        return sb.toString();
    }
}

