package com.atlassian.bitbucket.internal.mirroring.mirror.dao;

import com.atlassian.bitbucket.internal.mirroring.mirror.UpstreamInstallationParameters;
import com.atlassian.bitbucket.mirroring.mirror.IntegrationState;
import com.atlassian.bitbucket.mirroring.mirror.MirroringMode;

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

/**
 * DAO for CRUD operations on {@link AoUpstreamServer} instances.
 */
public interface UpstreamServerDao {

    /**
     * Creates a new {@link AoUpstreamServer} instance from the provided {@link CreateUpstreamServerRequest}.
     *
     * @param request the {@code AoUpstreamServer} to create.
     * @return the new {@link AoUpstreamServer} instance.
     * @since 4.6
     */
    @Nonnull
    AoUpstreamServer create(@Nonnull CreateUpstreamServerRequest request);

    /**
     * @param issuerId the issuer ID reported by the upstream server
     * @return the matching upstream server, or {@code null} if not found
     */
    @Nullable
    AoUpstreamServer getByIssuerId(@Nonnull String issuerId);

    /**
     * @param id the ID of the upstream server
     * @return an upstream server with the corresponding ID or {@code null} if not found
     */
    @Nullable
    AoUpstreamServer getById(@Nonnull String id);

    /**
     * @param namespace the namespace of the upstream server
     * @return an upstream server with the corresponding namespace or {@code null} if not found
     */
    @Nullable
    AoUpstreamServer getByNamespace(@Nonnull String namespace);

    /**
     * @param upstreamId the upstream server's ID
     * @return the settings if found, otherwise {@code null}
     */
    @Nullable
    AoUpstreamSettings getSettingsById(@Nonnull String upstreamId);

    /**
     * @return the upstream server or {@code null} if not found
     */
    @Nullable
    AoUpstreamServer getUpstream();

    /**
     * Updates the upstream account name
     *
     * @return the updated {@link AoUpstreamServer}
     */
    @Nullable
    AoUpstreamServer setAccountName(@Nonnull String id, @Nullable String name);

    /**
     * Updates the upstream when the add-on is installed.
     *
     * @return the updated {@link AoUpstreamServer}
     */
    @Nullable
    AoUpstreamServer setInstalledParameters(@Nonnull UpstreamInstallationParameters parameters);

    /**
     * Updates the last full sync date of the {@link AoUpstreamServer} identified by {@code id}
     *
     * @param id the ID of the upstream server
     * @param lastFullSyncDate the date of the last successful full synchronization
     * @return the updated {@link AoUpstreamServer}
     */
    AoUpstreamServer setLastFullSyncDate(@Nonnull String id, @Nullable Date lastFullSyncDate);

    /**
     * Updates the namespace of the {@link AoUpstreamServer} identified by {@code id}
     *
     * @param id the ID of the upstream server
     * @param namespace the namespace of the upstream as at the last successul full synchronization
     * @return the updated {@link AoUpstreamServer}
     */
    AoUpstreamServer setNamespace(@Nonnull String id, @Nonnull String namespace);

    /**
     * Updates the state of the {@link AoUpstreamServer} identified by {@code id}
     *
     * @param id the ID of the upstream server
     * @param state the new state
     * @return the updated {@link AoUpstreamServer}, or {@code null} if the upstream server was not found
     */
    @Nullable
    AoUpstreamServer transitionState(@Nonnull String id, @Nonnull IntegrationState state);

    /**
     * Updates the state of an {@link AoUpstreamServer}
     *
     * @param upstream the upstream server
     * @param state the new state
     * @return the updated {@link AoUpstreamServer}
     */
    @Nonnull
    AoUpstreamServer transitionState(@Nonnull AoUpstreamServer upstream, @Nonnull IntegrationState state);

    /**
     * Update the settings for the specified upstream
     *
     * @param upstreamId the ID of the upstream server
     * @param mode the mirroring mode to use for the upstream server
     * @param projectIds list of projects IDs that should be mirrored. Only relevant when {@code mode} is
     *                   {@link MirroringMode#SELECTED_PROJECTS}
     * @return the updated settings, or {@code null} if the upstream server was not found
     */
    @Nullable
    AoUpstreamSettings updateSettings(@Nonnull String upstreamId, @Nonnull MirroringMode mode,
                                    @Nonnull List<String> projectIds);


    /**
     * Update the settings for the specified upstream server
     *
     * @param upstreamId the ID of the upstream server
     * @param mode the mirroring mode to use for the upstream server. If not provided, the current value is retained
     * @param addProjectIds list of IDs for projects to start mirroring.
     *                      Only relevant when {@code mode} is {@link MirroringMode#SELECTED_PROJECTS}
     * @param removeProjectIds list of IDs for projects to stop mirroring.
     *                      Only relevant when {@code mode} is {@link MirroringMode#SELECTED_PROJECTS}
     * @return the updated settings, or {@code null} if the upstream server was not found
     */
    @Nullable
    AoUpstreamSettings updateSettings(@Nonnull String upstreamId, @Nullable MirroringMode mode,
                                    @Nonnull List<String> addProjectIds, @Nonnull List<String> removeProjectIds);
}
