package com.atlassian.bitbucket.hook.repository;

import com.atlassian.bitbucket.avatar.CacheableAvatarSupplier;
import com.atlassian.bitbucket.validation.ArgumentValidationException;
import com.atlassian.bitbucket.validation.FormValidationException;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.setting.Settings;
import com.atlassian.bitbucket.setting.SettingsBuilder;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequest;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * Handles the aggregation of hooks and their enabled status.
 * Settings for these hooks are handled elsewhere.
 */
public interface RepositoryHookService {

    /**
     * Find all installed hooks of the specified repository with their enabled status.
     *
     * @param repository  the repository to load the hook from
     * @param pageRequest paging options
     * @return a page of {@link RepositoryHook}s matching the supplied type
     */
    @Nonnull
    Page<RepositoryHook> findAll(@Nonnull Repository repository, @Nonnull PageRequest pageRequest);

    /**
     * Find all installed hooks of the specified {@link RepositoryHookType} with enabled status for this repository
     *
     * @param repository  the repository to load the hook from
     * @param type        the type of hooks to be looked for
     * @param pageRequest paging options
     * @return a page of {@link RepositoryHook}s matching the supplied type
     */
    @Nonnull
    Page<RepositoryHook> findByType(@Nonnull Repository repository, @Nonnull RepositoryHookType type, @Nonnull PageRequest pageRequest);

    /**
     * Find an installed hook with the specified key enabled status for this repository
     *
     * @param repository the repository to load the hook from
     * @param hookKey    the hook to lookup
     * @return the {@link RepositoryHook} if found. Otherwise {@code null}.
     */
    @Nullable
    RepositoryHook getByKey(@Nonnull Repository repository, @Nonnull String hookKey);

    /**
     * Enable the hook identified by the supplied key for the specified repository.
     *
     * @param repository the repository this hook is being enabled on
     * @param hookKey    the hook to be enabled
     * @return the newly updated {@link RepositoryHook}
     */
    @Nonnull
    RepositoryHook enable(@Nonnull Repository repository, @Nonnull String hookKey);

    /**
     * Enable the hook identified by the supplied key for the specified repository and apply the new settings.
     * <p>
     * This is a convenient method which performs {@link #enable(Repository, String)}
     * and {@link #setSettings(Repository, String, Settings)} in the same transaction
     * @param repository the repository this hook is being enabled on
     * @param hookKey    the hook to be enabled
     * @param settings   the new settings.
     * @return the newly updated {@link RepositoryHook}
     * @throws FormValidationException when settings are invalid, leaving the existing version unchanged
     * @throws ArgumentValidationException if the settings to be saved are too large, the current limit is 32KB once serialized.
     */
    @Nonnull
    RepositoryHook enable(@Nonnull Repository repository, @Nonnull String hookKey, @Nonnull Settings settings) throws FormValidationException;

    /**
     * Disable the hook identified by the supplied key for the specified repository.
     *
     * @param repository the repository this hook is being disabled on
     * @param hookKey    the hook to be disabled
     * @return the newly updated {@link RepositoryHook}
     */
    @Nonnull
    RepositoryHook disable(@Nonnull Repository repository, @Nonnull String hookKey);

    /**
     * Gets the settings for the specified hook and repository.
     *
     * @param repository the repository the settings apply to.
     * @param hookKey the complete module key of the hook which the settings are
     *                associated with.
     * @return the settings currently stored or {@code null} if no settings are stored.
     */
    @Nullable
    Settings getSettings(@Nonnull Repository repository, @Nonnull String hookKey);

    /**
     * Sets the settings for the specified hook and repository.
     *
     * @param repository the repository the settings apply to.
     * @param hookKey the complete module key of the hook which the settings are
     *                associated with.
     * @param settings the new settings to save
     * @return the settings saved.
     * @throws FormValidationException when settings are invalid, leaving the existing version unchanged
     * @throws ArgumentValidationException if the settings to be saved are too large, the current limit is 32KB once serialized.
     * @see #createSettingsBuilder()
     */
    @Nonnull
    Settings setSettings(@Nonnull Repository repository, @Nonnull String hookKey, @Nonnull Settings settings) throws FormValidationException;

    /**
     * Create a new {@link SettingsBuilder} which can be used to create a {@link Settings} instance.
     *
     * @return a new builder
     */
    @Nonnull
    SettingsBuilder createSettingsBuilder();

    /**
     * Returns a supplier of an avatar for this hook, either one specified in the module or a default.
     *
     * @param hookKey the hook key with the desired avatar
     * @return an supplier for streaming the avatar
     */
    @Nonnull
    CacheableAvatarSupplier getAvatar(@Nonnull String hookKey);
}
