// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.logger.sentry;

import io.sentry.SentryIntegrationPackageStorage;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import io.sentry.exception.ExceptionMechanismException;
import io.sentry.protocol.Mechanism;
import io.sentry.protocol.Message;
import java.util.Date;
import io.sentry.SentryEvent;
import io.sentry.Breadcrumb;
import javax.annotation.Nullable;
import io.sentry.SentryLevel;
import java.util.logging.LogManager;
import io.sentry.SentryLogLevel;
import io.sentry.logger.SentryLogParameters;
import io.sentry.SentryAttributes;
import io.sentry.SentryAttribute;
import io.sentry.Sentry;
import io.sentry.Hint;
import io.sentry.ScopesAdapter;
import java.util.logging.LogRecord;
import java.util.logging.Filter;
import javax.annotation.Nonnull;
import java.util.logging.Level;
import io.sentry.IScopes;
import java.util.logging.Handler;

public class HytaleSentryHandler extends Handler
{
    public static final String MECHANISM_TYPE = "JulSentryHandler";
    public static final String THREAD_ID = "thread_id";
    private final IScopes scope;
    private boolean printfStyle;
    @Nonnull
    private Level minimumBreadcrumbLevel;
    @Nonnull
    private Level minimumEventLevel;
    @Nonnull
    private Level minimumLevel;
    
    public HytaleSentryHandler(@Nonnull final IScopes scope) {
        this(scope, true);
    }
    
    HytaleSentryHandler(@Nonnull final IScopes scope, final boolean configureFromLogManager) {
        this.minimumBreadcrumbLevel = Level.INFO;
        this.minimumEventLevel = Level.SEVERE;
        this.minimumLevel = Level.INFO;
        this.setFilter(new DropSentryFilter());
        if (configureFromLogManager) {
            this.retrieveProperties();
        }
        this.scope = scope;
    }
    
    @Override
    public void publish(@Nonnull final LogRecord record) {
        if (!this.isLoggable(record)) {
            return;
        }
        try {
            if (ScopesAdapter.getInstance().getOptions().getLogs().isEnabled() && record.getLevel().intValue() >= this.minimumLevel.intValue()) {
                this.captureLog(record);
            }
            if (record.getLevel().intValue() >= this.minimumEventLevel.intValue()) {
                final Hint hint = new Hint();
                hint.set("syntheticException", record);
                this.scope.captureEvent(this.createEvent(record), hint);
            }
            if (record.getLevel().intValue() >= this.minimumBreadcrumbLevel.intValue()) {
                final Hint hint = new Hint();
                hint.set("jul:logRecord", record);
                Sentry.addBreadcrumb(this.createBreadcrumb(record), hint);
            }
        }
        catch (final RuntimeException e) {
            this.reportError("An exception occurred while creating a new event in Sentry", e, 1);
        }
    }
    
    protected void captureLog(@Nonnull final LogRecord loggingEvent) {
        final SentryLogLevel sentryLevel = toSentryLogLevel(loggingEvent.getLevel());
        final Object[] arguments = loggingEvent.getParameters();
        final SentryAttributes attributes = SentryAttributes.of(new SentryAttribute[0]);
        String message = loggingEvent.getMessage();
        if (loggingEvent.getResourceBundle() != null && loggingEvent.getResourceBundle().containsKey(loggingEvent.getMessage())) {
            message = loggingEvent.getResourceBundle().getString(loggingEvent.getMessage());
        }
        final String formattedMessage = this.maybeFormatted(arguments, message);
        if (!formattedMessage.equals(message)) {
            attributes.add(SentryAttribute.stringAttribute("sentry.message.template", message));
        }
        final SentryLogParameters params = SentryLogParameters.create(attributes);
        params.setOrigin("auto.log.jul.hytale");
        Sentry.logger().log(sentryLevel, params, formattedMessage, arguments);
    }
    
    @Nonnull
    private String maybeFormatted(@Nonnull final Object[] arguments, @Nonnull final String message) {
        if (arguments != null) {
            try {
                return this.formatMessage(message, arguments);
            }
            catch (final RuntimeException ex) {}
        }
        return message;
    }
    
    private void retrieveProperties() {
        final LogManager manager = LogManager.getLogManager();
        final String className = HytaleSentryHandler.class.getName();
        this.setPrintfStyle(Boolean.parseBoolean(manager.getProperty(className + ".printfStyle")));
        this.setLevel(this.parseLevelOrDefault(manager.getProperty(className + ".level")));
        final String minimumBreadCrumbLevel = manager.getProperty(className + ".minimumBreadcrumbLevel");
        if (minimumBreadCrumbLevel != null) {
            this.setMinimumBreadcrumbLevel(this.parseLevelOrDefault(minimumBreadCrumbLevel));
        }
        final String minimumEventLevel = manager.getProperty(className + ".minimumEventLevel");
        if (minimumEventLevel != null) {
            this.setMinimumEventLevel(this.parseLevelOrDefault(minimumEventLevel));
        }
        final String minimumLevel = manager.getProperty(className + ".minimumLevel");
        if (minimumLevel != null) {
            this.setMinimumLevel(this.parseLevelOrDefault(minimumLevel));
        }
    }
    
    @Nullable
    private static SentryLevel formatLevel(@Nonnull final Level level) {
        if (level.intValue() >= Level.SEVERE.intValue()) {
            return SentryLevel.ERROR;
        }
        if (level.intValue() >= Level.WARNING.intValue()) {
            return SentryLevel.WARNING;
        }
        if (level.intValue() >= Level.INFO.intValue()) {
            return SentryLevel.INFO;
        }
        if (level.intValue() >= Level.ALL.intValue()) {
            return SentryLevel.DEBUG;
        }
        return null;
    }
    
    @Nonnull
    private static SentryLogLevel toSentryLogLevel(@Nonnull final Level level) {
        if (level.intValue() >= Level.SEVERE.intValue()) {
            return SentryLogLevel.ERROR;
        }
        if (level.intValue() >= Level.WARNING.intValue()) {
            return SentryLogLevel.WARN;
        }
        if (level.intValue() >= Level.INFO.intValue()) {
            return SentryLogLevel.INFO;
        }
        if (level.intValue() >= Level.FINE.intValue()) {
            return SentryLogLevel.DEBUG;
        }
        return SentryLogLevel.TRACE;
    }
    
    @Nonnull
    private Level parseLevelOrDefault(@Nonnull final String levelName) {
        try {
            return Level.parse(levelName.trim());
        }
        catch (final RuntimeException e) {
            return Level.WARNING;
        }
    }
    
    @Nonnull
    private Breadcrumb createBreadcrumb(@Nonnull final LogRecord record) {
        final Breadcrumb breadcrumb = new Breadcrumb();
        breadcrumb.setLevel(formatLevel(record.getLevel()));
        breadcrumb.setCategory(record.getLoggerName());
        if (record.getParameters() != null) {
            try {
                breadcrumb.setMessage(this.formatMessage(record.getMessage(), record.getParameters()));
            }
            catch (final RuntimeException e) {
                breadcrumb.setMessage(record.getMessage());
            }
        }
        else {
            breadcrumb.setMessage(record.getMessage());
        }
        return breadcrumb;
    }
    
    @Nonnull
    SentryEvent createEvent(@Nonnull final LogRecord record) {
        final SentryEvent event = new SentryEvent(new Date(record.getMillis()));
        event.setLevel(formatLevel(record.getLevel()));
        event.setLogger(record.getLoggerName());
        final Message sentryMessage = new Message();
        sentryMessage.setParams(this.toParams(record.getParameters()));
        String message = record.getMessage();
        if (record.getResourceBundle() != null && record.getResourceBundle().containsKey(record.getMessage())) {
            message = record.getResourceBundle().getString(record.getMessage());
        }
        sentryMessage.setMessage(message);
        if (record.getParameters() != null) {
            try {
                sentryMessage.setFormatted(this.formatMessage(message, record.getParameters()));
            }
            catch (final RuntimeException ex) {}
        }
        event.setMessage(sentryMessage);
        final Throwable throwable = record.getThrown();
        if (throwable != null) {
            final Mechanism mechanism = new Mechanism();
            mechanism.setType("JulSentryHandler");
            final Throwable mechanismException = new ExceptionMechanismException(mechanism, throwable, Thread.currentThread());
            event.setThrowable(mechanismException);
        }
        event.setExtra("thread_id", String.valueOf(record.getLongThreadID()));
        return event;
    }
    
    @Nonnull
    private List<String> toParams(@Nullable final Object[] arguments) {
        final List<String> result = new ArrayList<String>();
        if (arguments != null) {
            for (final Object argument : arguments) {
                if (argument != null) {
                    result.add(argument.toString());
                }
            }
        }
        return result;
    }
    
    @Nonnull
    private String formatMessage(@Nonnull final String message, @Nullable final Object[] parameters) {
        String formatted;
        if (this.printfStyle) {
            formatted = String.format(message, parameters);
        }
        else {
            formatted = MessageFormat.format(message, parameters);
        }
        return formatted;
    }
    
    @Override
    public void flush() {
    }
    
    @Override
    public void close() throws SecurityException {
        try {
            Sentry.close();
        }
        catch (final RuntimeException e) {
            this.reportError("An exception occurred while closing the Sentry connection", e, 3);
        }
    }
    
    public void setPrintfStyle(final boolean printfStyle) {
        this.printfStyle = printfStyle;
    }
    
    public void setMinimumBreadcrumbLevel(@Nullable final Level minimumBreadcrumbLevel) {
        if (minimumBreadcrumbLevel != null) {
            this.minimumBreadcrumbLevel = minimumBreadcrumbLevel;
        }
    }
    
    @Nonnull
    public Level getMinimumBreadcrumbLevel() {
        return this.minimumBreadcrumbLevel;
    }
    
    public void setMinimumEventLevel(@Nullable final Level minimumEventLevel) {
        if (minimumEventLevel != null) {
            this.minimumEventLevel = minimumEventLevel;
        }
    }
    
    @Nonnull
    public Level getMinimumEventLevel() {
        return this.minimumEventLevel;
    }
    
    public void setMinimumLevel(@Nullable final Level minimumLevel) {
        if (minimumLevel != null) {
            this.minimumLevel = minimumLevel;
        }
    }
    
    @Nonnull
    public Level getMinimumLevel() {
        return this.minimumLevel;
    }
    
    public boolean isPrintfStyle() {
        return this.printfStyle;
    }
    
    static {
        SentryIntegrationPackageStorage.getInstance().addPackage("maven:io.sentry:sentry-jul", "8.29.0");
    }
    
    private static final class DropSentryFilter implements Filter
    {
        @Override
        public boolean isLoggable(@Nonnull final LogRecord record) {
            final String loggerName = record.getLoggerName();
            return loggerName == null || !loggerName.startsWith("server.io.sentry") || loggerName.startsWith("server.io.sentry.samples");
        }
    }
}
