package io.jenkins.blueocean.rest.impl.pipeline.scm;

import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import hudson.model.Item;
import io.jenkins.blueocean.commons.ErrorMessage;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.impl.pipeline.ScmContentProvider;
import jenkins.branch.MultiBranchProject;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.StaplerRequest;

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

/**
 * @author Vivek Pandey
 */
public abstract class AbstractScmContentProvider extends ScmContentProvider {

    @Override
    public Object getContent(@Nonnull StaplerRequest request, @Nonnull Item item) {
        String path = StringUtils.defaultIfEmpty(request.getParameter("path"), null);
        String type = StringUtils.defaultIfEmpty(request.getParameter("type"), null);
        String repo = StringUtils.defaultIfEmpty(request.getParameter("repo"), null);
        String branch = StringUtils.defaultIfEmpty(request.getParameter("branch"),null);

        List<ErrorMessage.Error> errors = new ArrayList<>();

        if(!(item instanceof MultiBranchProject) && repo == null){
            errors.add(
                    new ErrorMessage.Error("repo",ErrorMessage.Error.ErrorCodes.MISSING.toString(),
                            String.format("repo and branch parameters are required because pipeline %s is not a multi-branch project ",
                                    item.getFullName())));
        }

        if(type != null && !type.equals("file")){
            errors.add(
                    new ErrorMessage.Error("file",ErrorMessage.Error.ErrorCodes.INVALID.toString(),
                            String.format("type %s not supported. Only 'file' type supported.", type)));
        }


        if(path == null){
            errors.add(
                    new ErrorMessage.Error("path",ErrorMessage.Error.ErrorCodes.MISSING.toString(),
                            "path is required query parameter"));
        }
        if(!errors.isEmpty()){
            throw new ServiceException.BadRequestException(
                    new ErrorMessage(400, "Failed to load scm file").addAll(errors));
        }

        ScmContentProviderParams scmParamsFromItem = getScmParamsFromItem(item);

        //if no repo param, then see if its there in given Item.
        if(repo == null && scmParamsFromItem.getRepo() == null){
            throw new ServiceException.BadRequestException("github repo could not be determine from pipeline: "+item.getFullName());
        }

        // If both, repo param and repo in pipeline scm configuration present, they better match
        if(repo != null && scmParamsFromItem.getRepo() != null && !repo.equals(scmParamsFromItem.getRepo())){
            throw new ServiceException.BadRequestException(
                    String.format("repo parameter %s doesn't match with repo in pipeline %s github configuration repo: %s ",
                            repo, item.getFullName(), scmParamsFromItem.getRepo()));
        }

        if(repo == null){
            repo = scmParamsFromItem.getRepo();
        }

        ScmGetRequest scmGetRequest = new ScmGetRequest.Builder(scmParamsFromItem.getApiUrl())
                .branch(branch)
                .owner(scmParamsFromItem.getOwner())
                .repo(repo)
                .branch(branch)
                .path(path)
                .credentials(scmParamsFromItem.getCredentials()).build();

        return getContent(scmGetRequest);
    }

    protected abstract Object getContent(ScmGetRequest request);

    protected abstract ScmContentProviderParams getScmParamsFromItem(Item item);

    public static class ScmGetRequest{
        private final String apiUrl;
        private final String owner;
        private final String repo;
        private final String branch;
        private final String path;
        private final String type;
        private final StandardUsernamePasswordCredentials credentials;

        private ScmGetRequest(@Nonnull String apiUrl,
                              @Nonnull String owner,
                              @Nonnull String repo,
                              @Nullable String branch,
                              @Nonnull String path,
                              @Nullable String type,
                              @Nonnull StandardUsernamePasswordCredentials credentials) {
            this.apiUrl = apiUrl;
            this.owner = owner;
            this.repo = repo;
            this.branch = branch;
            this.path = path;
            this.type = type;
            this.credentials = credentials;
        }

        public String getApiUrl() {
            return apiUrl;
        }

        public String getOwner() {
            return owner;
        }

        public String getRepo() {
            return repo;
        }

        public String getBranch() {
            return branch;
        }

        public String getPath() {
            return path;
        }

        public String getType() {
            return type;
        }

        public StandardUsernamePasswordCredentials getCredentials() {
            return credentials;
        }

        public static class Builder{
            private String apiUrl;
            private String owner;
            private String repo;
            private String branch;
            private String path;
            private String type;
            private StandardUsernamePasswordCredentials credentials;

            public Builder(String apiUrl) {
                this.apiUrl = apiUrl;
            }

            Builder owner(String owner){
                this.owner = owner;
                return this;
            }

            Builder repo(String repo){
                this.repo = repo;
                return this;
            }

            Builder path(String path){
                this.path = path;
                return this;
            }

            Builder branch(String branch){
                this.branch = branch;
                return this;
            }

            Builder type(String type){
                this.type = type;
                return this;
            }

            Builder credentials(StandardUsernamePasswordCredentials credentials){
                this.credentials = credentials;
                return this;
            }

            public ScmGetRequest build(){
                return new ScmGetRequest(apiUrl,owner,repo,branch,path, type, credentials);
            }

        }

    }
}