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.git.builder.GitProcessBuilderFactory;
import com.atlassian.bitbucket.mesh.git.builder.packrefs.GitPackRefsBuilder;
import com.atlassian.bitbucket.mesh.io.MoreFiles;
import com.atlassian.bitbucket.mesh.repository.Repository;

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

import static com.atlassian.bitbucket.mesh.git.GitConstants.PATH_TAG_CACHE;
import static com.atlassian.bitbucket.mesh.io.MoreFiles.getDurationSinceLastModified;

class PackRefsOperation extends AbstractGcOperation {

    /**
     * The name of the marker file to track the last time {@code pack-refs} was run. This file is created in the
     * {@link GitRepositoryLayout#getApplicationInfoDir app-info} directory.
     */
    static final String PACKREFS_TIMESTAMP = "packrefs.ts";

    private final GitProcessBuilderFactory builderFactory;
    private final GcConfig config;

    PackRefsOperation(GitProcessBuilderFactory builderFactory, GcConfig config,
                      Repository repository, GitRepositoryLayout repositoryLayout) {
        super(repository, repositoryLayout);
        this.builderFactory = builderFactory;
        this.config = config;
    }

    @Override
    public void run(@Nonnull GcContext context) {
        if (!shouldRun()) {
            return;
        }

        //Run pack-refs
        GitPackRefsBuilder builder = builderFactory.builder(repository).packRefs()
                .all(true)
                .prune(true);
        runAction(context, GcActionType.PACK_REFS, builder, config.getPackRefsTimeout());

        // pack-refs succeeded, so delete the tag cache if it exists
        try {
            Files.delete(repositoryLayout.getApplicationInfoDir(repository).resolve(PATH_TAG_CACHE));
        } catch (FileNotFoundException | NoSuchFileException ignored) {
            // If the tag cache doesn't exist, no harm
        } catch (IOException e) {
            log.warn("[{}] Could not delete {} ({})",
                    repository, PATH_TAG_CACHE, e.getMessage(), log.isDebugEnabled() ? e : null);
        }

        Path markerFile = getPackRefsMarkerFile();
        try {
            MoreFiles.touch(markerFile);
        } catch (IOException e) {
            log.warn("[{}] Could not update {} ({}). Refs will be packed more frequently",
                    repository, markerFile.getFileName(), e.getMessage(), log.isDebugEnabled() ? e : null);
        }
    }

    @Override
    public boolean shouldRun() {
        Path packedRefs = getPackRefsMarkerFile();
        return getDurationSinceLastModified(packedRefs).compareTo(config.getPackRefsInterval()) >= 0;
    }

    private Path getPackRefsMarkerFile() {
        return repositoryLayout.getApplicationInfoDir(repository).resolve(PACKREFS_TIMESTAMP);
    }
}
