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

import com.atlassian.bitbucket.auth.AuthenticationContext;
import com.atlassian.bitbucket.avatar.AvatarRequest;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.permission.PermissionValidationService;
import com.atlassian.bitbucket.repository.*;
import com.atlassian.bitbucket.repository.ref.restriction.RefRestriction;
import com.atlassian.bitbucket.repository.ref.restriction.RestrictionSearchRequest;
import com.atlassian.bitbucket.rest.enrich.AvatarEnricher;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageUtils;
import com.atlassian.sal.api.user.UserKey;
import com.atlassian.sal.api.usersettings.UserSettings;
import com.atlassian.sal.api.usersettings.UserSettingsService;
import com.atlassian.soy.renderer.SoyException;
import com.atlassian.soy.renderer.SoyTemplateRenderer;
import com.atlassian.stash.internal.repository.ref.restriction.DefaultRefRestrictionService;
import com.atlassian.stash.internal.repository.ref.restriction.rest.RestRefRestriction;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class RefRestrictionsServlet extends HttpServlet {

    private final ApplicationPropertiesService propertiesService;
    private final AvatarEnricher avatarEnricher;
    private final DefaultRefRestrictionService restrictionsService;
    private final RefService refService;
    private final RepositorySupplier repositorySupplier;
    private final SoyTemplateRenderer soyTemplateRenderer;
    private final AuthenticationContext authenticationContext;
    private final UserSettingsService userSettingsService;
    private final PermissionValidationService validationService;

    private static final String SHOW_FIRST_CONTACT_PAGE_KEY = "SHOW_FIRST_CONTACT_PAGE";
    private static final String PROP_FEATURE_SPLASH = "plugin.ref-restriction.feature.splash";

    public RefRestrictionsServlet(ApplicationPropertiesService propertiesService,
                                  AvatarEnricher avatarEnricher,
                                  RefService refService,
                                  RepositorySupplier repositorySupplier,
                                  DefaultRefRestrictionService restrictionsService,
                                  SoyTemplateRenderer soyTemplateRenderer,
                                  AuthenticationContext authenticationContext,
                                  UserSettingsService userSettingsService,
                                  PermissionValidationService validationService) {
        this.propertiesService = propertiesService;
        this.avatarEnricher = avatarEnricher;
        this.refService = refService;
        this.repositorySupplier = repositorySupplier;
        this.restrictionsService = restrictionsService;
        this.soyTemplateRenderer = soyTemplateRenderer;
        this.authenticationContext = authenticationContext;
        this.userSettingsService = userSettingsService;
        this.validationService = validationService;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String pathInfo = req.getPathInfo();
        if (Strings.isNullOrEmpty(pathInfo) || pathInfo.equals("/")) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        String[] pathParts = pathInfo.substring(1).split("/");
        if (pathParts.length != 2) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        String projectKey = pathParts[0];
        String repoSlug = pathParts[1];
        Repository repository = repositorySupplier.getBySlug(projectKey, repoSlug);
        if (repository == null) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        String at = req.getParameter("at");
        Branch branch = null;
        if (at != null) {
            MinimalRef ref = refService.resolveRef(new ResolveRefRequest.Builder(repository)
                    .refId(at)
                    .type(StandardRefType.BRANCH)
                    .build());
            if (ref instanceof Branch) {
                branch = (Branch) ref;
            }
        }
        doView(repository, branch, req, resp);
    }

    private void addAvatars(boolean isSecure, List<RestRefRestriction> restrictions) {
        restrictions.stream()
                .forEach(restriction -> avatarEnricher.enrich(restriction.getUsers(), new AvatarRequest(isSecure, 32)));
    }

    private void doView(Repository repository, Branch branch, HttpServletRequest req,
                        HttpServletResponse resp) throws ServletException, IOException {
        validationService.validateForRepository(repository, Permission.REPO_ADMIN);
        Page<RefRestriction> restrictions = restrictionsService.search(
                new RestrictionSearchRequest.Builder(repository).build(), PageUtils.newRequest(0, 500));

        ApplicationUser currentUser = authenticationContext.getCurrentUser();
        boolean isFirstContact = false;
        if (currentUser != null) {
            UserKey currentUserKey = UserKey.fromLong(currentUser.getId());
            UserSettings userSettings = userSettingsService.getUserSettings(currentUserKey);

            if (showSplashScreen(userSettings)) {
                isFirstContact = true;
                userSettingsService.updateUserSettings(currentUserKey, input -> {
                    input.put(SHOW_FIRST_CONTACT_PAGE_KEY, false);
                    return input.build();
                });
            }
        }

        ImmutableMap.Builder<String, Object> builder =
                ImmutableMap.<String, Object>builder()
                        .put("repository", repository)
                        .put("isFirstContact", isFirstContact)
                        .put("refRestrictions", restify(req.isSecure(), restrictions));


        if (branch != null) {
            builder.put("branch", branch);
        }
        render(resp, "bitbucket.internal.page.branchPermissions.viewBranchPermissions", builder.build());
    }

    private void render(HttpServletResponse resp, String templateName,
                        Map<String, Object> data) throws IOException, ServletException {
        resp.setContentType("text/html;charset=UTF-8");
        try {
            soyTemplateRenderer.render(resp.getWriter(),
                    "com.atlassian.bitbucket.server.bitbucket-ref-restriction:branch-permissions-serverside",
                    templateName, data);
        } catch (SoyException e) {
            Throwable cause = e.getCause();
            if (cause instanceof IOException) {
                throw (IOException) cause;
            }
            throw new ServletException(e);
        }
    }

    private Iterable<RestRefRestriction> restify(boolean isSecure,
                                                 Page<RefRestriction> restrictionPage) {
        List<RestRefRestriction> restrictions = new ArrayList<>();
        for (RefRestriction restriction : restrictionPage.getValues()) {
            restrictions.add(new RestRefRestriction(restriction));
        }
        addAvatars(isSecure, restrictions);
        return restrictions;
    }

    private boolean showSplashScreen(UserSettings userSettings) {
        return propertiesService.getPluginProperty(PROP_FEATURE_SPLASH, true) && userSettings.getBoolean(SHOW_FIRST_CONTACT_PAGE_KEY).isEmpty();
    }
}