package com.qmth.boot.core.logger.context;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.util.FileSize;
import com.qmth.boot.core.logger.config.LoggerProperties;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

@Component
public class LoggerContextService {

    private static final String TIME_ROLLING_PATTERN = ".%d{yyyy-MM-dd}-%i.log";

    private static final String ROLLING_FILE_APPENDER = "RollingFileAppender";

    @Resource
    private LoggerProperties loggerProperties;

    @PostConstruct
    public void init() {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME);
        rootLogger.setLevel(Level.toLevel(loggerProperties.getRootLevel(), Level.INFO));

        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(context);
        encoder.setCharset(StandardCharsets.UTF_8);
        encoder.setPattern(loggerProperties.getPattern());
        encoder.start();

        if (StringUtils.isNotBlank(loggerProperties.getFilePath())) {
            RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>();
            appender.setContext(context);
            appender.setEncoder(encoder);
            appender.setFile(loggerProperties.getFilePath());
            appender.setName(ROLLING_FILE_APPENDER);
            appender.setAppend(true);

            String filePattern;
            String basePath = loggerProperties.getFilePath();
            if (basePath.endsWith(".log")) {
                filePattern = basePath.substring(0, basePath.length() - 4) + TIME_ROLLING_PATTERN;
            } else {
                filePattern = basePath + TIME_ROLLING_PATTERN;
            }
            SizeAndTimeBasedRollingPolicy<ILoggingEvent> policy = new SizeAndTimeBasedRollingPolicy<>();
            policy.setFileNamePattern(filePattern);
            policy.setMaxHistory(loggerProperties.getFileMaxHistory());
            policy.setMaxFileSize(new FileSize(loggerProperties.getFileMaxSize().toBytes()));
            policy.setTotalSizeCap(new FileSize(loggerProperties.getTotalMaxSize().toBytes()));
            policy.setParent(appender);
            policy.setContext(context);
            policy.start();

            appender.setRollingPolicy(policy);
            appender.start();
            rootLogger.addAppender(appender);
        }

        boolean hasConsole = false;
        Iterator<Appender<ILoggingEvent>> iterator = rootLogger.iteratorForAppenders();
        while (iterator.hasNext()) {
            Appender<ILoggingEvent> appender = iterator.next();
            if (appender instanceof ConsoleAppender) {
                ConsoleAppender<ILoggingEvent> consoleAppender = (ConsoleAppender<ILoggingEvent>) appender;
                consoleAppender.setEncoder(encoder);
                consoleAppender.start();
                hasConsole = true;
            }
        }
        if (!hasConsole) {
            ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
            appender.setContext(context);
            appender.setEncoder(encoder);
            appender.setName("Console");
            appender.setTarget("System.out");
            appender.start();
            rootLogger.addAppender(appender);
        }
    }

    public void setLevel(String name, Level level) {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        ch.qos.logback.classic.Logger logger = context.getLogger(name);
        logger.setLevel(level);
    }

    public void setLevel(Class<?> clazz, Level level) {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        ch.qos.logback.classic.Logger logger = context.getLogger(clazz);
        logger.setLevel(level);
    }

}
