package com.tzld.piaoquan.api.common;

import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.encoder.Encoder;
import com.aliyun.openservices.aliyun.log.producer.LogProducer;
import com.aliyun.openservices.aliyun.log.producer.Producer;
import com.aliyun.openservices.aliyun.log.producer.ProducerConfig;
import com.aliyun.openservices.aliyun.log.producer.ProjectConfig;
import com.aliyun.openservices.aliyun.log.producer.errors.ProducerException;
import com.aliyun.openservices.log.common.LogItem;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * @author dyp
 */
public class LoghubAppender<E> extends UnsynchronizedAppenderBase<E> {
    private String project;
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String userAgent = "logback";
    protected Encoder<E> encoder;
    protected ProducerConfig producerConfig = new ProducerConfig();
    protected ProjectConfig projectConfig;
    protected Producer producer;
    protected String logStore;
    protected String topic = "";
    protected String source = "";
    protected String timeZone = "UTC";
    protected String timeFormat = "yyyy-MM-dd'T'HH:mmZ";
    protected DateTimeFormatter formatter;
    protected java.time.format.DateTimeFormatter formatter1;
    private String mdcFields;

    public LoghubAppender() {
    }

    public void start() {
        try {
            this.doStart();
        } catch (Exception var2) {
            this.addError("Failed to start LoghubAppender.", var2);
        }

    }

    private void doStart() {
        try {
            this.formatter = DateTimeFormat.forPattern(this.timeFormat).withZone(DateTimeZone.forID(this.timeZone));
        } catch (Exception var2) {
            this.formatter1 = java.time.format.DateTimeFormatter.ofPattern(this.timeFormat).withZone(ZoneId.of(this.timeZone));
        }

        this.producer = this.createProducer();
        super.start();
    }

    public Producer createProducer() {
        this.projectConfig = this.buildProjectConfig();
        Producer producer = new LogProducer(this.producerConfig);
        producer.putProjectConfig(this.projectConfig);
        return producer;
    }

    private ProjectConfig buildProjectConfig() {
        return new ProjectConfig(this.project, this.endpoint, this.accessKeyId, this.accessKeySecret, (String) null, this.userAgent);
    }

    public void stop() {
        try {
            this.doStop();
        } catch (Exception var2) {
            this.addError("Failed to stop LoghubAppender.", var2);
        }

    }

    private void doStop() throws InterruptedException, ProducerException {
        if (this.isStarted()) {
            super.stop();
            this.producer.close();
        }
    }

    public void append(E eventObject) {
        try {
            this.appendEvent(eventObject);
        } catch (Exception var3) {
            this.addError("Failed to append event.", var3);
        }
    }

    private void appendEvent(E eventObject) {
        if (!(eventObject instanceof LoggingEvent)) {
            return;
        }
        LoggingEvent event = (LoggingEvent) eventObject;
        if (event.getMarker() == null) {
            appendPlainEvent(eventObject);
        }
    }

    private void appendPlainEvent(E eventObject) {
        LoggingEvent event = (LoggingEvent) eventObject;
        List<LogItem> logItems = new ArrayList();
        LogItem item = new LogItem();
        logItems.add(item);
        item.SetTime((int) (event.getTimeStamp() / 1000L));
        if (this.formatter != null) {
            DateTime dateTime = new DateTime(event.getTimeStamp());
            item.PushBack("time", dateTime.toString(this.formatter));
        } else {
            Instant instant = Instant.ofEpochMilli(event.getTimeStamp());
            item.PushBack("time", this.formatter1.format(instant));
        }

        item.PushBack("level", event.getLevel().toString());
        item.PushBack("thread", event.getThreadName());
        StackTraceElement[] caller = event.getCallerData();
        if (caller != null && caller.length > 0) {
            item.PushBack("location", caller[0].toString());
        }

        String message = event.getFormattedMessage();
        item.PushBack("message", message);
        IThrowableProxy iThrowableProxy = event.getThrowableProxy();
        if (iThrowableProxy != null) {
            String throwable = this.getExceptionInfo(iThrowableProxy);
            throwable = throwable + this.fullDump(event.getThrowableProxy().getStackTraceElementProxyArray());
            item.PushBack("throwable", throwable);
        }

        if (this.encoder != null) {
            item.PushBack("log", new String(this.encoder.encode(eventObject)));
        }

        Optional.ofNullable(this.mdcFields).ifPresent((f) -> {
            event.getMDCPropertyMap().entrySet().stream().filter((v) -> {
                return Arrays.stream(f.split(",")).anyMatch((i) -> {
                    return i.equals(v.getKey());
                });
            }).forEach((map) -> {
                item.PushBack((String) map.getKey(), (String) map.getValue());
            });
        });

        try {
            this.producer.send(this.projectConfig.getProject(), this.logStore, this.topic, this.source, logItems,
                    new LoghubAppenderCallback(this, this.projectConfig.getProject(), this.logStore,
                            this.topic, this.source, logItems));
        } catch (Exception var9) {
            this.addError("Failed to send log, project=" + this.project + ", logStore=" + this.logStore + ", topic=" + this.topic + ", source=" + this.source + ", logItem=" + logItems, var9);
        }
    }

    public String getTimeFormat() {
        return this.timeFormat;
    }

    public void setTimeFormat(String timeFormat) {
        this.timeFormat = timeFormat;
    }

    private String getExceptionInfo(IThrowableProxy iThrowableProxy) {
        String s = iThrowableProxy.getClassName();
        String message = iThrowableProxy.getMessage();
        return message != null ? s + ": " + message : s;
    }

    private String fullDump(StackTraceElementProxy[] stackTraceElementProxyArray) {
        StringBuilder builder = new StringBuilder();
        StackTraceElementProxy[] var3 = stackTraceElementProxyArray;
        int var4 = stackTraceElementProxyArray.length;

        for (int var5 = 0; var5 < var4; ++var5) {
            StackTraceElementProxy step = var3[var5];
            builder.append(CoreConstants.LINE_SEPARATOR);
            String string = step.toString();
            builder.append('\t').append(string);
            ThrowableProxyUtil.subjoinPackagingData(builder, step);
        }

        return builder.toString();
    }

    public String getLogStore() {
        return this.logStore;
    }

    public void setLogStore(String logStore) {
        this.logStore = logStore;
    }

    public String getTopic() {
        return this.topic;
    }

    public void setTopic(String topic) {
        this.topic = topic;
    }

    public String getSource() {
        return this.source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getTimeZone() {
        return this.timeZone;
    }

    public void setTimeZone(String timeZone) {
        this.timeZone = timeZone;
    }

    public String getProject() {
        return this.project;
    }

    public void setProject(String project) {
        this.project = project;
    }

    public String getEndpoint() {
        return this.endpoint;
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public String getAccessKeyId() {
        return this.accessKeyId;
    }

    public void setAccessKeyId(String accessKeyId) {
        this.accessKeyId = accessKeyId;
    }

    public String getAccessKeySecret() {
        return this.accessKeySecret;
    }

    public void setAccessKeySecret(String accessKeySecret) {
        this.accessKeySecret = accessKeySecret;
    }

    public String getUserAgent() {
        return this.userAgent;
    }

    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }

    public int getTotalSizeInBytes() {
        return this.producerConfig.getTotalSizeInBytes();
    }

    public void setTotalSizeInBytes(int totalSizeInBytes) {
        this.producerConfig.setTotalSizeInBytes(totalSizeInBytes);
    }

    public long getMaxBlockMs() {
        return this.producerConfig.getMaxBlockMs();
    }

    public void setMaxBlockMs(long maxBlockMs) {
        this.producerConfig.setMaxBlockMs(maxBlockMs);
    }

    public int getIoThreadCount() {
        return this.producerConfig.getIoThreadCount();
    }

    public void setIoThreadCount(int ioThreadCount) {
        this.producerConfig.setIoThreadCount(ioThreadCount);
    }

    public int getBatchSizeThresholdInBytes() {
        return this.producerConfig.getBatchSizeThresholdInBytes();
    }

    public void setBatchSizeThresholdInBytes(int batchSizeThresholdInBytes) {
        this.producerConfig.setBatchSizeThresholdInBytes(batchSizeThresholdInBytes);
    }

    public int getBatchCountThreshold() {
        return this.producerConfig.getBatchCountThreshold();
    }

    public void setBatchCountThreshold(int batchCountThreshold) {
        this.producerConfig.setBatchCountThreshold(batchCountThreshold);
    }

    public int getLingerMs() {
        return this.producerConfig.getLingerMs();
    }

    public void setLingerMs(int lingerMs) {
        this.producerConfig.setLingerMs(lingerMs);
    }

    public int getRetries() {
        return this.producerConfig.getRetries();
    }

    public void setRetries(int retries) {
        this.producerConfig.setRetries(retries);
    }

    public int getMaxReservedAttempts() {
        return this.producerConfig.getMaxReservedAttempts();
    }

    public void setMaxReservedAttempts(int maxReservedAttempts) {
        this.producerConfig.setMaxReservedAttempts(maxReservedAttempts);
    }

    public long getBaseRetryBackoffMs() {
        return this.producerConfig.getBaseRetryBackoffMs();
    }

    public void setBaseRetryBackoffMs(long baseRetryBackoffMs) {
        this.producerConfig.setBaseRetryBackoffMs(baseRetryBackoffMs);
    }

    public long getMaxRetryBackoffMs() {
        return this.producerConfig.getMaxRetryBackoffMs();
    }

    public void setMaxRetryBackoffMs(long maxRetryBackoffMs) {
        this.producerConfig.setMaxRetryBackoffMs(maxRetryBackoffMs);
    }

    public Encoder<E> getEncoder() {
        return this.encoder;
    }

    public void setEncoder(Encoder<E> encoder) {
        this.encoder = encoder;
    }

    public void setMdcFields(String mdcFields) {
        this.mdcFields = mdcFields;
    }
}
