package com.github.swissquote.carnotzet.core.runtime;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.concurrent.TimeoutException;

import org.zeroturnaround.exec.ProcessExecutor;
import org.zeroturnaround.exec.ProcessResult;
import org.zeroturnaround.exec.stream.slf4j.Slf4jStream;

import com.github.swissquote.carnotzet.core.CarnotzetDefinitionException;
import com.google.common.base.Joiner;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
public final class DefaultCommandRunner implements CommandRunner {

	public static final DefaultCommandRunner INSTANCE = new DefaultCommandRunner();

	private DefaultCommandRunner() {
	}

	public int runCommand(String... command) {
		return runCommand(new File("/"), command);
	}

	public int runCommand(Boolean inheritIo, String... command) {
		return runCommand(inheritIo, new File("/"), command);
	}

	public int runCommand(File directoryForRunning, String... command) {
		return runCommand(true, directoryForRunning, command);
	}

	public int runCommand(Boolean inheritIo, File directoryForRunning, String... command) {
		log.debug("Running command [{}]", Joiner.on(" ").join(command));
		ProcessExecutor pe = new ProcessExecutor()
				.command(command)
				.directory(directoryForRunning);

		// by default, zt-exec pumps the logs to a nullOutputStream, so we don't need to pump or redirect to a file

		if (inheritIo) {
			// we forward output lines to SLF4J by default, writing to stdout can cause issue
			// (for example writing from a forked JVM when running in surefire produces error messages)
			pe = pe.redirectOutput(Slf4jStream.of(log).asInfo());
			pe = pe.redirectError(Slf4jStream.of(log).asInfo());
		}

		try {
			ProcessResult processResult = pe.execute();
			log.debug("Command completed : [{}]", Joiner.on(" ").join(command));
			return processResult.getExitValue();
		}
		catch (InterruptedException e) {
			Thread.currentThread().interrupt();
			throw new CarnotzetDefinitionException(e);
		}
		catch (IOException e) {
			throw new UncheckedIOException(e);
		}
		catch (TimeoutException e) {
			throw new CarnotzetDefinitionException(e);
		}
	}

	public String runCommandAndCaptureOutput(String... command) {
		return runCommandAndCaptureOutput(new File("/"), command);
	}

	public String runCommandAndCaptureOutput(File directoryForRunning, String... command) {
		log.debug("Running command [{}]", Joiner.on(" ").join(command));

		ProcessExecutor pe = new ProcessExecutor()
				.command(command)
				.redirectErrorStream(true)
				.directory(directoryForRunning)
				.readOutput(true);
		try {
			ProcessResult processResult = pe.execute();
			String output = processResult.outputUTF8().trim();
			if (processResult.getExitValue() != 0) {
				throw new RuntimeException("External command [" + Joiner.on(" ").join(command) + "] exited with [" + processResult.getExitValue()
						+ "], output: " + output);
			}
			return output;
		}
		catch (InterruptedException | IOException | TimeoutException e) {
			throw new RuntimeException(e);
		}

	}

}