package nu.studer.teamcity.buildscan.agent;

import com.intellij.openapi.diagnostic.Logger;
import jetbrains.buildServer.agent.BuildRunnerContext;
import jetbrains.buildServer.agent.ToolCannotBeFoundException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * Executes Maven commands and returns their output, both standard output and standard error, when given a {@link BuildRunnerContext}.
 */
final class MavenCommandExecutor {

    private static final Logger LOG = Logger.getInstance("jetbrains.buildServer.BUILDSCAN");

    private final BuildRunnerContext runnerContext;

    MavenCommandExecutor(BuildRunnerContext context) {
        this.runnerContext = context;
    }

    @NotNull
    Result execute(String args) throws IOException, InterruptedException {
        File mavenExec = getMvnExec();
        if (mavenExec == null) {
            return new Result();
        }

        String command = mavenExec.getAbsolutePath() + " " + args;
        ProcessBuilder processBuilder = new ProcessBuilder(command.split(" ")).redirectErrorStream(true);
        Process process = processBuilder.start();
        process.waitFor();

        return new Result(process);
    }

    @Nullable
    private File getMvnExec() {
        String mavenPath;
        try {
            mavenPath = runnerContext.getToolPath("maven");
        } catch (ToolCannotBeFoundException e) {
            return null;
        }

        File installationBinDir = new File(mavenPath, "bin");
        String mvnExecutableName = System.getProperty("os.name").toLowerCase().contains("win") ? "mvn.cmd" : "mvn";
        return new File(installationBinDir, mvnExecutableName);
    }

    static class Result {

        private final Process process;

        Result() {
            process = null;
        }

        Result(Process process) {
            this.process = process;
        }

        boolean isSuccessful() {
            if (process == null) {
                return false;
            }

            return process.exitValue() == 0;
        }

        @NotNull
        String getOutput() {
            if (process == null || !isSuccessful()) {
                return "";
            }

            StringBuilder sb = new StringBuilder();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            try {
                // this logic eagerly consumes the entire output into memory, which should not be an issue when only
                // used for `mvn --version`, which generates ~5 lines of output
                // this should be revisited if other commands are executed here

                int ch;
                while ((ch = reader.read()) != -1) {
                    sb.append((char) ch);
                }
            } catch (IOException e) {
                LOG.warn("Unable to read output from Maven process", e);
            } finally {
                try {
                    reader.close();
                } catch (IOException e) {
                    LOG.warn("Unable to close reader", e);
                }
            }

            return sb.toString();
        }

    }

}

@@ -1009,26 +1009,36 @@
  return 
-new 
 Result
+.didNotExecute
 ();%0A    
@@ -1290,25 +1290,24 @@
 .waitFor();%0A
-%0A
         retu
@@ -1313,18 +1313,33 @@
 urn 
-new 
 Result
+.forExecutedProcess
 (pro
@@ -1885,34 +1885,33 @@
 nal 
-Process pro
+boolean suc
 cess;%0A
-%0A
         
 Resu
@@ -1910,88 +1910,117 @@
     
-Result() %7B%0A            process = null;%0A        %7D%0A%0A        Result(Process process
+@NotNull%0A        private final String output;%0A%0A        private Result(boolean success, @NotNull String output
 ) %7B%0A
@@ -2036,29 +2036,29 @@
    this.
-pro
+suc
 cess = 
-pro
+suc
 cess;%0A  
@@ -2067,56 +2067,53 @@
     
-%7D%0A%0A
     
-    boolean isSuccessful() %7B%0A
+this.output = output;%0A    
     
+%7D%0A%0A
         
 if (
@@ -2112,120 +2112,152 @@
     
-if (process == null) %7B%0A                return false;%0A            %7D%0A%0A            return process.exitValue() == 0;
+static Result didNotExecute() %7B%0A            return new Result(false, %22%22);%0A        %7D%0A%0A        static Result forExecutedProcess(Process process) %7B
 %0A   
@@ -2265,93 +2265,141 @@
     
-%7D%0A%0A
     
-    @NotNull%0A        String getOutput() %7B%0A            if (process == null %7C%7C !
+String output = readOutput(process);%0A            return new Result(process.exitValue() == 0, output);%0A        %7D%0A%0A        boolean 
 isSu
@@ -2408,17 +2408,16 @@
 essful()
-)
  %7B%0A     
@@ -2427,21 +2427,22 @@
     
- 
-   
 return 
-%22%22
+success
 ;%0A  
@@ -2443,35 +2443,44 @@
 ss;%0A        
+%7D%0A%0A
     
-%7D%0A%0A
     
+@NotNull%0A
         Stri
@@ -2485,147 +2485,137 @@
 ring
-Builder sb = new StringBuilder();%0A%0A            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ getOutput() %7B%0A            return output;%0A        %7D%0A%0A        @NotNull%0A        private static String readOutput(Process process) %7B
 %0A   
@@ -2619,34 +2619,65 @@
             
+S
 tr
-y %7B%0A    
+ingBuilder sb = new StringBuilder();%0A%0A
             
@@ -2777,20 +2777,16 @@
 en only%0A
-    
         
@@ -2857,36 +2857,32 @@
 put%0A            
-    
 // this should b
@@ -2930,16 +2930,139 @@
 d here%0A%0A
+            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));%0A            try %7B%0A
         
@@ -3477,16 +3477,33 @@
 e reader
+ of Maven process
 %22, e);%0A 

Change Result object creation a bit