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

package io.sentry.clientreport;

import java.util.Date;
import io.sentry.DateUtils;
import io.sentry.SentryLogEvent;
import io.sentry.SentryLogEvents;
import io.sentry.protocol.SentrySpan;
import io.sentry.protocol.SentryTransaction;
import io.sentry.DataCategory;
import io.sentry.SentryItemType;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
import io.sentry.SentryEnvelopeItem;
import java.util.ArrayList;
import io.sentry.SentryLevel;
import io.sentry.SentryEnvelope;
import io.sentry.SentryOptions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public final class ClientReportRecorder implements IClientReportRecorder
{
    @NotNull
    private final IClientReportStorage storage;
    @NotNull
    private final SentryOptions options;
    
    public ClientReportRecorder(@NotNull final SentryOptions options) {
        this.options = options;
        this.storage = new AtomicClientReportStorage();
    }
    
    @NotNull
    @Override
    public SentryEnvelope attachReportToEnvelope(@NotNull final SentryEnvelope envelope) {
        final ClientReport clientReport = this.resetCountsAndGenerateClientReport();
        if (clientReport == null) {
            return envelope;
        }
        try {
            this.options.getLogger().log(SentryLevel.DEBUG, "Attaching client report to envelope.", new Object[0]);
            final List<SentryEnvelopeItem> items = new ArrayList<SentryEnvelopeItem>();
            for (final SentryEnvelopeItem item : envelope.getItems()) {
                items.add(item);
            }
            items.add(SentryEnvelopeItem.fromClientReport(this.options.getSerializer(), clientReport));
            return new SentryEnvelope(envelope.getHeader(), items);
        }
        catch (final Throwable e) {
            this.options.getLogger().log(SentryLevel.ERROR, e, "Unable to attach client report to envelope.", new Object[0]);
            return envelope;
        }
    }
    
    @Override
    public void recordLostEnvelope(@NotNull final DiscardReason reason, @Nullable final SentryEnvelope envelope) {
        if (envelope == null) {
            return;
        }
        try {
            for (final SentryEnvelopeItem item : envelope.getItems()) {
                this.recordLostEnvelopeItem(reason, item);
            }
        }
        catch (final Throwable e) {
            this.options.getLogger().log(SentryLevel.ERROR, e, "Unable to record lost envelope.", new Object[0]);
        }
    }
    
    @Override
    public void recordLostEnvelopeItem(@NotNull final DiscardReason reason, @Nullable final SentryEnvelopeItem envelopeItem) {
        if (envelopeItem == null) {
            return;
        }
        try {
            final SentryItemType itemType = envelopeItem.getHeader().getType();
            if (SentryItemType.ClientReport.equals(itemType)) {
                try {
                    final ClientReport clientReport = envelopeItem.getClientReport(this.options.getSerializer());
                    this.restoreCountsFromClientReport(clientReport);
                }
                catch (final Exception e) {
                    this.options.getLogger().log(SentryLevel.ERROR, "Unable to restore counts from previous client report.", new Object[0]);
                }
            }
            else {
                final DataCategory itemCategory = this.categoryFromItemType(itemType);
                if (itemCategory.equals(DataCategory.Transaction)) {
                    final SentryTransaction transaction = envelopeItem.getTransaction(this.options.getSerializer());
                    if (transaction != null) {
                        final List<SentrySpan> spans = transaction.getSpans();
                        this.recordLostEventInternal(reason.getReason(), DataCategory.Span.getCategory(), spans.size() + 1L);
                        this.executeOnDiscard(reason, DataCategory.Span, spans.size() + 1L);
                    }
                    this.recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), 1L);
                    this.executeOnDiscard(reason, itemCategory, 1L);
                }
                else if (itemCategory.equals(DataCategory.LogItem)) {
                    final SentryLogEvents logs = envelopeItem.getLogs(this.options.getSerializer());
                    if (logs != null) {
                        final List<SentryLogEvent> items = logs.getItems();
                        final long count = items.size();
                        this.recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), count);
                        final long logBytes = envelopeItem.getData().length;
                        this.recordLostEventInternal(reason.getReason(), DataCategory.LogByte.getCategory(), logBytes);
                        this.executeOnDiscard(reason, itemCategory, count);
                    }
                    else {
                        this.options.getLogger().log(SentryLevel.ERROR, "Unable to parse lost logs envelope item.", new Object[0]);
                    }
                }
                else {
                    this.recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), 1L);
                    this.executeOnDiscard(reason, itemCategory, 1L);
                }
            }
        }
        catch (final Throwable e2) {
            this.options.getLogger().log(SentryLevel.ERROR, e2, "Unable to record lost envelope item.", new Object[0]);
        }
    }
    
    @Override
    public void recordLostEvent(@NotNull final DiscardReason reason, @NotNull final DataCategory category) {
        this.recordLostEvent(reason, category, 1L);
    }
    
    @Override
    public void recordLostEvent(@NotNull final DiscardReason reason, @NotNull final DataCategory category, final long count) {
        try {
            this.recordLostEventInternal(reason.getReason(), category.getCategory(), count);
            this.executeOnDiscard(reason, category, count);
        }
        catch (final Throwable e) {
            this.options.getLogger().log(SentryLevel.ERROR, e, "Unable to record lost event.", new Object[0]);
        }
    }
    
    private void executeOnDiscard(@NotNull final DiscardReason reason, @NotNull final DataCategory category, @NotNull final Long countToAdd) {
        if (this.options.getOnDiscard() != null) {
            try {
                this.options.getOnDiscard().execute(reason, category, countToAdd);
            }
            catch (final Throwable e) {
                this.options.getLogger().log(SentryLevel.ERROR, "The onDiscard callback threw an exception.", e);
            }
        }
    }
    
    private void recordLostEventInternal(@NotNull final String reason, @NotNull final String category, @NotNull final Long countToAdd) {
        final ClientReportKey key = new ClientReportKey(reason, category);
        this.storage.addCount(key, countToAdd);
    }
    
    @Nullable
    ClientReport resetCountsAndGenerateClientReport() {
        final Date currentDate = DateUtils.getCurrentDateTime();
        final List<DiscardedEvent> discardedEvents = this.storage.resetCountsAndGet();
        if (discardedEvents.isEmpty()) {
            return null;
        }
        return new ClientReport(currentDate, discardedEvents);
    }
    
    private void restoreCountsFromClientReport(@Nullable final ClientReport clientReport) {
        if (clientReport == null) {
            return;
        }
        for (final DiscardedEvent discardedEvent : clientReport.getDiscardedEvents()) {
            this.recordLostEventInternal(discardedEvent.getReason(), discardedEvent.getCategory(), discardedEvent.getQuantity());
        }
    }
    
    private DataCategory categoryFromItemType(final SentryItemType itemType) {
        if (SentryItemType.Event.equals(itemType)) {
            return DataCategory.Error;
        }
        if (SentryItemType.Session.equals(itemType)) {
            return DataCategory.Session;
        }
        if (SentryItemType.Transaction.equals(itemType)) {
            return DataCategory.Transaction;
        }
        if (SentryItemType.UserFeedback.equals(itemType)) {
            return DataCategory.UserReport;
        }
        if (SentryItemType.Feedback.equals(itemType)) {
            return DataCategory.Feedback;
        }
        if (SentryItemType.Profile.equals(itemType)) {
            return DataCategory.Profile;
        }
        if (SentryItemType.ProfileChunk.equals(itemType)) {
            return DataCategory.ProfileChunkUi;
        }
        if (SentryItemType.Attachment.equals(itemType)) {
            return DataCategory.Attachment;
        }
        if (SentryItemType.CheckIn.equals(itemType)) {
            return DataCategory.Monitor;
        }
        if (SentryItemType.ReplayVideo.equals(itemType)) {
            return DataCategory.Replay;
        }
        if (SentryItemType.Log.equals(itemType)) {
            return DataCategory.LogItem;
        }
        if (SentryItemType.Span.equals(itemType)) {
            return DataCategory.Span;
        }
        if (SentryItemType.TraceMetric.equals(itemType)) {
            return DataCategory.TraceMetric;
        }
        return DataCategory.Default;
    }
}
