package com.atlassian.bitbucket.mesh.repository;

import com.atlassian.bitbucket.mesh.annotation.Profiled;
import com.atlassian.bitbucket.mesh.event.EventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

import static java.util.Objects.requireNonNull;

@Profiled
public class DefaultRepositoryAttributeManager implements RepositoryAttributeManager {

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

    private final RepositoryAttributeDao repositoryAttributeDao;

    public DefaultRepositoryAttributeManager(RepositoryAttributeDao repositoryAttributeDao) {
        this.repositoryAttributeDao = repositoryAttributeDao;
    }

    @Override
    public void delete(@Nonnull String repositoryId) {
        repositoryAttributeDao.delete(requireNonNull(repositoryId, "repositoryId"));
    }

    @Override
    public String getContentHash(@Nonnull String repositoryId) {
        return repositoryAttributeDao.getContentHash(requireNonNull(repositoryId, "repositoryId"));
    }

    @Override
    public String getMetadataHash(@Nonnull String repositoryId) {
        return repositoryAttributeDao.getMetadataHash(requireNonNull(repositoryId, "repositoryId"));
    }

    @Override
    public long getVersion(@Nonnull String repositoryId) {
        return repositoryAttributeDao.getVersion(requireNonNull(repositoryId, "repositoryId"));
    }

    @Nonnull
    @Override
    public Map<String, Long> getVersionsByPartitions(@Nonnull Set<String> partitions) {
        return repositoryAttributeDao.getVersionsByPartitions(requireNonNull(partitions, "partitions"));
    }

    @Override
    public long incrementAndGetVersion(@Nonnull String repositoryId) {
        DataIntegrityViolationException exception = null;
        for (int i = 0; i < 5; ++i) {
            try {
                return repositoryAttributeDao.incrementAndGetVersion(requireNonNull(repositoryId, "repositoryId"));
            } catch (DataIntegrityViolationException e) {
                log.debug("[{}] Concurrent version update detected. Retrying..", repositoryId, e);
                exception = e;
            }
        }
        throw exception;
    }

    @Override
    public boolean isReadOnly(@Nonnull String repositoryId) {
        return repositoryAttributeDao.isReadOnly(requireNonNull(repositoryId, "repositoryId"));
    }

    @EventListener
    public void onRepositoryDeleted(@Nonnull RepositoryDeletedEvent event) {
        repositoryAttributeDao.delete(event.getRepository().getId());
    }

    @Override
    public void replaceReadOnly(@Nonnull Collection<String> repositoryIds) {
        repositoryAttributeDao.replaceReadOnly(requireNonNull(repositoryIds, "repositoryIds"));
    }

    @Override
    public void setContentHash(@Nonnull String repositoryId, @Nullable String contentHash) {
        repositoryAttributeDao.setContentHash(requireNonNull(repositoryId, "repositoryId"), contentHash);
    }

    @Override
    public void setMetadataHash(@Nonnull String repositoryId, @Nullable String metadataHash) {
        repositoryAttributeDao.setMetadataHash(requireNonNull(repositoryId, "repositoryId"), metadataHash);
    }

    @Override
    public void setReadOnly(@Nonnull String repositoryId, boolean readOnly) {
        repositoryAttributeDao.setReadOnly(repositoryId, readOnly);
    }

    @Override
    public void setVersion(@Nonnull String repositoryId, long version) {
        repositoryAttributeDao.setVersion(requireNonNull(repositoryId, "repositoryId"), version);
    }
}
