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

import com.atlassian.bitbucket.repository.NoSuchRepositoryException;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.i18n.KeyedMessage;
import com.atlassian.stash.internal.project.InternalProjectService;
import com.atlassian.stash.internal.scm.InternalScmService;
import com.atlassian.stash.internal.web.soy.StashSoyResponseBuilder;
import com.atlassian.bitbucket.project.Project;
import com.atlassian.bitbucket.project.ProjectType;
import com.atlassian.bitbucket.repository.RefService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.repository.RepositoryService;
import com.atlassian.bitbucket.scm.ScmFeature;
import com.atlassian.bitbucket.scm.ScmService;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.permission.PermissionService;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class RepositoryControllerSupport extends ControllerSupport {

    public static final String EMPTY_REPO_VIEW = "bitbucket.internal.page.emptyRepository";

    protected static final String NO_TARGET_REPO_ID_AS_STRING = "-1";
    protected static final int NO_TARGET_REPO_ID = Integer.parseInt(NO_TARGET_REPO_ID_AS_STRING);

    protected final RefService refService;
    protected final PermissionService permissionService;
    protected final InternalProjectService projectService;
    protected final RepositoryService repositoryService;
    protected final InternalScmService scmService;

    protected RepositoryControllerSupport(I18nService i18nService, RefService refService,
                                          PermissionService permissionService, InternalProjectService projectService,
                                          RepositoryService repositoryService, InternalScmService scmService) {
        super(i18nService);

        this.refService = refService;
        this.projectService = projectService;
        this.repositoryService = repositoryService;
        this.permissionService = permissionService;
        this.scmService = scmService;
    }

    @Nonnull
    protected Project getProject(int projectId) {
        Project project = projectService.getById(projectId);
        if (project == null) {
            throw newNoSuchProjectException(projectId);
        }
        return project;
    }

    @Nonnull
    protected Project getProject(String projectKey) {
        Project project = projectService.getByKey(projectKey);
        if (project == null) {
            throw newNoSuchProjectException(projectKey);
        }
        return project;
    }

    @Nonnull
    protected Repository getRepository(int repoId) {
        Repository repository = repositoryService.getById(repoId);
        if (repository == null) {
            throw newNoSuchRepositoryException(repoId);
        }
        return repository;
    }

    @Nonnull
    protected Repository getRepository(String projectKey, String repoSlug) {
        Repository repository = repositoryService.getBySlug(projectKey, repoSlug);
        if (repository == null) {
            Project project = getProject(projectKey);

            throw newNoSuchRepositoryException(project, repoSlug);
        }
        return repository;
    }

    @Nullable
    protected Repository getSecondaryRepository(@Nonnull WebRequest request, @Nonnull Repository primary) {
        String id = request.getParameter("secondaryRepositoryId");
        if (id != null) {
            int repoId = Integer.parseInt(id);
            Repository repository = repositoryService.getById(repoId);
            if (repository == null) {
                throw newNoSuchRepositoryException(repoId);
            }
            return repository;
        }

        String projectKey = request.getParameter("secondaryProjectKey");
        String repositorySlug = request.getParameter("secondaryRepositorySlug");
        if (projectKey == null) {
            projectKey = primary.getProject().getKey();
        }
        if (repositorySlug == null) {
            repositorySlug = primary.getSlug();
        }

        if (projectKey.equalsIgnoreCase(primary.getProject().getKey()) &&
                repositorySlug.equalsIgnoreCase(primary.getSlug())) {
            //The specified secondary repository is the same as the primary; just ignore it
            return null;
        }

        Repository repository = repositoryService.getBySlug(projectKey, repositorySlug);
        if (repository == null) {
            Project project = getProject(projectKey);
            KeyedMessage message = i18nService.createKeyedMessage("bitbucket.web.nosuchrepo.secondarybyslug", projectKey, repositorySlug);

            throw new NoSuchRepositoryException(message, project);
        }
        return repository;
    }

    @Nonnull
    protected Repository getTargetRepository(@Nonnull Repository repository, int targetRepoId) {
        Repository toRepository = repository;

        //If the SCM does not support cross-repository operations, the target repository is always the source
        //repository _regardless of requested targets or fork status_
        if (isScmFeatureSupported(repository, ScmFeature.CROSS_REPOSITORY)) {
            boolean targeted = false;
            if (targetRepoId != NO_TARGET_REPO_ID) {
                Repository targetRepository = repositoryService.getById(targetRepoId);
                if (targetRepository != null && repository.getHierarchyId().equals(targetRepository.getHierarchyId())) {
                    toRepository = targetRepository;
                    targeted = true;
                }
            }

            if (!targeted && repository.isFork() && repository.getProject().getType() == ProjectType.PERSONAL &&
                    permissionService.hasRepositoryPermission(repository.getOrigin(), Permission.REPO_READ)) {
                //repo is a personal fork and the user can access the origin, so set the target repo to the origin.
                toRepository = repository.getOrigin();
            }
        }

        return toRepository;
    }

    protected boolean isEmptyRepository(Repository repository) {
        return repository.getState() != Repository.State.AVAILABLE || repositoryService.isEmpty(repository);
    }

    protected boolean isScmFeatureSupported(Repository repository, ScmFeature feature) {
        return scmService.isSupported(repository, feature);
    }

    protected ModelAndView handleEmptyRepo(Repository repository) {
        return new StashSoyResponseBuilder(EMPTY_REPO_VIEW)
                .putRepository(repository)
                .put("notInitialised", repository.getState() != Repository.State.AVAILABLE)
                .build();
    }
}
