package com.atlassian.bitbucket.internal.scm.git.lfs.embedded;

import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.internal.scm.git.lfs.GitLfsDisabledException;
import com.atlassian.bitbucket.internal.scm.git.lfs.settings.GitLfsSettingsService;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.repository.RepositoryService;
import com.atlassian.bitbucket.user.SecurityService;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageUtils;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.io.IOException;

import static java.util.Objects.requireNonNull;

public abstract class AbstractEmbeddedStoreService implements EmbeddedStoreService {

    /**
     * The hosting ticket bucket used to limit the number of connections performing file upload
     * or download operations. The risk that this is mitigating is that of many long running file
     * transfers consuming all connections and preventing access to the web UI and REST interface.
     */
    protected static final String LFS_THROTTLE_BUCKET = "git-lfs";
    private static final Logger log = LoggerFactory.getLogger(AbstractEmbeddedStoreService.class);

    protected final I18nService i18nService;
    protected final LfsFileSystemHelper lfsFileSystemHelper;
    private final RepositoryService repositoryService;
    private final SecurityService securityService;
    private final GitLfsSettingsService settingsService;

    public AbstractEmbeddedStoreService(I18nService i18nService, LfsFileSystemHelper lfsFileSystemHelper,
                                        RepositoryService repositoryService, SecurityService securityService,
                                        GitLfsSettingsService settingsService) {
        this.i18nService = i18nService;
        this.lfsFileSystemHelper = lfsFileSystemHelper;
        this.repositoryService = repositoryService;
        this.securityService = securityService;
        this.settingsService = settingsService;
    }

    @Override
    public void delete(@Nonnull Repository repository) {
        requireNonNull(repository, "repository");

        // Objects are bucketed per hierarchy id, so only delete the objects contained within the hierarchy
        // if there are no other repositories in the hierarchy. We MUST escalate permissions here, since
        // findRelated() will return a repository list filtered to contain only those repositories which the
        // requesting user has access to.
        Page<Repository> related = securityService.withPermission(Permission.REPO_READ,
                "Finding related repositories").call(() ->
                repositoryService.findRelated(repository, PageUtils.newRequest(0, 1)));

        if (related.getSize() == 0 && related.getIsLastPage()) {
            String hierarchyId = repository.getHierarchyId();
            try {
                log.debug("Deleting LFS content for hierarchy " + hierarchyId);
                FileUtils.deleteDirectory(lfsFileSystemHelper.getHierarchyPath(hierarchyId).toFile());
            } catch (IOException e) {
                log.error("Failed to delete LFS content for hierarchy " + hierarchyId, e);
            }
        }
    }

    @Override
    public long getFreeSpace() {
        return lfsFileSystemHelper.getFreeSpace();
    }

    @Override
    public boolean objectExists(@Nonnull Repository repository, @Nonnull String oid) {
        return lfsFileSystemHelper.objectExists(repository, oid);
    }

    protected void requireLfsEnabled() {
        if (!settingsService.isEnabled()) {
            throw new GitLfsDisabledException(i18nService.createKeyedMessage("bitbucket.scm.git.lfs.disabled"));
        }
    }
}
