package com.atlassian.bitbucket.internal.branch.automerge;

import com.atlassian.bitbucket.branch.model.BranchModel;
import com.atlassian.bitbucket.branch.model.BranchModelService;
import com.atlassian.bitbucket.internal.branch.BranchModelUtils;
import com.atlassian.bitbucket.internal.branch.model.BranchModelNode;
import com.atlassian.bitbucket.repository.Branch;
import com.atlassian.bitbucket.repository.Ref;
import com.atlassian.bitbucket.repository.Repository;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;

public class DefaultAutoMergeService implements AutoMergeService {

    private final AutoMergeConfig autoMergeConfig;
    private final BranchModelService branchModelService;

    public DefaultAutoMergeService(AutoMergeConfig autoMergeConfig, BranchModelService branchModelService) {
        this.autoMergeConfig = autoMergeConfig;
        this.branchModelService = branchModelService;
    }

    @Nonnull
    @Override
    public AutoMerge getAutoMerge(@Nonnull Repository repository, @Nonnull Ref ref) {
        Branch branch = BranchModelUtils.convertRefToBranch(ref);
        if (!autoMergeConfig.isEnabled(repository)) {
            return SimpleAutoMerge.skip(AutoMergeStatus.AUTO_MERGE_DISABLED, branch);
        }

        BranchModel model = branchModelService.getModel(repository);
        if (model == null) {
            return SimpleAutoMerge.skip(AutoMergeStatus.CANNOT_CALCULATE_PATH, branch);
        }

        BranchModelNode mergePath = BranchModelUtils.asInternalBranchModel(model).getMergePath(branch);
        List<Branch> downstream = getAllDownstreamBranches(mergePath);

        if (downstream.size() <= 1) {
            return SimpleAutoMerge.skip(AutoMergeStatus.NO_PATH, branch);
        }

        //4 branches in the merge path implies 3 merges, so the maximum number of branches
        //in the merge path is limit+1
        int maxBranches = autoMergeConfig.getLimit() + 1;
        if (downstream.size() > maxBranches) {
            return SimpleAutoMerge.skip(AutoMergeStatus.PATH_TOO_LONG, branch);
        }

        return SimpleAutoMerge.proceed(downstream);
    }

    private static List<Branch> getAllDownstreamBranches(BranchModelNode node) {
        List<Branch> branches = new ArrayList<>();
        addAllDownstreamBranches(node, branches);

        return branches;
    }

    private static void addAllDownstreamBranches(BranchModelNode node, List<Branch> branches) {
        branches.add(node.getBranch());
        for (BranchModelNode downstream : node.getDownstream()) {
            addAllDownstreamBranches(downstream, branches);
        }
    }
}
