package cz.moneta.test.testrunner;

import org.junit.platform.commons.JUnitException;
import org.junit.platform.console.options.CommandLineOptions;
import org.junit.platform.console.options.CommandLineOptionsParser;
import org.junit.platform.console.options.Details;
import org.junit.platform.console.options.PicocliCommandLineOptionsParser;
import org.junit.platform.console.tasks.ConsoleTestExecutor;
import org.junit.platform.launcher.listeners.TestExecutionSummary;

import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TestRunner {
    private final CommandLineOptionsParser commandLineOptionsParser;
    private final PrintWriter out;
    private final PrintWriter err;

    private TestRunner(CommandLineOptionsParser commandLineOptionsParser, PrintWriter out, PrintWriter err) {
        this.commandLineOptionsParser = commandLineOptionsParser;
        this.out = out;
        this.err = err;
    }

    public static void main(String... args) {
        int exitCode = execute(System.out, System.err, args).getExitCode();
        System.exit(exitCode);
    }

    private static TestRunnerExecutionResult execute(PrintStream out, PrintStream err, String... args) {
        return execute(new PrintWriter(out), new PrintWriter(err), args);
    }

    private static TestRunnerExecutionResult execute(PrintWriter out, PrintWriter err, String... args) {
        CommandLineOptionsParser parser = new PicocliCommandLineOptionsParser();
        TestRunner consoleLauncher = new TestRunner(parser, out, err);
        return consoleLauncher.execute(args);
    }

    private TestRunnerExecutionResult execute(String... args) {
        try {
            CommandLineOptions options = commandLineOptionsParser.parse(args);
            if (options.isDisplayHelp()) {
                commandLineOptionsParser.printHelp(out);
                return TestRunnerExecutionResult.success();
            }
            Map<String, String> configurationParameters = options.getConfigurationParameters();
            configurationParameters.put("junit.platform.output.capture.stdout", "true");
            options.setConfigurationParameters(configurationParameters);
            options.setIncludedClassNamePatterns(Stream.of("^(.*)$").collect(Collectors.toList()));
            options.setExcludedEngines(Stream.of("junit-vintage").collect(Collectors.toList()));
            options.setReportsDir(Paths.get("target", "xml-reports"));
            options.setDetails(Details.NONE);
            return executeTests(options, out);
        } catch (JUnitException ex) {
            err.println(ex.getMessage());
            err.println();
            commandLineOptionsParser.printHelp(err);
            return TestRunnerExecutionResult.failed();
        } finally {
            out.flush();
            err.flush();
        }
    }

    private TestRunnerExecutionResult executeTests(CommandLineOptions options, PrintWriter out) {
        try {
            TestExecutionSummary testExecutionSummary = new ConsoleTestExecutor(options).execute(out);
            return TestRunnerExecutionResult.forSummary(testExecutionSummary, options);
        } catch (Exception exception) {
            exception.printStackTrace(err);
            err.println();
            commandLineOptionsParser.printHelp(out);
        }
        return TestRunnerExecutionResult.failed();
    }
}
