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

package io.sentry.cache;

import java.util.List;
import java.util.ArrayList;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.Reader;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.ByteArrayInputStream;
import io.sentry.SentryItemType;
import org.jetbrains.annotations.Nullable;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.util.Iterator;
import io.sentry.Session;
import io.sentry.SentryEnvelope;
import java.io.IOException;
import io.sentry.SentryEnvelopeItem;
import io.sentry.clientreport.DiscardReason;
import java.util.Arrays;
import io.sentry.SentryLevel;
import io.sentry.util.Objects;
import java.io.File;
import io.sentry.ISerializer;
import io.sentry.util.LazyEvaluator;
import org.jetbrains.annotations.NotNull;
import io.sentry.SentryOptions;
import java.nio.charset.Charset;

abstract class CacheStrategy
{
    protected static final Charset UTF_8;
    @NotNull
    protected SentryOptions options;
    @NotNull
    protected final LazyEvaluator<ISerializer> serializer;
    @NotNull
    protected final File directory;
    private final int maxSize;
    
    CacheStrategy(@NotNull final SentryOptions options, @NotNull final String directoryPath, final int maxSize) {
        this.serializer = new LazyEvaluator<ISerializer>(() -> this.options.getSerializer());
        Objects.requireNonNull(directoryPath, "Directory is required.");
        this.options = Objects.requireNonNull(options, "SentryOptions is required.");
        this.directory = new File(directoryPath);
        this.maxSize = maxSize;
    }
    
    protected boolean isDirectoryValid() {
        if (!this.directory.isDirectory() || !this.directory.canWrite() || !this.directory.canRead()) {
            this.options.getLogger().log(SentryLevel.ERROR, "The directory for caching files is inaccessible.: %s", this.directory.getAbsolutePath());
            return false;
        }
        return true;
    }
    
    private void sortFilesOldestToNewest(@NotNull final File[] files) {
        if (files.length > 1) {
            Arrays.sort(files, (f1, f2) -> Long.compare(f1.lastModified(), f2.lastModified()));
        }
    }
    
    protected void rotateCacheIfNeeded(@NotNull final File[] files) {
        final int length = files.length;
        if (length >= this.maxSize) {
            this.options.getLogger().log(SentryLevel.WARNING, "Cache folder if full (respecting maxSize). Rotating files", new Object[0]);
            final int totalToBeDeleted = length - this.maxSize + 1;
            this.sortFilesOldestToNewest(files);
            final File[] notDeletedFiles = Arrays.copyOfRange(files, totalToBeDeleted, length);
            for (final File file : files) {
                this.moveInitFlagIfNecessary(file, notDeletedFiles);
                if (!file.delete()) {
                    this.options.getLogger().log(SentryLevel.WARNING, "File can't be deleted: %s", file.getAbsolutePath());
                }
            }
        }
    }
    
    private void moveInitFlagIfNecessary(@NotNull final File currentFile, @NotNull final File[] notDeletedFiles) {
        final SentryEnvelope currentEnvelope = this.readEnvelope(currentFile);
        if (currentEnvelope == null || !this.isValidEnvelope(currentEnvelope)) {
            return;
        }
        this.options.getClientReportRecorder().recordLostEnvelope(DiscardReason.CACHE_OVERFLOW, currentEnvelope);
        final Session currentSession = this.getFirstSession(currentEnvelope);
        if (currentSession == null || !this.isValidSession(currentSession)) {
            return;
        }
        final Boolean currentSessionInit = currentSession.getInit();
        if (currentSessionInit == null || !currentSessionInit) {
            return;
        }
        for (final File notDeletedFile : notDeletedFiles) {
            final SentryEnvelope envelope = this.readEnvelope(notDeletedFile);
            if (envelope != null) {
                if (this.isValidEnvelope(envelope)) {
                    SentryEnvelopeItem newSessionItem = null;
                    final Iterator<SentryEnvelopeItem> itemsIterator = envelope.getItems().iterator();
                    while (itemsIterator.hasNext()) {
                        final SentryEnvelopeItem envelopeItem = itemsIterator.next();
                        if (!this.isSessionType(envelopeItem)) {
                            continue;
                        }
                        final Session session = this.readSession(envelopeItem);
                        if (session == null) {
                            continue;
                        }
                        if (!this.isValidSession(session)) {
                            continue;
                        }
                        final Boolean init = session.getInit();
                        if (init != null && init) {
                            this.options.getLogger().log(SentryLevel.ERROR, "Session %s has 2 times the init flag.", currentSession.getSessionId());
                            return;
                        }
                        if (currentSession.getSessionId() != null && currentSession.getSessionId().equals(session.getSessionId())) {
                            session.setInitAsTrue();
                            try {
                                newSessionItem = SentryEnvelopeItem.fromSession(this.serializer.getValue(), session);
                                itemsIterator.remove();
                            }
                            catch (final IOException e) {
                                this.options.getLogger().log(SentryLevel.ERROR, e, "Failed to create new envelope item for the session %s", currentSession.getSessionId());
                            }
                            break;
                        }
                    }
                    if (newSessionItem != null) {
                        final SentryEnvelope newEnvelope = this.buildNewEnvelope(envelope, newSessionItem);
                        final long notDeletedFileTimestamp = notDeletedFile.lastModified();
                        if (!notDeletedFile.delete()) {
                            this.options.getLogger().log(SentryLevel.WARNING, "File can't be deleted: %s", notDeletedFile.getAbsolutePath());
                        }
                        this.saveNewEnvelope(newEnvelope, notDeletedFile, notDeletedFileTimestamp);
                        break;
                    }
                }
            }
        }
    }
    
    @Nullable
    private SentryEnvelope readEnvelope(@NotNull final File file) {
        try (final InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
            return this.serializer.getValue().deserializeEnvelope(inputStream);
        }
        catch (final IOException e) {
            this.options.getLogger().log(SentryLevel.ERROR, "Failed to deserialize the envelope.", e);
            return null;
        }
    }
    
    @Nullable
    private Session getFirstSession(@NotNull final SentryEnvelope envelope) {
        for (final SentryEnvelopeItem item : envelope.getItems()) {
            if (!this.isSessionType(item)) {
                continue;
            }
            return this.readSession(item);
        }
        return null;
    }
    
    private boolean isValidSession(@NotNull final Session session) {
        if (!session.getStatus().equals(Session.State.Ok)) {
            return false;
        }
        final String sessionId = session.getSessionId();
        return sessionId != null;
    }
    
    private boolean isSessionType(@Nullable final SentryEnvelopeItem item) {
        return item != null && item.getHeader().getType().equals(SentryItemType.Session);
    }
    
    @Nullable
    private Session readSession(@NotNull final SentryEnvelopeItem item) {
        try (final Reader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(item.getData()), CacheStrategy.UTF_8))) {
            return this.serializer.getValue().deserialize(reader, Session.class);
        }
        catch (final Throwable e) {
            this.options.getLogger().log(SentryLevel.ERROR, "Failed to deserialize the session.", e);
            return null;
        }
    }
    
    private void saveNewEnvelope(@NotNull final SentryEnvelope envelope, @NotNull final File file, final long timestamp) {
        try (final OutputStream outputStream = new FileOutputStream(file)) {
            this.serializer.getValue().serialize(envelope, outputStream);
            file.setLastModified(timestamp);
        }
        catch (final Throwable e) {
            this.options.getLogger().log(SentryLevel.ERROR, "Failed to serialize the new envelope to the disk.", e);
        }
    }
    
    @NotNull
    private SentryEnvelope buildNewEnvelope(@NotNull final SentryEnvelope envelope, @NotNull final SentryEnvelopeItem sessionItem) {
        final List<SentryEnvelopeItem> newEnvelopeItems = new ArrayList<SentryEnvelopeItem>();
        for (final SentryEnvelopeItem newEnvelopeItem : envelope.getItems()) {
            newEnvelopeItems.add(newEnvelopeItem);
        }
        newEnvelopeItems.add(sessionItem);
        return new SentryEnvelope(envelope.getHeader(), newEnvelopeItems);
    }
    
    private boolean isValidEnvelope(@NotNull final SentryEnvelope envelope) {
        return envelope.getItems().iterator().hasNext();
    }
    
    static {
        UTF_8 = Charset.forName("UTF-8");
    }
}
