package com.atlassian.bitbucket.repository.ref.restriction;

import com.atlassian.bitbucket.repository.MinimalRef;
import com.atlassian.bitbucket.repository.RefChange;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequest;
import com.google.common.collect.Multimap;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;

/**
 * A service that manages restrictions to refs.
 * <p>
 * Repository admin permissions are required for all operations unless specified.
 */
public interface RefRestrictionService {

    /**
     * Get a {@link RefRestriction} by its ID.
     *
     * @param id the ID associated with the {@link RefRestriction}
     * @return the {@link RefRestriction} or {@code null} if not found
     */
    @Nullable
    RefRestriction getById(int id);

    /**
     * Check if the current user has permission to perform an action on a ref or collection of {@link MinimalRef refs}.
     * This method does not require any permissions and can be called by any user.
     *
     * @param request specifies which branches and which access rights to check for
     * @return {@code true} if the user can take the action specified in {@code request}, {@code false} otherwise
     */
    boolean hasPermission(@Nonnull RefAccessRequest request);

    /**
     * Match a proposed list of {@link RefChange refChanges} (optionally taking into consideration a specific user who
     * is about to perform them) with any restriction(s) that would prevent the change. The results are returned as a
     * {@link Multimap} that matches up each disallowed {@link RefChange refChange} to any restriction(s) that match it.
     * This allows a caller to discover for any arbitrary set of {@link RefChange refChanges} not only whether they
     * will be prevented by restrictions but also which specific restriction(s) apply to each individual
     * {@link RefChange refChange}.
     * <p>
     * If the {@link com.atlassian.bitbucket.repository.ref.restriction.RefRestrictionType.FAST_FORWARD_ONLY FAST_FORWARD_ONLY}
     * restriction type is included in the request, then the {@code fromHash} and {@code toHash} of any matching
     * {@link RefChange refChanges} will be checked to determine if the ref change is a fast-forward-only change or
     * would attempt to rewrite history (and therefore be prevented). This requires {@code fromHash} and {@code toHash}
     * to be valid for the specified repository, or {@link com.atlassian.bitbucket.scm.CommandFailedException} will be
     * thrown.
     *
     * @param request specifies the repository, {@link RefRestrictionType types}, {@link RefChange refChanges}, and
     *                (optionally) a user for the proposed change.
     * @return a {@link Multimap} containing {@link RefRestriction}(s) (if any) that apply to each
     *         {@link RefChange refChange} specified in the input.  If the {@link RefChange refChanges} would not be
     *         prevented by any restriction, then an empty {@link Multimap} is returned.
     * @throws com.atlassian.bitbucket.scm.CommandFailedException if {@code FAST_FORWARD_ONLY} restrictions are checked
     *         and {@code fromHash} and {@code toHash} are invalid for the specified repository.
     * @since 4.5
     */
    @Nonnull
    Multimap<RefChange, RefRestriction> match(@Nonnull RestrictionMatchRequest request);

    /**
     * Remove a {@link RefRestriction} by its ID.
     *
     * @param id the ID associated with the {@link RefRestriction}
     * @return {@code true} if the {@link RefRestriction} was deleted, {@code false} otherwise
     */
    boolean removeRefRestriction(int id);

    /**
     * Sets a restriction based on the {@link SetRestrictionRequest}.
     *
     * If the same {@link RefRestriction} already exists it will be overwritten.
     * @param request the {@link SetRestrictionRequest} which specifies the restriction to be set
     * @return the created or modified {@link RefRestriction}
     */
    @Nonnull
    RefRestriction setRestriction(@Nonnull SetRestrictionRequest request);

    /**
     * Sets all the restrictions specified by the provided {@link SetRestrictionRequest requests}.
     *
     * @param requests a list of {@link SetRestrictionRequest requests} which specify the restrictions to be set
     * @return a list of the created or modified {@link RefRestriction restrictions}
     */
    @Nonnull
    List<RefRestriction> setRestrictions(@Nonnull Collection<SetRestrictionRequest> requests);

    /**
     * Search for {@link RefRestriction restrictions} based on the {@link RestrictionSearchRequest}.
     *
     * @param request the {@link RestrictionSearchRequest} which specifies the {@link RefRestriction restrictions} to search for
     * @param pageRequest the {@link PageRequest} which specifies the page to return
     * @return a {@link Page} of {@link RefRestriction restrictions}
     */
    @Nonnull
    Page<RefRestriction> search(@Nonnull RestrictionSearchRequest request, @Nonnull PageRequest pageRequest);
}
