package com.atlassian.bitbucket.idx;

import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.property.PropertyMap;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequest;

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

/**
 * Index to store meta data against commits. This interface serves to decouple commit indexing from storing metadata in
 * an index.
 */
public interface CommitIndex {

    /**
     * Associates the provided {@link Commit} with the specified {@link Repository}.
     *
     * @param commit     the commit
     * @param repository the repository
     */
    void addCommit(@Nonnull Commit commit, @Nonnull Repository repository);

    /**
     * Stores a string commit property. If the property (name-value pair) already exists, calling this method will have
     * no effect.
     *
     * @param commitId the commit ID
     * @param key the property key
     * @param value the property value
     */
    void addProperty(@Nonnull String commitId, @Nonnull String key, @Nonnull String value);

    /**
     * Retrieves all {@link IndexedCommit indexed commits} that have a given property with a specified value.
     *
     * @param propertyKey  the property key
     * @param propertyValue the property value
     * @param caseSensitive  whether matches against {@code value} should be case-sensitive
     * @param pageRequest the page request
     * @return a page of {@code IndexedCommit commits} that have the provided property. The results in the page
     * are ordered by {@code authorTimestamp} descending
     */
    @Nonnull
    Page<IndexedCommit> findByProperty(@Nonnull String propertyKey, @Nonnull String propertyValue,
                                       boolean caseSensitive, @Nonnull PageRequest pageRequest);

    /**
     * Retrieves the specified commit if it's been indexed.
     *
     * @param commitId the {@link Commit#getId() commit ID}
     * @return the {@link IndexedCommit}, or {@code null} if the specified commit has not been indexed
     */
    @Nullable
    IndexedCommit getCommit(@Nonnull String commitId);

    /**
     * Retrieves a property map containing all properties stored against the given commit, matching
     * {@code propertyKeys}. The value of all the properties is of type {@code Set<String>}.
     *
     * @param commitId the commit id
     * @param propertyKeys the property keys to retrieve
     * @return properties for commit with {@code commitId}, matching {@code propertyKeys}. Can be empty, but not
     * {@code null}.
     */
    @Nonnull
    PropertyMap getProperties(@Nonnull String commitId, @Nonnull Iterable<String> propertyKeys);

    /**
     * Retrieves a properties for each of the specified commits, matching the provided {@code propertyKeys}. The
     * value of all the properties is of type {@code Set<String>}.
     *
     * @param commitIds commit IDs
     * @param propertyKeys the property keys to retrieve for each commit
     * @return properties for each commit in {@code commitIds}, matching {@code propertyKeys}, mapped by commit IDs.
     * Can be empty, but not {@code null}.
     */
    @Nonnull
    Map<String, PropertyMap> getProperties(@Nonnull Iterable<String> commitIds, @Nonnull Iterable<String> propertyKeys);

    /**
     * @param commitId   the {@link Commit#getId() commit ID}
     * @param repository the repository
     * @return {@code} true if the provided commit was indexed in the specified repository
     */
    boolean isIndexed(@Nonnull String commitId, @Nonnull Repository repository);

    /**
     * Removes the association between the specified commit and repository in the index. If the specified repository
     * was the <i>only</i> association for the commit, it will be fully removed from the index. Otherwise, the commit
     * may still appear in the index if it is available in other repositories.
     *
     * @param commitId   the {@link Commit#getId() ID} of the commit to remove from the index
     * @param repository the repository to remove the commit's association for
     */
    void removeCommit(@Nonnull String commitId, @Nonnull Repository repository);

    /**
     * Removes a commit property.
     *
     * @param commitId the commit ID
     * @param key the property key
     * @param value the property value
     */
    void removeProperty(@Nonnull String commitId, @Nonnull String key, @Nonnull String value);

    /**
     * Searches the index for any matching commits.
     *
     * @param searchRequest the criteria to match
     * @param pageRequest   a pageRequest to delimit the search
     * @return a page of {@code IndexedCommit}s, possibly empty if no indexed commits match
     */
    @Nonnull
    Page<IndexedCommit> search(@Nonnull IndexSearchRequest searchRequest, @Nonnull PageRequest pageRequest);
}
