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

package io.sentry;

import io.sentry.util.LogUtils;
import io.sentry.hints.Flushable;
import io.sentry.protocol.SentryId;
import io.sentry.util.SampleRateUtils;
import java.util.Iterator;
import io.sentry.hints.Resettable;
import io.sentry.hints.SubmissionResult;
import io.sentry.protocol.SentryTransaction;
import java.io.Reader;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.ByteArrayInputStream;
import io.sentry.util.CollectionUtils;
import org.jetbrains.annotations.Nullable;
import io.sentry.util.HintUtils;
import io.sentry.hints.Retryable;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.File;
import io.sentry.util.Objects;
import org.jetbrains.annotations.NotNull;
import java.nio.charset.Charset;
import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public final class OutboxSender extends DirectoryProcessor implements IEnvelopeSender
{
    private static final Charset UTF_8;
    @NotNull
    private final IScopes scopes;
    @NotNull
    private final IEnvelopeReader envelopeReader;
    @NotNull
    private final ISerializer serializer;
    @NotNull
    private final ILogger logger;
    
    public OutboxSender(@NotNull final IScopes scopes, @NotNull final IEnvelopeReader envelopeReader, @NotNull final ISerializer serializer, @NotNull final ILogger logger, final long flushTimeoutMillis, final int maxQueueSize) {
        super(scopes, logger, flushTimeoutMillis, maxQueueSize);
        this.scopes = Objects.requireNonNull(scopes, "Scopes are required.");
        this.envelopeReader = Objects.requireNonNull(envelopeReader, "Envelope reader is required.");
        this.serializer = Objects.requireNonNull(serializer, "Serializer is required.");
        this.logger = Objects.requireNonNull(logger, "Logger is required.");
    }
    
    @Override
    protected void processFile(@NotNull final File file, @NotNull final Hint hint) {
        Objects.requireNonNull(file, "File is required.");
        if (!this.isRelevantFileName(file.getName())) {
            this.logger.log(SentryLevel.DEBUG, "File '%s' should be ignored.", file.getAbsolutePath());
            return;
        }
        try (final InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
            final SentryEnvelope envelope = this.envelopeReader.read(stream);
            if (envelope == null) {
                this.logger.log(SentryLevel.ERROR, "Stream from path %s resulted in a null envelope.", file.getAbsolutePath());
            }
            else {
                this.processEnvelope(envelope, hint);
                this.logger.log(SentryLevel.DEBUG, "File '%s' is done.", file.getAbsolutePath());
            }
        }
        catch (final IOException e) {
            this.logger.log(SentryLevel.ERROR, "Error processing envelope.", e);
        }
        finally {
            HintUtils.runIfHasTypeLogIfNot(hint, Retryable.class, this.logger, retryable -> {
                if (!retryable.isRetry()) {
                    try {
                        if (!file.delete()) {
                            this.logger.log(SentryLevel.ERROR, "Failed to delete: %s", file.getAbsolutePath());
                        }
                    }
                    catch (final RuntimeException e2) {
                        this.logger.log(SentryLevel.ERROR, e2, "Failed to delete: %s", file.getAbsolutePath());
                    }
                }
            });
        }
    }
    
    @Override
    protected boolean isRelevantFileName(@Nullable final String fileName) {
        return fileName != null && !fileName.startsWith("session") && !fileName.startsWith("previous_session") && !fileName.startsWith("startup_crash");
    }
    
    @Override
    public void processEnvelopeFile(@NotNull final String path, @NotNull final Hint hint) {
        Objects.requireNonNull(path, "Path is required.");
        this.processFile(new File(path), hint);
    }
    
    private void processEnvelope(@NotNull final SentryEnvelope envelope, @NotNull final Hint hint) throws IOException {
        this.logger.log(SentryLevel.DEBUG, "Processing Envelope with %d item(s)", CollectionUtils.size(envelope.getItems()));
        int currentItem = 0;
        for (final SentryEnvelopeItem item : envelope.getItems()) {
            ++currentItem;
            if (item.getHeader() == null) {
                this.logger.log(SentryLevel.ERROR, "Item %d has no header", currentItem);
            }
            else {
                if (SentryItemType.Event.equals(item.getHeader().getType())) {
                    try (final Reader eventReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(item.getData()), OutboxSender.UTF_8))) {
                        final SentryEvent event = this.serializer.deserialize(eventReader, SentryEvent.class);
                        if (event == null) {
                            this.logEnvelopeItemNull(item, currentItem);
                        }
                        else {
                            if (event.getSdk() != null) {
                                HintUtils.setIsFromHybridSdk(hint, event.getSdk().getName());
                            }
                            if (envelope.getHeader().getEventId() != null && !envelope.getHeader().getEventId().equals(event.getEventId())) {
                                this.logUnexpectedEventId(envelope, event.getEventId(), currentItem);
                                eventReader.close();
                                continue;
                            }
                            this.scopes.captureEvent(event, hint);
                            this.logItemCaptured(currentItem);
                            if (!this.waitFlush(hint)) {
                                this.logTimeout(event.getEventId());
                                eventReader.close();
                                break;
                            }
                        }
                    }
                    catch (final Throwable e) {
                        this.logger.log(SentryLevel.ERROR, "Item failed to process.", e);
                    }
                }
                else if (SentryItemType.Transaction.equals(item.getHeader().getType())) {
                    try (final Reader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(item.getData()), OutboxSender.UTF_8))) {
                        final SentryTransaction transaction = this.serializer.deserialize(reader, SentryTransaction.class);
                        if (transaction == null) {
                            this.logEnvelopeItemNull(item, currentItem);
                        }
                        else {
                            if (envelope.getHeader().getEventId() != null && !envelope.getHeader().getEventId().equals(transaction.getEventId())) {
                                this.logUnexpectedEventId(envelope, transaction.getEventId(), currentItem);
                                reader.close();
                                continue;
                            }
                            final TraceContext traceContext = envelope.getHeader().getTraceContext();
                            if (transaction.getContexts().getTrace() != null) {
                                transaction.getContexts().getTrace().setSamplingDecision(this.extractSamplingDecision(traceContext));
                            }
                            this.scopes.captureTransaction(transaction, traceContext, hint);
                            this.logItemCaptured(currentItem);
                            if (!this.waitFlush(hint)) {
                                this.logTimeout(transaction.getEventId());
                                reader.close();
                                break;
                            }
                        }
                    }
                    catch (final Throwable e) {
                        this.logger.log(SentryLevel.ERROR, "Item failed to process.", e);
                    }
                }
                else {
                    final SentryEnvelope newEnvelope = new SentryEnvelope(envelope.getHeader().getEventId(), envelope.getHeader().getSdkVersion(), item);
                    this.scopes.captureEnvelope(newEnvelope, hint);
                    this.logger.log(SentryLevel.DEBUG, "%s item %d is being captured.", item.getHeader().getType().getItemType(), currentItem);
                    if (!this.waitFlush(hint)) {
                        this.logger.log(SentryLevel.WARNING, "Timed out waiting for item type submission: %s", item.getHeader().getType().getItemType());
                        break;
                    }
                }
                final Object sentrySdkHint = HintUtils.getSentrySdkHint(hint);
                if (sentrySdkHint instanceof SubmissionResult && !((SubmissionResult)sentrySdkHint).isSuccess()) {
                    this.logger.log(SentryLevel.WARNING, "Envelope had a failed capture at item %d. No more items will be sent.", currentItem);
                    break;
                }
                HintUtils.runIfHasType(hint, Resettable.class, resettable -> resettable.reset());
            }
        }
    }
    
    @NotNull
    private TracesSamplingDecision extractSamplingDecision(@Nullable final TraceContext traceContext) {
        if (traceContext != null) {
            final String sampleRateString = traceContext.getSampleRate();
            if (sampleRateString != null) {
                try {
                    final Double sampleRate = Double.parseDouble(sampleRateString);
                    if (SampleRateUtils.isValidTracesSampleRate(sampleRate, false)) {
                        final String sampleRandString = traceContext.getSampleRand();
                        if (sampleRandString != null) {
                            final Double sampleRand = Double.parseDouble(sampleRandString);
                            if (SampleRateUtils.isValidTracesSampleRate(sampleRand, false)) {
                                return new TracesSamplingDecision(true, sampleRate, sampleRand);
                            }
                        }
                        return SampleRateUtils.backfilledSampleRand(new TracesSamplingDecision(true, sampleRate));
                    }
                    this.logger.log(SentryLevel.ERROR, "Invalid sample rate parsed from TraceContext: %s", sampleRateString);
                }
                catch (final Exception e) {
                    this.logger.log(SentryLevel.ERROR, "Unable to parse sample rate from TraceContext: %s", sampleRateString);
                }
            }
        }
        return new TracesSamplingDecision(true);
    }
    
    private void logEnvelopeItemNull(@NotNull final SentryEnvelopeItem item, final int itemIndex) {
        this.logger.log(SentryLevel.ERROR, "Item %d of type %s returned null by the parser.", itemIndex, item.getHeader().getType());
    }
    
    private void logUnexpectedEventId(@NotNull final SentryEnvelope envelope, @Nullable final SentryId eventId, final int itemIndex) {
        this.logger.log(SentryLevel.ERROR, "Item %d of has a different event id (%s) to the envelope header (%s)", itemIndex, envelope.getHeader().getEventId(), eventId);
    }
    
    private void logItemCaptured(final int itemIndex) {
        this.logger.log(SentryLevel.DEBUG, "Item %d is being captured.", itemIndex);
    }
    
    private void logTimeout(@Nullable final SentryId eventId) {
        this.logger.log(SentryLevel.WARNING, "Timed out waiting for event id submission: %s", eventId);
    }
    
    private boolean waitFlush(@NotNull final Hint hint) {
        final Object sentrySdkHint = HintUtils.getSentrySdkHint(hint);
        if (sentrySdkHint instanceof Flushable) {
            return ((Flushable)sentrySdkHint).waitFlush();
        }
        LogUtils.logNotInstanceOf(Flushable.class, sentrySdkHint, this.logger);
        return true;
    }
    
    static {
        UTF_8 = Charset.forName("UTF-8");
    }
}
