package com.atlassian.stash.internal.plugin.hooks.branchlinks;

import com.atlassian.bitbucket.pull.*;
import com.atlassian.bitbucket.repository.*;
import com.atlassian.bitbucket.repository.NoDefaultBranchException;
import com.atlassian.bitbucket.hook.HookResponse;
import com.atlassian.bitbucket.hook.PostReceiveHook;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.nav.NavBuilder;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequestImpl;

import javax.annotation.Nonnull;
import java.io.PrintWriter;
import java.util.Collection;

import static com.atlassian.bitbucket.repository.StandardRefType.BRANCH;

/**
 * Prints links for any added/updated branches, for easier pull request creation/viewing.
 * @since 3.3
 */
public class PrintBranchLinksHook implements PostReceiveHook {

    private final I18nService i18nService;
    private final NavBuilder navBuilder;
    private final PullRequestService pullRequestService;
    private final RefService refService;

    public PrintBranchLinksHook(I18nService i18nService, NavBuilder navBuilder, PullRequestService pullRequestService,
                                RefService refService) {
        this.i18nService = i18nService;
        this.navBuilder = navBuilder;
        this.pullRequestService = pullRequestService;
        this.refService = refService;
    }

    @Override
    public void onReceive(@Nonnull Repository repository, @Nonnull Collection<RefChange> refChanges, @Nonnull HookResponse response) {
        // Only print link(s) when a single branch has been pushed
        if (refChanges.size() != 1) {
            return;
        }
        RefChange refChange = refChanges.iterator().next();
        if (!shouldPrint(repository, refChange)) {
            return;
        }

        PrintWriter out = response.out();
        print(repository, refChange, out);
    }

    private boolean shouldPrint(Repository repository, RefChange refChange) {
        MinimalRef ref = refChange.getRef();
        if (BRANCH.equals(ref.getType())) {
            RefChangeType type = refChange.getType();
            if (type == RefChangeType.ADD || type == RefChangeType.UPDATE) {
                String defaultBranchId = getDefaultBranchId(repository);
                return !ref.getId().equals(defaultBranchId);
            }
        }
        return false;
    }

    private String getDefaultBranchId(Repository repository) {
        try {
            Branch defaultBranch = refService.getDefaultBranch(repository);
            return defaultBranch.getId();
        } catch (NoDefaultBranchException e) {
            return null;
        }
    }

    private void print(Repository repository, RefChange branchChange, PrintWriter out) {
        out.println();
        printBranchLinks(repository, branchChange, out);
        out.println();
    }

    private void printBranchLinks(Repository repository, RefChange branchChange, PrintWriter out) {
        MinimalRef ref = branchChange.getRef();

        if (branchChange.getType() == RefChangeType.ADD) {
            printCreatePullRequestLink(repository, ref, out);
        } else if (branchChange.getType() == RefChangeType.UPDATE) {
            PullRequestSearchRequest searchRequest = new PullRequestSearchRequest.Builder().
                    repositoryAndBranch(PullRequestDirection.OUTGOING, repository.getId(), ref.getId()).
                    state(PullRequestState.OPEN).
                    build();

            Page<PullRequest> pullRequests = pullRequestService.search(searchRequest, new PageRequestImpl(0, 10));
            if (pullRequests.getSize() != 0) {
                printPullRequestLinks(pullRequests, out);
            } else {
                printCreatePullRequestLink(repository, ref, out);
            }
        }
    }

    private void printCreatePullRequestLink(Repository repository, MinimalRef ref, PrintWriter out) {
        String branch = ref.getDisplayId();
        String createPullRequest = i18nService.getMessage("bitbucket.plugins.hooks.branchlinks.createPullRequest", branch);

        String compareUrl = navBuilder.repo(repository).compare().sourceBranch(ref.getId()).buildAbsolute();

        out.println(createPullRequest);

        out.print("  ");
        out.println(compareUrl);
    }

    private void printPullRequestLinks(Page<PullRequest> pullRequests, PrintWriter out) {
        for (PullRequest pr : pullRequests.getValues()) {
            String from = pr.getFromRef().getDisplayId();
            String to = pr.getToRef().getDisplayId();
            String viewPullRequest = i18nService.getMessage("bitbucket.plugins.hooks.branchlinks.viewPullRequest", from, to);

            String prUrl = navBuilder.repo(pr.getToRef().getRepository()).pullRequest(pr.getId()).buildAbsolute();

            out.println(viewPullRequest);

            out.print("  ");
            out.println(prUrl);
        }
        if (!pullRequests.getIsLastPage()) {
            out.println("  ...");
        }
    }
}
