package com.atlassian.bitbucket.mesh.git.gc;

import com.atlassian.bitbucket.mesh.config.GcConfig;
import com.atlassian.bitbucket.mesh.git.GitRepositoryLayout;
import com.atlassian.bitbucket.mesh.repository.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;

import static com.atlassian.bitbucket.mesh.git.GitConstants.PATH_PEELED_TAGS;

class PruneStaleObjectsOperation implements GcOperation {

    private static final Logger log = LoggerFactory.getLogger(PruneStaleObjectsOperation.class);

    private final GcConfig config;
    private final Repository repository;
    private final GitRepositoryLayout repositoryLayout;

    PruneStaleObjectsOperation(Repository repository, GcConfig config, GitRepositoryLayout repositoryLayout) {
        this.config = config;
        this.repository = repository;
        this.repositoryLayout = repositoryLayout;
    }

    /**
     * Prunes stale files and directories under the {@code objects/} directory, which can accumulate over
     * time and consume significant amounts of storage.
     *
     * @see GcStaleFileVisitor
     */
    @Override
    public void run(@Nonnull GcContext context) {
        // The legacy peeled-tags cache is no longer used, so if it exists we delete it. There's no explicit
        // existence check first because it's not NFS-safe (and if we're going to do a stat operation there's
        // no real difference in cost to just running the delete instead)
        try {
            Files.delete(repositoryLayout.getApplicationInfoDir(repository).resolve(PATH_PEELED_TAGS));
        } catch (FileNotFoundException | NoSuchFileException ignored) {
            // If peeled-tags doesn't exist, no harm
        } catch (IOException e) {
            log.warn("[{}] Could not delete {} cache ({})",
                    repository, PATH_PEELED_TAGS, e.getMessage(), log.isDebugEnabled() ? e : null);
        }

        try {
            Files.walkFileTree(repositoryLayout.getObjectsDir(repository),
                    new GcStaleFileVisitor(repository, config.getPruneExpire(repository)));
        } catch (IOException e) {
            log.warn("[{}]: Stale entries in the objects/ directory could not be pruned", repository, e);
        }
    }
}
