package com.atlassian.bitbucket.scm.git.command;

import com.atlassian.bitbucket.commit.graph.TraversalCallback;
import com.atlassian.bitbucket.content.*;
import com.atlassian.bitbucket.repository.*;
import com.atlassian.bitbucket.scm.*;
import com.atlassian.bitbucket.commit.Changeset;
import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.commit.CommitCallback;
import com.atlassian.bitbucket.io.TypeAwareOutputSupplier;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequest;

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

/**
 * Overrides methods from the standard {@link PluginCommandFactory} interface and returns {@link GitCommand} instances
 * instead of simple {@link Command} or {@link AsyncCommand} instances. This allows commands created by this factory to
 * be used synchronously or asynchronously at the caller's discretion, rather than fixing execution to one approach or
 * the other.
 * <p>
 * This interface does <i>not</i> add any commands to the standard interface. It only covariantly adjusts their return
 * types.
 */
public interface GitCommandFactory extends PluginCommandFactory {

    @Nonnull
    @Override
    GitCommand<List<Blame>> blame(@Nonnull Repository repository, @Nonnull BlameCommandParameters parameters,
                                  @Nonnull PageRequest pageRequest);

    /**
     * @param repository the repository to stream branches from
     * @param parameters parameters describing any filter to apply, and the order to stream branches in
     * @param callback   a callback to receive the streamed branches
     * @return a command which, when executed, will stream branches
     * @since 4.6
     */
    @Nonnull
    @Override
    GitCommand<Void> branches(@Nonnull Repository repository, @Nonnull BranchesCommandParameters parameters,
                              @Nonnull BranchCallback callback);

    @Nonnull
    @Override
    GitCommand<Page<Branch>> branches(@Nonnull Repository repository, @Nonnull BranchesCommandParameters parameters,
                                      @Nonnull PageRequest pageRequest);

    @Nonnull
    @Override
    GitCommand<Void> changes(@Nonnull Repository repository, @Nonnull ChangesCommandParameters parameters,
                             @Nonnull ChangeCallback callback);

    @Nonnull
    @Override
    GitCommand<Page<Change>> changes(@Nonnull Repository repository, @Nonnull ChangesCommandParameters parameters,
                                     @Nonnull PageRequest pageRequest);

    @Nonnull
    @Override
    GitCommand<Page<Changeset>> changesets(@Nonnull Repository repository,
                                           @Nonnull ChangesetsCommandParameters parameters,
                                           @Nonnull PageRequest pageRequest);

    @Nonnull
    @Override
    GitCommand<Commit> commit(@Nonnull Repository repository, @Nonnull CommitCommandParameters parameters);

    @Nonnull
    @Override
    GitCommand<Page<Commit>> commits(@Nonnull Repository repository, @Nonnull CommitsCommandParameters parameters,
                                     @Nonnull PageRequest pageRequest);

    @Nonnull
    @Override
    GitCommand<Void> commits(@Nonnull Repository repository, @Nonnull CommitsCommandParameters parameters,
                             @Nonnull CommitCallback callback);

    /**
     * @param repository the repository to create
     * @return a command which can be used to create a repository synchronously or asynchronously
     */
    @Nonnull
    @Override
    GitCommand<Void> create(@Nonnull Repository repository);

    /**
     * @param repository the repository to retrieve the default branch for
     * @return a command which can retrieve the default branch synchronously or asynchronously
     */
    @Nonnull
    @Override
    GitCommand<Branch> defaultBranch(@Nonnull Repository repository);

    /**
     * @param repository the repository to delete
     * @return a command which can delete the repository synchronously or asynchronously
     */
    @Nonnull
    @Override
    GitCommand<Void> delete(@Nonnull Repository repository, @Nonnull DeleteCommandParameters parameters);

    @Nonnull
    @Override
    GitCommand<Void> diff(@Nonnull Repository repository, @Nonnull DiffCommandParameters parameters,
                          @Nonnull DiffContentCallback callback);

    @Nonnull
    @Override
    GitCommand<Void> directory(@Nonnull Repository repository, @Nonnull DirectoryCommandParameters parameters,
                               @Nonnull ContentTreeCallback callback, @Nonnull PageRequest pageRequest);

    @Nonnull
    @Override
    GitCommand<Void> file(@Nonnull Repository repository, @Nonnull FileCommandParameters parameters,
                          @Nonnull FileContentCallback callback, @Nonnull PageRequest pageRequest);

    /**
     * @param parent the repository to be forked
     * @param fork the fork
     * @return a command which can create a fork synchronously or asynchronously
     * @deprecated in 4.6 for removal in 5.0. Use {@link GitExtendedCommandFactory#fork} instead.
     */
    @Deprecated
    @Nonnull
    @Override
    GitCommand<Void> fork(@Nonnull Repository parent, @Nonnull Repository fork);

    @Nonnull
    @Override
    GitCommand<Void> heads(@Nonnull Repository repository, @Nonnull RefCallback callback);

    /**
     * @param repository the repository containing <i>both</i> the source commit and the target branch
     * @param parameters parameters controlling how the merge will be performed
     * @return a command which can perform a merge synchronously or asynchronously
     * @deprecated in 4.6 for removal in 5.0. Use {@link GitExtendedCommandFactory#merge} instead.
     */
    @Deprecated
    @Nonnull
    @Override
    GitCommand<Branch> merge(@Nonnull Repository repository, @Nonnull MergeCommandParameters parameters);

    @Nonnull
    @Override
    GitCommand<Void> rawFile(@Nonnull Repository repository, @Nonnull RawFileCommandParameters parameters,
                             @Nonnull TypeAwareOutputSupplier outputSupplier);

    /**
     * @param repository the repository to resolve the ref in
     * @param parameters parameters describing the ref to resolve, optionally including a type hint
     * @return a command which, when executed, will attempt to resolve the provided ID to a {@link Branch} or
     *         {@link Tag}
     * @since 4.6
     */
    @Override
    GitCommand<Ref> resolveRef(@Nonnull Repository repository, @Nonnull ResolveRefCommandParameters parameters);

    @Nonnull
    @Override
    GitCommand<Ref> resolveRef(@Nonnull Repository repository, @Nonnull String refId);

    /**
     * @since 4.4
     */
    @Nonnull
    @Override
    GitCommand<Map<String, Ref>> resolveRefs(@Nonnull Repository repository,
                                             @Nonnull ResolveRefsCommandParameters parameters);

    @Nonnull
    @Override
    GitCommand<Page<Tag>> tags(@Nonnull Repository repository, @Nonnull TagsCommandParameters parameters,
                               @Nonnull PageRequest pageRequest);

    @Nonnull
    @Override
    GitCommand<Void> tags(@Nonnull Repository repository, @Nonnull TagsCommandParameters parameters,
                          @Nonnull TagCallback callback);

    /**
     * @param repository the repository to traverse commits for
     * @param callback   the callback to receive the streamed commits
     * @return a command which, when executed, will stream all of the commits in the repository, topologically ordered
     * @since 4.6
     */
    @Nonnull
    @Override
    GitCommand<Void> traverseCommits(@Nonnull Repository repository, @Nonnull TraversalCallback callback);

    @Nonnull
    @Override
    GitCommand<ContentTreeNode.Type> type(@Nonnull Repository repository, @Nonnull TypeCommandParameters parameters);

    /**
     * @param repository the repository whose default branch should be updated
     * @param branchName the branch to make the default
     * @return a command which can update the default branch in the specified repository
     * @deprecated in 4.6 for removal in 5.0. Use {@link GitExtendedCommandFactory#updateDefaultBranch} instead.
     */
    @Deprecated
    @Nonnull
    @Override
    GitCommand<Void> updateDefaultBranch(@Nonnull Repository repository, @Nonnull String branchName);
}
