package org.col.db;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.col.common.io.PathUtils;
import org.col.common.lang.Exceptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.yandex.qatools.embed.postgresql.EmbeddedPostgres;
import ru.yandex.qatools.embed.postgresql.distribution.Version;

import static java.util.Arrays.asList;

/**
 * An {@link EmbeddedPostgres} server that can be start up and inits a minimal CoL+ db.
 * If PgConfig.host is pointing to an absolute path it will be used to reuse a already unzipped, cached server instance,
 * but does not share a data directory.
 */
public class EmbeddedColPg {
  private static final Logger LOG = LoggerFactory.getLogger(EmbeddedColPg.class);

	private static final List<String> DEFAULT_ADD_PARAMS = asList(
			"-E", "SQL_ASCII",
			"--locale=C",
			"--lc-collate=C",
			"--lc-ctype=C");

	private EmbeddedPostgres postgres;
  private final PgConfig cfg;
	private Path serverDir;
	private boolean tmpServerDir;

	@Deprecated
	public EmbeddedColPg() {
		this.cfg = new PgConfig();
		cfg.host = null;
		cfg.user = "postgres";
		cfg.password = "postgres";
		cfg.database = "colplus";
	}

	public EmbeddedColPg(PgConfig cfg) {
		this.cfg = cfg;
	}

	public PgConfig getCfg() {
    return cfg;
  }

	public void start() {
		if (postgres == null) {
			startDb();
		} else {
			LOG.info("Embedded Postgres already running");
		}
	}

	private void startDb() {
		try {
			LOG.info("Starting embedded Postgres");
			Instant start = Instant.now();
			// use a cached server directory to not unzip postgres binaries on every run
			tmpServerDir = cfg.host == null;
			serverDir = tmpServerDir ? Files.createTempDirectory("colplus-pg-") : Paths.get(cfg.host);
			LOG.debug("Use embedded Postgres, server dir={}", serverDir);
			postgres = new EmbeddedPostgres(Version.V10_3);
			// assigned some free port using local socket 0
			cfg.port = new ServerSocket(0).getLocalPort();
			cfg.host = "localhost";
			cfg.maximumPoolSize = 4;
			postgres.start(EmbeddedPostgres.cachedRuntimeConfig(serverDir),
					cfg.host, cfg.port, cfg.database, cfg.user, cfg.password,
					DEFAULT_ADD_PARAMS
			);
			if (postgres.getProcess().isPresent()) {
				LOG.info("Pg started on port {}. Startup time: {} ms", cfg.port, Duration.between(start, Instant.now()).toMillis());
			} else {
				throw new IllegalStateException("Embedded postgres failed to startup");
			}

		} catch (Exception e) {
      LOG.error("Pg startup error {}: {}", e.getMessage(), cfg, e);
			stop();
			Exceptions.throwRuntime(e);
		}
	}

	public void stop() {
		if (postgres != null && postgres.getProcess().isPresent()) {
			final File dataDir = postgres.getConfig().get().storage().dbDir();
			LOG.info("Stopping embedded Postgres server={}, data={}", serverDir, dataDir);
			postgres.stop();

			if (tmpServerDir) {
				try {
					FileUtils.deleteDirectory(serverDir.toFile());
					LOG.info("Removed Postgres server directory {}", serverDir);
				} catch (IllegalArgumentException | IOException e) {
					LOG.warn("Failed to remove Postgres server directory {}", serverDir, e);
				}
			} else {
				try {
					FileUtils.deleteDirectory(dataDir);
					PathUtils.removeFileAndParentsIfEmpty(dataDir.toPath());
					LOG.info("Removed Postgres data directory {}", dataDir);
				} catch (IllegalArgumentException | IOException e) {
					LOG.warn("Failed to remove Postgres data directory {}", dataDir, e);
				}
			}
		}

	}

}

@@ -1761,14 +1761,8 @@
 %09%09//
- use a
  cac
@@ -1825,16 +1825,150 @@
 very run
+ is flawed:%0A%09%09%09// https://github.com/yandex-qatools/postgresql-embedded/issues/142%0A%0A%09%09%09// use workaround with system property instead!
 %0A%09%09%09tmpS
@@ -2090,16 +2090,94 @@
 .host);%0A
+%09%09%09System.setProperty(%22de.flapdoodle.embed.io.tmpdir%22, serverDir.toString());%0A
 %09%09%09LOG.d
@@ -2229,24 +2229,25 @@
 serverDir);%0A
+%0A
 %09%09%09postgres 
@@ -2442,14 +2442,16 @@
 e = 
-4
+3
 ;%0A%09%09%09
+//
 post
@@ -2478,22 +2478,23 @@
 ostgres.
-cached
+default
 RuntimeC
@@ -2503,23 +2503,16 @@
 fig(
-serverDir
 ),%0A%09%09%09
+//
 %09%09cf
@@ -2570,16 +2570,18 @@
 ord,%0A%09%09%09
+//
 %09%09DEFAUL
@@ -2596,16 +2596,95 @@
 RAMS%0A%09%09%09
+//);%0A%09%09%09postgres.start(cfg.host, cfg.port, cfg.database, cfg.user, cfg.password
 );%0A%09%09%09if
@@ -3632,32 +3632,54 @@
 %0A%09%09%09%09%7D%0A%09%09%09%7D else
+ if (dataDir.exists())
  %7B%0A%09%09%09%09try %7B%0A%09%09%09
@@ -3967,30 +3967,167 @@
 ataDir, e);%0A
-
 %09%09%09%09%7D%0A
+%09%09%09%7D else %7B%0A%09%09%09%09LOG.info(%22Postgres data directory %7B%7D already removed, server dir %7B%7D preserved for subsequent runs%22, dataDir, serverDir);%0A
 %09%09%09%7D%0A%09%09%7D%0A%0A%09%7D

Use system property to define cache dir