package com.atlassian.bitbucket.internal.importer;

import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.internal.importer.event.ExternalOwnerSearchEvent;
import com.atlassian.bitbucket.internal.importer.event.ExternalRepositorySearchEvent;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequest;
import com.atlassian.event.api.EventPublisher;

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

import static java.util.Objects.requireNonNull;

/**
 * An {@link ExternalRepositoryService} implementation which delegates to an appropriate {@link ExternalRepositoryService}
 * implementation based on the {@link SourceType} present in each request.
 */
public class DelegatingRepositoryService implements ExternalRepositoryService {

    private final EventPublisher eventPublisher;
    private final Map<String, ExternalRepositoryService> externalRepositoryServices;
    private final I18nService i18nService;

    public DelegatingRepositoryService(@Nonnull EventPublisher eventPublisher,
                                       @Nonnull Map<String, ExternalRepositoryService> externalRepositoryServices,
                                       @Nonnull I18nService i18nService) {
        this.eventPublisher = eventPublisher;
        this.externalRepositoryServices = externalRepositoryServices;
        this.i18nService = i18nService;
    }

    @Nonnull
    @Override
    public Iterable<OAuthScope> getMissingOAuthScopes(ExternalRequest request) {
        SourceType sourceType = requireNonNull(request, "request").getSource().getType();
        return getExternalSource(sourceType).getMissingOAuthScopes(request);
    }

    @Nonnull
    @Override
    public ExternalUser getUser(@Nonnull ExternalRequest request) {
        SourceType sourceType = requireNonNull(request, "request").getSource().getType();
        return getExternalSource(sourceType).getUser(request);
    }

    @Nonnull
    @Override
    public Page<ExternalOwner> searchOwners(@Nonnull ExternalOwnerSearchRequest request,
                                            @Nonnull PageRequest pageRequest) {
        SourceType sourceType = requireNonNull(request, "request").getSource().getType();
        requireNonNull(pageRequest, "pageRequest");
        Page<ExternalOwner> page = getExternalSource(sourceType).searchOwners(request, pageRequest);
        eventPublisher.publish(new ExternalOwnerSearchEvent(this, sourceType, page.getSize(), page.getStart(), page.getIsLastPage()));
        return page;
    }

    @Nonnull
    @Override
    public Page<ExternalRepository> searchRepositories(@Nonnull ExternalRepositorySearchRequest request,
                                                       @Nonnull PageRequest pageRequest) {
        SourceType sourceType = requireNonNull(request, "request").getSource().getType();
        requireNonNull(pageRequest, "pageRequest");
        Page<ExternalRepository> page = getExternalSource(sourceType).searchRepositories(request, pageRequest);
        eventPublisher.publish(new ExternalRepositorySearchEvent(this, sourceType, page.getSize(), page.getStart(), page.getIsLastPage()));
        return page;
    }

    private ExternalRepositoryService getExternalSource(SourceType sourceType) {
        ExternalRepositoryService externalRepositoryService = externalRepositoryServices.get(sourceType.name());
        if (externalRepositoryService == null) {
            throw new UnsupportedSourceException(i18nService.createKeyedMessage("bitbucket.importer.unsupported.sourcetype",
                    sourceType.name()));
        }
        return externalRepositoryService;
    }
}
