package com.atlassian.stash.internal.scm;

import com.atlassian.bitbucket.commit.LastModifiedCallback;
import com.atlassian.bitbucket.repository.Branch;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.*;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.function.BiFunction;
import java.util.function.Supplier;

import static java.util.Objects.requireNonNull;

/**
 * @since 4.6
 */
public class PluginScmExtendedCommandFactory implements ScmExtendedCommandFactory {

    private final PluginExtendedCommandFactory commandFactory;
    private final Repository repository;
    private final BiFunction<String, ScmFeature, FeatureUnsupportedScmException> featureUnsupported;

    //Taking featureUnsupported as a BiFunction allows PluginScmService to provide its existing featureUnsupported
    //via a method reference, and allows SimpleScmService to avoid a new dependency on I18nService
    public PluginScmExtendedCommandFactory(@Nonnull Repository repository,
                                           @Nullable PluginExtendedCommandFactory commandFactory,
                                           @Nonnull BiFunction<String, ScmFeature, FeatureUnsupportedScmException> featureUnsupported) {
        this.commandFactory = commandFactory;
        this.featureUnsupported = requireNonNull(featureUnsupported, "featureUnsupported");
        this.repository = requireNonNull(repository, "repository");
    }

    @Nonnull
    @Override
    public Command<Void> lastModified(@Nonnull LastModifiedCommandParameters parameters,
                                      @Nonnull LastModifiedCallback callback) {
        return getCommandOrThrowUnsupported(ScmFeature.LAST_MODIFIED,
                () -> commandFactory.lastModified(repository, parameters, callback));
    }

    @Nonnull
    @Override
    public Command<Branch> merge(@Nonnull MergeCommandParameters parameters) {
        return getCommandOrThrowUnsupported(ScmFeature.MERGE, () -> commandFactory.merge(repository, parameters));
    }

    @Nonnull
    @Override
    public Command<Void> updateDefaultBranch(@Nonnull UpdateDefaultBranchCommandParameters parameters) {
        return getCommandOrThrowUnsupported(ScmFeature.UPDATE_DEFAULT_BRANCH,
                () -> commandFactory.updateDefaultBranch(repository, parameters));
    }

    @Nonnull
    private <T> Command<T> getCommandOrThrowUnsupported(ScmFeature feature, Supplier<Command<T>> supplier) {
        Command<T> command;
        try {
            command = commandFactory == null ? null : supplier.get();
        } catch (UnsupportedOperationException e) {
            command = null;
        }

        if (command == null) {
            throw featureUnsupported.apply(repository.getScmId(), feature);
        }
        return command;
    }
}
