package com.atlassian.bitbucket.mesh.repository;

import com.atlassian.bitbucket.mesh.config.ReplicationConfig;
import com.atlassian.bitbucket.mesh.git.ref.RefUpdate;

import javax.annotation.Nonnull;
import java.util.Set;

public interface RepositoryHashService {

    /**
     * Calculate the content hash for a set of {@link RefUpdate ref updates}.
     *
     * @param refUpdates    the ref updates to derive the transaction hash from
     * @return the new content hash
     */
    @Nonnull
    String calculateTransactionHash(@Nonnull Set<RefUpdate> refUpdates);

    /**
     * Update the content hash for the specified repository if the {@code fromHash} matches the current hash.
     *
     * @param repository        the repository for which the content hash will be updated
     * @param fromHash          the expected value of the stored content hash before this update is applied. If the
     *                          {@code fromHash} does not match the stored content hash then new content hash will not
     *                          be stored, instead the stored hash will be cleared such that the next time the content
     *                          hash is requested a full hash of the repository will be carried out.
     * @param transactionHash   the transaction hash to be applied. This is obtained by calling
     *                          {@link #calculateTransactionHash(Set)} for a set of ref updates that comprise a push.
     * @return the updated content hash
     */
    @Nonnull
    String compareAndUpdateContentHash(@Nonnull Repository repository, @Nonnull String fromHash,
                                       @Nonnull String transactionHash);

    /**
     * Retrieve the content hash for the specified repository, derived from the refs in the repository.
     *
     * @param repository    the repository for which a content hash will be returned
     * @return the content hash
     */
    @Nonnull
    String getContentHash(@Nonnull Repository repository);

    /**
     * Retrieve the metadata hash for the specified repository, derived from repository's metadata.
     *
     * @param repository    the repository for which a metadata hash will be returned
     * @return a metadata hash
     */
    @Nonnull
    String getMetadataHash(@Nonnull Repository repository);

    /**
     * Recalculates the content hash depending on the configured
     * {@link ReplicationConfig#getContentHashForcedRecalculationFrequency() content-hash-forced-recalculation-frequency}.
     * <p>
     * It is generally only safe to call this method while holding the write token for the specified repository.
     *
     * @param repository the repository to recalculate the content hash for
     * @return whether the content hash was recalculated or not
     */
    boolean maybeRecalculateContentHash(@Nonnull Repository repository);

    /**
     * Clears the content hash for the specified repository, which ensures it is calculated from the state of the
     * refs next time {@link #getContentHash(Repository)} is called for the repository.
     *
     * @param repository the repository for which the content hash should be reset
     */
    void resetContentHash(@Nonnull Repository repository);

    /**
     * Clears the metadata hash for the specified repository, which ensures it is calculated from the state of the
     * metadata next time {@link #getMetadataHash(Repository)} is called for the repository.
     *
     * @param repository the repository for which the metadata hash should be reset
     */
    void resetMetadataHash(@Nonnull Repository repository);
}
