package com.atlassian.stash.internal.compare;

import com.atlassian.bitbucket.commit.BatchingCommitCallback;
import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.commit.CommitCallback;
import com.atlassian.bitbucket.compare.CompareDiffRequest;
import com.atlassian.bitbucket.compare.CompareRequest;
import com.atlassian.bitbucket.compare.CompareService;
import com.atlassian.bitbucket.content.ChangeCallback;
import com.atlassian.bitbucket.content.DiffContentCallback;
import com.atlassian.bitbucket.scm.ScmService;
import com.atlassian.bitbucket.scm.compare.CompareChangeCommandParameters;
import com.atlassian.bitbucket.scm.compare.CompareDiffCommandParameters;
import com.atlassian.plugin.spring.AvailableToPlugins;
import com.atlassian.stash.internal.commit.InternalCommitEnricher;
import com.atlassian.stash.internal.content.DiffContentCallbackFilter;
import com.atlassian.stash.internal.scm.AbstractScmService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Collections;

import static com.google.common.base.Preconditions.checkNotNull;

@AvailableToPlugins(CompareService.class)
@PreAuthorize("isRepositoryAccessible(#request.fromRef.repository) and isRepositoryAccessible(#request.toRef.repository)")
@Service("compareService")
public class DefaultCompareService extends AbstractScmService implements CompareService {

    private final InternalCommitEnricher commitEnricher;

    @Autowired
    public DefaultCompareService(ScmService scmService, InternalCommitEnricher commitEnricher) {
        super(scmService);

        this.commitEnricher = commitEnricher;
    }

    @Override
    public void streamChanges(@Nonnull CompareRequest request, @Nonnull ChangeCallback callback) {
        checkNotNull(request, "request");
        checkNotNull(callback, "callback");

        scmService.getCompareCommandFactory(request)
                .changes(new CompareChangeCommandParameters.Builder()
                        .maxChanges(maxChanges)
                        .build(), callback)
                .call();
    }

    @Override
    public void streamCommits(@Nonnull final CompareRequest request, @Nonnull CommitCallback callback) {
        checkNotNull(request, "request");
        checkNotNull(callback, "callback");

        scmService.getCompareCommandFactory(request)
                .commits(new BatchingCommitCallback(callback, 25) {

                    @Override
                    protected boolean onCommits(@Nonnull Iterable<Commit> commits) throws IOException {
                        commits = commitEnricher.enrichAll(request.getFromRef().getRepository(),
                                commits, Collections.<String>emptySet());

                        return super.onCommits(commits);
                    }
                })
                .call();
    }

    @Override
    public void streamDiff(@Nonnull CompareDiffRequest request, @Nonnull DiffContentCallback callback) {
        checkNotNull(request, "request");
        checkNotNull(request, "callback");

        scmService.getCompareCommandFactory(asCompare(request))
                .diff(new CompareDiffCommandParameters.Builder()
                        .contextLines(request.hasContextLines() ? request.getContextLines() : diffContext)
                        .maxLineLength(maxLineLength)
                        .maxLines(maxDiffLines)
                        .paths(request.getPaths())
                        .whitespace(request.getWhitespace())
                        .build(), DiffContentCallbackFilter.filter(callback, request.getFilter()))
                .call();
    }

    private CompareRequest asCompare(CompareDiffRequest request) {
        return new CompareRequest.Builder(request).build();
    }
}
