package com.ysu.zyw.tc.components.commons.logger;

import com.google.common.base.Throwables;
import com.ysu.zyw.tc.base.ex.TcException;
import com.ysu.zyw.tc.base.utils.TcFormatUtils;
import lombok.*;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;

import java.util.Date;
import java.util.Objects;

import static com.google.common.base.Preconditions.checkNotNull;

@Slf4j
public abstract class TcAbstractExtensionLogger implements TcExtensionLogger, InitializingBean {

    private static final String EXTENSION_LOGGER_LEVEL_ENV_VARIABLE = "extensionLoggerLevel";

    private boolean traceEnabled = false;

    private boolean debugEnabled = false;

    private boolean infoEnabled = false;

    private boolean warnEnabled = false;

    private boolean errorEnabled = false;

    @Getter
    @Setter
    private TcLogLevelEnum springConfigFileLevel = TcLogLevelEnum.INFO;

    @Override
    public boolean isTraceEnabled() {
        return traceEnabled;
    }

    @Override
    public boolean isDebugEnabled() {
        return debugEnabled;
    }

    @Override
    public boolean isInfoEnabled() {
        return infoEnabled;
    }

    @Override
    public boolean isWarnEnabled() {
        return warnEnabled;
    }

    @Override
    public boolean isErrorEnabled() {
        return errorEnabled;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        String extensionLoggerLevel = System.getProperty(EXTENSION_LOGGER_LEVEL_ENV_VARIABLE);
        TcLogLevelEnum logLevel = springConfigFileLevel;
        if (StringUtils.isNotEmpty(extensionLoggerLevel)) {
            try {
                logLevel = TcLogLevelEnum.valueOf(extensionLoggerLevel);
            } catch (IllegalArgumentException e) {
                // no match log level, fast failed.
                throw new TcException("extension logger only support [trace,debug,info,warn,error] system properties",
                        e);
            }
        }
        checkNotNull(logLevel);
        switch (logLevel) {
            case TRACE:
                traceEnabled = true;
                // no break, pass through
            case DEBUG:
                debugEnabled = true;
                // no break, pass through
            case INFO:
                infoEnabled = true;
                // no break, pass through
            case WARN:
                warnEnabled = true;
                // no break, pass through
            case ERROR:
                errorEnabled = true;
                // no break, pass through
        }
        log.info("initializing extension logger with trace-{} debug-{} info-{} warn-{} error-{}",
                traceEnabled, debugEnabled, infoEnabled, warnEnabled, errorEnabled);
    }

    @Data
    @Accessors(chain = true)
    @NoArgsConstructor
    @AllArgsConstructor
    public static class TcExtensionLog {

        private Integer id;

        private String uniqueKey;

        private String level;

        private Date date;

        private String logger;

        private String thread;

        private String file;

        private Integer line;

        private String msg;

        private String exception;

    }

    protected TcExtensionLog convert(String uniqueKey,
                                     TcLogLevelEnum tcLogLevelEnum,
                                     Class<?> clazz,
                                     String format,
                                     Object... arguments) {
        StackTraceElement ste = tryGetLoggerCallerStackTraceElement();
        // the last arguments may be a throwable
        boolean isLastArgumentInstanceOfThrowable = ArrayUtils.isNotEmpty(arguments)
                && arguments[arguments.length - 1] instanceof Throwable;
        Throwable t = isLastArgumentInstanceOfThrowable ? (Throwable) arguments[arguments.length - 1] : null;
        return new TcExtensionLog()
                // auto_increment
                .setId(null)
                .setUniqueKey(uniqueKey)
                .setLevel(tcLogLevelEnum.name())
                .setDate(new Date())
                .setLogger(clazz.toString())
                .setThread(Thread.currentThread().getName())
                .setFile(tryGetFilename(ste))
                .setLine(tryGetLineNumber(ste))
                // the last arguments may be a throwable
                .setMsg(tryFormatMsg(format, arguments))
                .setException(tryFormatException(t));
    }

    protected TcExtensionLog convert(String uniqueKey,
                                     TcLogLevelEnum tcLogLevelEnum,
                                     Class<?> clazz,
                                     String msg,
                                     Throwable t) {
        StackTraceElement ste = tryGetLoggerCallerStackTraceElement();
        return new TcExtensionLog()
                // auto_increment
                .setId(null)
                .setUniqueKey(uniqueKey)
                .setLevel(tcLogLevelEnum.name())
                .setDate(new Date())
                .setLogger(clazz.toString())
                .setThread(Thread.currentThread().getName())
                .setFile(tryGetFilename(ste))
                .setLine(tryGetLineNumber(ste))
                .setMsg(msg)
                .setException(tryFormatException(t));
    }

    protected StackTraceElement tryGetLoggerCallerStackTraceElement() {
        return Thread.currentThread().getStackTrace()[1];
    }

    protected String tryGetFilename(StackTraceElement ste) {
        return ste.getFileName();
    }

    protected int tryGetLineNumber(StackTraceElement ste) {
        return ste.getLineNumber();
    }

    protected String tryFormatMsg(String format, Object... arguments) {
        if (ArrayUtils.isNotEmpty(arguments)) {
            return TcFormatUtils.format(format, arguments);
        } else {
            return format;
        }
    }

    protected String tryFormatException(Throwable t) {
        return Objects.isNull(t)
                ? null : t.getMessage() + "\n" + Throwables.getStackTraceAsString(Throwables.getRootCause(t));
    }

}

@@ -3570,16 +3570,66 @@
 format,%0A
+                                     Throwable t,%0A
         
@@ -3755,325 +3755,8 @@
 ();%0A
-        // the last arguments may be a throwable%0A        boolean isLastArgumentInstanceOfThrowable = ArrayUtils.isNotEmpty(arguments)%0A                && arguments%5Barguments.length - 1%5D instanceof Throwable;%0A        Throwable t = isLastArgumentInstanceOfThrowable ? (Throwable) arguments%5Barguments.length - 1%5D : null;%0A
     
@@ -4356,872 +4356,8 @@
  %7D%0A%0A
-    protected TcExtensionLog convert(String uniqueKey,%0A                                     TcLogLevelEnum tcLogLevelEnum,%0A                                     Class%3C?%3E clazz,%0A                                     String msg,%0A                                     Throwable t) %7B%0A        StackTraceElement ste = tryGetLoggerCallerStackTraceElement();%0A        return new TcExtensionLog()%0A                // auto_increment%0A                .setId(null)%0A                .setUniqueKey(uniqueKey)%0A                .setLevel(tcLogLevelEnum.name())%0A                .setDate(new Date())%0A                .setLogger(clazz.toString())%0A                .setThread(Thread.currentThread().getName())%0A                .setFile(tryGetFilename(ste))%0A                .setLine(tryGetLineNumber(ste))%0A                .setMsg(msg)%0A                .setException(tryFormatException(t));%0A    %7D%0A%0A
     
@@ -5141,10 +5141,364 @@
 %0A    %7D%0A%0A
+    protected Throwable tryGetLastArgumentsInstanceOfThrowable(Object... arguments) %7B%0A        boolean isLastArgumentInstanceOfThrowable = ArrayUtils.isNotEmpty(arguments)%0A                && arguments%5Barguments.length - 1%5D instanceof Throwable;%0A        return isLastArgumentInstanceOfThrowable ? (Throwable) arguments%5Barguments.length - 1%5D : null;%0A    %7D%0A%0A
 %7D%0A

optimize code