package com.atlassian.stash.internal.web.util;

import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.commit.CommitRequest;
import com.atlassian.bitbucket.commit.CommitService;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.permission.PermissionService;
import com.atlassian.bitbucket.repository.*;
import com.atlassian.bitbucket.ui.PluginFormFragmentsFactory;
import com.atlassian.stash.internal.page.PageConstants;
import com.atlassian.stash.internal.project.InternalProjectService;
import com.atlassian.stash.internal.scm.InternalScmService;
import com.atlassian.stash.internal.web.soy.StashSoyResponseBuilder;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;

import java.util.Map;

public abstract class CompareControllerSupport extends RepositoryControllerSupport {

    protected final CommitService commitService;
    protected final PluginFormFragmentsFactory fragmentsFactory;

    @Value(PageConstants.MAX_CHANGES)
    protected int maxChanges;

    protected CompareControllerSupport(I18nService i18nService, RefService refService,
                                       PermissionService permissionService, InternalProjectService projectService,
                                       RepositoryService repositoryService, InternalScmService scmService,
                                       CommitService commitService, PluginFormFragmentsFactory fragmentsFactory) {
        super(i18nService, refService, permissionService, projectService, repositoryService, scmService);
        this.commitService = commitService;
        this.fragmentsFactory = fragmentsFactory;

    }

    protected StashSoyResponseBuilder getCompareViewBuilder(String view, Repository fromRepository, String fromBranch,
                                                            Repository toRepository, String toBranch,
                                                            String pluginFieldsContent) {
        Ref fromBranchRef = resolveFromRef(fromRepository, fromBranch);
        Commit sourceCommit = null;
        if (fromBranchRef != null) {
            sourceCommit = getCommit(fromRepository, fromBranchRef);
        }

        Ref toBranchRef = resolveToRef(toRepository, toBranch);
        Commit targetCommit = null;
        if (toBranchRef != null) {
            targetCommit = getCommit(toRepository, toBranchRef);
        }

        return new StashSoyResponseBuilder(view)
                .put("pluginFieldsContent", pluginFieldsContent)
                .put("maxChanges", maxChanges)
                .put("sourceRepository", fromRepository)
                .putIfNotNull("sourceBranch", fromBranchRef)
                .putIfNotNull("sourceCommit", sourceCommit)
                .put("targetRepository", toRepository)
                .putIfNotNull("targetBranch", toBranchRef)
                .putIfNotNull("targetCommit", targetCommit)
                .putRepository(toRepository);
    }

    protected StashSoyResponseBuilder getCompareViewBuilder(String view, Repository fromRepository, String fromBranch,
                                                            Repository toRepository, String toBranch) {
        // Assumes that the fragment key and the view are the same
        return getCompareViewBuilder(view, view, fromRepository, fromBranch, toRepository, toBranch);
    }

    protected StashSoyResponseBuilder getCompareViewBuilder(String view, String fragmentKey, Repository fromRepository, String fromBranch,
                                                            Repository toRepository, String toBranch) {
        Map<String, Object> context = Maps.newHashMap();
        context.put("fromRepository", fromRepository);
        context.put("toRepository", toRepository);

        String pluginFieldsContent = fragmentsFactory.forKey(fragmentKey, context).getViewHtml();

        return getCompareViewBuilder(view, fromRepository, fromBranch, toRepository, toBranch, pluginFieldsContent);
    }

    protected void handleNullRef(Repository repository, String branch) {
    }

    private Commit getCommit(Repository repository, Ref ref) {
        return commitService.getCommit(new CommitRequest.Builder(repository, ref.getLatestCommit()).build());
    }

    private Ref resolveFromRef(Repository fromRepository, String fromBranch) {
        if (StringUtils.isBlank(fromBranch)) {
            return null;
        }

        Ref ref = refService.resolveRef(new ResolveRefRequest.Builder(fromRepository)
                .refId(fromBranch)
                .build());
        if (ref == null) {
            handleNullRef(fromRepository, fromBranch);
        }

        return ref;
    }

    private Ref resolveToRef(Repository toRepository, String toBranch) {
        toBranch = StringUtils.isBlank(toBranch) ? null : toBranch;
        Ref toBranchRef = null;
        try {
            toBranchRef = refService.resolveRef(new ResolveRefRequest.Builder(toRepository)
                    .refId(toBranch)
                    .build());
            if (toBranchRef == null) {
                handleNullRef(toRepository, toBranch);
            }
        } catch (NoDefaultBranchException e) {
            log.debug("No default branch set on {}/{}", toRepository.getProject().getKey(), toRepository.getSlug());
        }
        return toBranchRef;
    }
}
