package org.jenkinsci.plugins.scripttrigger.groovy;

import antlr.ANTLRException;
import hudson.Extension;
import hudson.FilePath;
import hudson.Util;
import hudson.console.AnnotatedLargeText;
import hudson.model.*;
import hudson.remoting.VirtualChannel;
import hudson.security.ACL;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContextHolder;
import org.apache.commons.jelly.XMLOutput;
import org.jenkinsci.lib.envinject.EnvInjectException;
import org.jenkinsci.lib.envinject.service.EnvVarsResolver;
import org.jenkinsci.lib.xtrigger.XTriggerDescriptor;
import org.jenkinsci.lib.xtrigger.XTriggerLog;
import org.jenkinsci.plugins.scripttrigger.AbstractTrigger;
import org.jenkinsci.plugins.scripttrigger.LabelRestrictionClass;
import org.jenkinsci.plugins.scripttrigger.ScriptTriggerException;
import org.kohsuke.stapler.DataBoundConstructor;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;


/**
 * @author Gregory Boissinot
 */
public class GroovyScriptTrigger extends AbstractTrigger {

    private final String groovyExpression;

    private final String groovyFilePath;

    private final String propertiesFilePath;

    private final boolean groovySystemScript;

    @DataBoundConstructor
    @SuppressWarnings("unused")
    public GroovyScriptTrigger(String cronTabSpec, LabelRestrictionClass labelRestriction, boolean enableConcurrentBuild, String groovyExpression, String groovyFilePath, String propertiesFilePath, boolean groovySystemScript) throws ANTLRException {
        super(cronTabSpec, (labelRestriction == null) ? false : true, (labelRestriction == null) ? null : labelRestriction.getTriggerLabel(), enableConcurrentBuild);
        this.groovyExpression = Util.fixEmpty(groovyExpression);
        this.groovyFilePath = Util.fixEmpty(groovyFilePath);
        this.propertiesFilePath = Util.fixEmpty(propertiesFilePath);
        this.groovySystemScript = groovySystemScript;
    }

    @SuppressWarnings("unused")
    public String getGroovyExpression() {
        return groovyExpression;
    }

    @SuppressWarnings("unused")
    public String getGroovyFilePath() {
        return groovyFilePath;
    }

    @SuppressWarnings("unused")
    public String getPropertiesFilePath() {
        return propertiesFilePath;
    }

    public boolean isGroovySystemScript() {
        return groovySystemScript;
    }

    @Override
    public Collection<? extends Action> getProjectActions() {
        GroovyScriptTriggerAction action = new InternalGroovyScriptTriggerAction(getDescriptor().getDisplayName());
        return Collections.singleton(action);
    }

    public final class InternalGroovyScriptTriggerAction extends GroovyScriptTriggerAction {

        private transient String actionTitle;

        public InternalGroovyScriptTriggerAction(String actionTitle) {
            this.actionTitle = actionTitle;
        }

        @SuppressWarnings("unused")
        public AbstractProject<?, ?> getOwner() {
            return (AbstractProject) job;
        }

        @Override
        public String getDisplayName() {
            return "GroovyScriptTrigger Log";
        }

        @Override
        public String getUrlName() {
            return "groovyScripttriggerPollLog";
        }

        @Override
        public String getIconFileName() {
            return "clipboard.gif";
        }

        @SuppressWarnings("unused")
        public String getLabel() {
            return actionTitle;
        }

        @SuppressWarnings("unused")
        public String getLog() throws IOException {
            return Util.loadFile(getLogFile());
        }

        @SuppressWarnings("unused")
        public void writeLogTo(XMLOutput out) throws IOException {
            new AnnotatedLargeText<InternalGroovyScriptTriggerAction>(getLogFile(), Charset.defaultCharset(), true, this).writeHtmlTo(0, out.asWriter());
        }
    }


    @Override
    protected File getLogFile() {
        return new File(job.getRootDir(), "groovyScriptTrigger-polling.log");
    }

    @Override
    protected String getDefaultMessageCause() {
        return "Groovy Expression evaluation to true.";
    }

    @Override
    protected Action[] getScheduledActions(Node pollingNode, final XTriggerLog log) throws ScriptTriggerException {

        if (propertiesFilePath != null) {
            try {
                FilePath rootPath = pollingNode.getRootPath();
                if (rootPath == null) {
                    throw new ScriptTriggerException("The node is offline.");
                }
                return rootPath.act(new FilePath.FileCallable<Action[]>() {

                    public Action[] invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
                        File propFile = new File(propertiesFilePath);
                        if (!propFile.exists()) {
                            log.info(String.format("Can't load the properties file '%s'. It doesn't exist.", f.getPath()));
                            return null;
                        }

                        Properties properties = new Properties();
                        FileInputStream fis = new FileInputStream(propFile);
                        properties.load(fis);
                        fis.close();

                        List<ParameterValue> parameterValueList = new ArrayList<ParameterValue>();
                        for (Map.Entry property : properties.entrySet()) {
                            ParameterValue parameterValue = new StringParameterValue(
                                    String.valueOf(property.getKey()),
                                    String.valueOf(property.getValue()));
                            parameterValueList.add(parameterValue);
                        }
                        return new Action[]{new ParametersAction(parameterValueList)};
                    }
                });
            } catch (IOException ioe) {
                throw new ScriptTriggerException(ioe);
            } catch (InterruptedException ie) {
                throw new ScriptTriggerException(ie);
            }
        }
        return new Action[0];
    }


    @Override
    protected boolean checkIfModified(Node pollingNode, XTriggerLog log) throws ScriptTriggerException {
        final Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
        SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM);
        try {

            GroovyScriptTriggerExecutor executor = getGroovyScriptTriggerExecutor(log);
            final AbstractProject proj = (AbstractProject) job;

            EnvVarsResolver envVarsResolver = new EnvVarsResolver();
            Map<String, String> envVars;
            try {
                envVars = envVarsResolver.getPollingEnvVars(proj, pollingNode);
            } catch (EnvInjectException e) {
                throw new ScriptTriggerException(e);
            }

            if (groovyExpression != null) {
                boolean evaluationSucceed = executor.evaluateGroovyScript(pollingNode, proj, getGroovyExpression(), envVars, groovySystemScript);
                if (evaluationSucceed) {
                    return true;
                }
            }

            if (groovyFilePath != null) {
                boolean evaluationSucceed = executor.evaluateGroovyScriptFilePath(pollingNode, proj, Util.replaceMacro(groovyFilePath, envVars), envVars, groovySystemScript);
                if (evaluationSucceed) {
                    return true;
                }
            }

            return false;
        } finally {
            SecurityContextHolder.getContext().setAuthentication(existingAuth);
        }
    }

    private GroovyScriptTriggerExecutor getGroovyScriptTriggerExecutor(XTriggerLog log) throws ScriptTriggerException {
        return new GroovyScriptTriggerExecutor(log);
    }

    @Extension
    @SuppressWarnings("unused")
    public static class GroovyScriptTriggerDescriptor extends XTriggerDescriptor {

        @Override
        public boolean isApplicable(Item item) {
            return true;
        }

        @Override
        public String getDisplayName() {
            return "[ScriptTrigger] - Poll with a Groovy script";
        }

        @Override
        public String getHelpFile() {
            return "/plugin/scripttrigger/help-groovyScript.html";
        }
    }
}

@@ -1010,16 +1010,47 @@
 til.*;%0A%0A
+import jenkins.model.Jenkins;%0A%0A
 %0A/**%0A * 
@@ -4484,16 +4484,394 @@
 otPath =
+ null;%0A                if (this.isGroovySystemScript()) %7B%0A                    log.info(%22System script.%22);%0A                    // In the case of a system script we run on the master all the time...%0A                    rootPath = Jenkins.getInstance().getRootPath();%0A                %7D else %7B%0A                    log.info(%22Script executed on node.%22);%0A                    rootPath =
  polling
@@ -4882,32 +4882,50 @@
 .getRootPath();%0A
+                %7D%0A
                 

Fix proposal for JENKINS-19379