package com.atlassian.stash.internal.repository.ref.restriction.stp;

import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.repository.ref.restriction.*;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageUtils;
import com.atlassian.support.tools.spi.SupportInfoAppender;
import com.atlassian.support.tools.spi.SupportInfoBuilder;
import com.google.common.collect.Iterables;

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

/**
 * Adds information about the branch permissions on a repository to STP.
 *
 * @since 3.10
 */
public class RefRestrictionsSupportInfo implements SupportInfoAppender<Repository> {

    private static final String PLUGINS_BRANCH_PERMISSIONS = "stp.properties.stash.branch.permissions";
    private static final String PLUGINS_BRANCH_PERMISSION = PLUGINS_BRANCH_PERMISSIONS + ".permission";
    private static final String PLUGINS_BRANCH_PERMISSION_ACCESS_GRANTS = PLUGINS_BRANCH_PERMISSION + ".access-grants";
    private static final String PLUGINS_BRANCH_PERMISSION_MATCHER = PLUGINS_BRANCH_PERMISSION + ".matcher";
    private static final String PLUGINS_BRANCH_PERMISSION_MATCHER_TYPE = PLUGINS_BRANCH_PERMISSION_MATCHER + ".type";
    private static final String PLUGINS_BRANCH_PERMISSION_MATCHER_ID = PLUGINS_BRANCH_PERMISSION_MATCHER + ".id";
    private static final String PLUGINS_BRANCH_PERMISSION_RESTRICTION_TYPE = PLUGINS_BRANCH_PERMISSION + ".type";

    private final RefRestrictionService refRestrictionService;

    public RefRestrictionsSupportInfo(RefRestrictionService refRestrictionService) {
        this.refRestrictionService = refRestrictionService;
    }


    @Override
    public void addSupportInfo(SupportInfoBuilder supportInfoBuilder, Repository repository) {
        addBranchPermissionDetails(supportInfoBuilder, repository);
    }

    private void addBranchPermissionDetails(SupportInfoBuilder builder, Repository repository) {
        RestrictionSearchRequest request = new RestrictionSearchRequest.Builder(repository).build();
        Page<? extends RefRestriction> restrictions = refRestrictionService.search(request, PageUtils.newRequest(0, 500));
        if (Iterables.isEmpty(restrictions.getValues())) {
            //Don't print out the category at all if the permissions are empty
            return;
        }
        builder = builder.addCategory(PLUGINS_BRANCH_PERMISSIONS);
        for (RefRestriction ref : restrictions.getValues()) {
            SupportInfoBuilder permissionBuilder = builder.addCategory(PLUGINS_BRANCH_PERMISSION);
            permissionBuilder.addValue(PLUGINS_BRANCH_PERMISSION_MATCHER_TYPE, ref.getMatcher().getType().getDisplayId());
            permissionBuilder.addValue(PLUGINS_BRANCH_PERMISSION_MATCHER_ID, ref.getMatcher().getId());
            permissionBuilder.addValue(PLUGINS_BRANCH_PERMISSION_RESTRICTION_TYPE, ref.getType().name());
            addAccessGrants(permissionBuilder, ref.getAccessGrants());
            permissionBuilder.addContext(ref);
        }
    }

    private static void addAccessGrants(SupportInfoBuilder permissionBuilder, Collection<AccessGrant> accessGrants) {
        if (accessGrants.isEmpty()) {
            return;
        }
        permissionBuilder = permissionBuilder.addCategory(PLUGINS_BRANCH_PERMISSION_ACCESS_GRANTS);
        SupportInfoBuilder groupBuilder = permissionBuilder.addCategory("groups");
        SupportInfoBuilder userBuilder = permissionBuilder.addCategory("users");

        AccessGrantVisitor<Void> builderVisitor = new AccessGrantVisitor<Void>() {
            @Override
            public Void visit(@Nonnull GroupAccessGrant grant) {
                addValue(groupBuilder, "group", grant.getGroup());
                return null;
            }

            @Override
            public Void visit(@Nonnull UserAccessGrant grant) {
                addValue(userBuilder, "user", grant.getUser().getName());
                return null;
            }

            private void addValue(SupportInfoBuilder builder, String categoryName, String value) {
                //Create a new category to put individual users on separate rows
                builder.addCategory(categoryName).addValue("name", value);
            }
        };

        for (AccessGrant accessGrant : accessGrants) {
            accessGrant.accept(builderVisitor);
        }
    }
}
