package hudson.plugins.git.extensions.impl;

import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.Result;
import hudson.model.StringParameterDefinition;
import hudson.plugins.git.AbstractGitTestCase;
import hudson.plugins.git.BranchSpec;
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.UserRemoteConfig;
import hudson.tasks.BatchFile;
import hudson.tasks.Builder;
import hudson.tasks.Shell;
import org.jenkinsci.plugins.gitclient.JGitTool;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.jvnet.hudson.test.Issue;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;

@RunWith(Parameterized.class)
public class CloneOptionHonorRefSpecTest extends AbstractGitTestCase {

    private final String refSpecName;
    private final Boolean honorRefSpec;

    private static final Random random = new Random();
    private FreeStyleProject project;
    private String refSpecExpectedValue;

    public CloneOptionHonorRefSpecTest(String refSpecName, Boolean honorRefSpec) {
        this.refSpecName = refSpecName;
        this.honorRefSpec = honorRefSpec;
    }

    @Parameterized.Parameters(name = "{0}-{1}")
    public static Collection permuteRefSpecVariable() {
        List<Object[]> values = new ArrayList<>();
        // Should behave same with honor refspec enabled or disabled
        boolean honorRefSpec = random.nextBoolean();

        String[] keys = {
                "JOB_NAME", // Variable set by Jenkins
                (isWindows() ? "USERNAME" : "USER"), // Variable set by the operating system
                "USER_SELECTED_BRANCH_NAME" // Parametrised build param
        };

        for (String refSpecName : keys) {
            Object[] combination = {refSpecName, true};
            values.add(combination);
            honorRefSpec = !honorRefSpec;
        }

        return values;
    }

    private static Builder createEnvEchoBuilder(String envVarName) {
        if (isWindows()) {
            return new BatchFile(String.format("echo %s=%%%s%%", envVarName, envVarName));
        }
        return new Shell(String.format("echo \"%s=${%s}\"", envVarName, envVarName));
    }

    @Before
    public void setUp() throws Exception {
        super.setUp();

        // Setup job beforehand to get expected value of the environment variable
        project = createFreeStyleProject();
        project.addProperty(new ParametersDefinitionProperty(
                new StringParameterDefinition("USER_SELECTED_BRANCH_NAME", "user_branch")
        ));
        project.getBuildersList().add(createEnvEchoBuilder(refSpecName));

        final FreeStyleBuild b = project.scheduleBuild2(0).get();
        rule.assertBuildStatus(Result.SUCCESS, b);

        List<String> logs = b.getLog(50);
        for (String line : logs) {
            if (line.startsWith(refSpecName + '=')) {
                refSpecExpectedValue = line.split("=")[1];
            }
        }

        if (refSpecExpectedValue == null) {
            throw new Exception("Could not obtain env var expected value");
        }
    }

    /**
     * This test confirms behavior of refspecs on initial clone with expanded
     * variables. When an environment variable reference is embedded in the
     * refspec, it should be expanded in all cases.
     *
     * @throws Exception on error
     */
    @Test
    @Issue("JENKINS-56063")
    public void testRefSpecWithExpandedVariables() throws Exception {
        // Create initial commit
        final String commitFile1 = "commitFile1";
        commit(commitFile1, johnDoe, "Commit in master branch");

        // Create branch and make initial commit
        git.checkout().ref("master").branch(refSpecExpectedValue).execute();
        commit(commitFile1, johnDoe, "Commit in '" + refSpecExpectedValue + "' branch");

        // Use the variable reference in the refspec
        // Should be expanded by the clone option whether or not honor refspec is enabled
        List<UserRemoteConfig> repos = new ArrayList<>();
        repos.add(new UserRemoteConfig(
                testRepo.gitDir.getAbsolutePath(),
                "origin",
                "+refs/heads/${" + refSpecName + "}:refs/remotes/origin/${" + refSpecName + "}", null));

        /* Use the variable or its value as the branch name.
         * Same result expected in either case.
         */
        String branchName = random.nextBoolean() ? "${" + refSpecName + "}" : refSpecExpectedValue;
        GitSCM scm = new GitSCM(
                repos,
                Collections.singletonList(new BranchSpec(branchName)),
                false, Collections.emptyList(),
                null, random.nextBoolean() ? JGitTool.MAGIC_EXENAME : null,
                Collections.emptyList());
        project.setScm(scm);

        // Same result expected whether refspec honored or not
        CloneOption cloneOption = new CloneOption(false, null, null);
        cloneOption.setHonorRefspec(honorRefSpec);
        scm.getExtensions().add(cloneOption);

        FreeStyleBuild b = build(project, Result.SUCCESS, commitFile1);

        // Check that unexpanded refspec name is not in the log
        List<String> buildLog = b.getLog(50);
        assertThat(buildLog, not(hasItem(containsString("${" + refSpecName + "}"))));
    }

    private static boolean isWindows() {
        return File.pathSeparatorChar == ';';
    }
}

@@ -1077,48 +1077,8 @@
 ame;
-%0A    private final Boolean honorRefSpec;
 %0A%0A  
@@ -1271,30 +1271,8 @@
 Name
-, Boolean honorRefSpec
 ) %7B%0A
@@ -1315,50 +1315,8 @@
 me;%0A
-        this.honorRefSpec = honorRefSpec;%0A
     
@@ -1363,12 +1363,8 @@
 %22%7B0%7D
--%7B1%7D
 %22)%0A 
@@ -1472,130 +1472,8 @@
 %3E();
-%0A        // Should behave same with honor refspec enabled or disabled%0A        boolean honorRefSpec = random.nextBoolean();
 %0A%0A  
@@ -1821,14 +1821,8 @@
 Name
-, true
 %7D;%0A 
@@ -1861,50 +1861,8 @@
 n);%0A
-            honorRefSpec = !honorRefSpec;%0A
     
@@ -4979,20 +4979,12 @@
 pec(
-honorRefSpec
+true
 );%0A 

Remove dead code randomizing honor refSpec test