package com.airlock.iam.application.parameters.infrastructure;

import com.airlock.iam.application.parameters.domain.model.AdminappParameters;
import com.airlock.iam.application.parameters.domain.model.ApiPolicyServiceParameters;
import com.airlock.iam.application.parameters.domain.model.ApplicationParameters;
import com.airlock.iam.application.parameters.domain.model.ClientSslConnectionParameters;
import com.airlock.iam.application.parameters.domain.model.GlobalParameters;
import com.airlock.iam.application.parameters.domain.model.H2Parameters;
import com.airlock.iam.application.parameters.domain.model.HealthCheckParameters;
import com.airlock.iam.application.parameters.domain.model.IamMode;
import com.airlock.iam.application.parameters.domain.model.IamTarget;
import com.airlock.iam.application.parameters.domain.model.Log4jConfig;
import com.airlock.iam.application.parameters.domain.model.LogParameters;
import com.airlock.iam.application.parameters.domain.model.LoginappParameters;
import com.airlock.iam.application.parameters.domain.model.MetricsParameters;
import com.airlock.iam.application.parameters.domain.model.ParameterSpec;
import com.airlock.iam.application.parameters.domain.model.ParameterVariableSpec;
import com.airlock.iam.application.parameters.domain.model.SecureStore;
import com.airlock.iam.application.parameters.domain.model.ServiceContainerParameters;
import com.airlock.iam.application.parameters.domain.model.TransactionApprovalParameters;
import com.airlock.iam.application.parameters.domain.model.WebServerLogParameters;
import com.airlock.iam.application.parameters.domain.model.WebServerParameters;
import com.airlock.iam.system.config.ConfigFormat;
import com.airlock.iam.system.module.IamModule;
import com.google.common.base.Strings;
import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class ParametersBuilder {
   private static final String GLOBAL_TEMP_WORK_DIR_NAME = "tmp";
   private static final String WEB_SERVER_WORK_DIR_NAME = "ws";
   private static final String LOGIN_LAYOUT_WORK_DIR_NAME = "login-layout";
   private final IamTarget target;
   private final Map<ParameterSpec, String> parameters;

   public ParametersBuilder(IamTarget target, Map<ParameterSpec, String> parameters) {
      this.target = target;
      this.parameters = Map.copyOf(parameters);
   }

   public ApplicationParameters build() throws ParameterValidationException {
      try {
         return this.buildApplicationParameters();
      } catch (RuntimeException e) {
         throw new ParameterValidationException(e.getMessage(), e);
      }
   }

   private ApplicationParameters buildApplicationParameters() throws ParameterValidationException {
      Path workDir = ParameterParser.resolvePath(this.target.configRoot(), this.parameters.get(ParameterSpec.WORK_DIR));
      Set<IamModule> modules = ParameterParser.parseModules(this.parameters.get(ParameterSpec.MODULES));
      return new ApplicationParameters(
         this.buildInfo(),
         this.buildGlobalParameters(this.resolveWorkDir(workDir, "tmp")),
         this.buildLogging(),
         this.buildWebServer(this.resolveWorkDir(workDir, "ws")),
         this.buildHealthCheck(),
         modules.contains(IamModule.H2) ? this.buildH2() : null,
         modules.contains(IamModule.ADMINAPP) ? this.buildAdmin() : null,
         modules.contains(IamModule.LOGINAPP) ? this.buildLogin(this.resolveWorkDir(workDir, "login-layout")) : null,
         modules.contains(IamModule.TRANSACTION_APPROVAL) ? this.buildTransactionApproval() : null,
         modules.contains(IamModule.API_POLICY_SERVICE) ? this.buildApiPolicyService() : null,
         modules.contains(IamModule.SERVICE_CONTAINER) ? this.buildServiceContainer() : null,
         this.buildClientSslConnectionParameters(),
         this.buildMetrics()
      );
   }

   private Path resolveWorkDir(Path workDir, String name) {
      return workDir.resolve(this.target.id()).resolve(name).toAbsolutePath();
   }

   private String buildInfo() {
      return this.parameters
         .entrySet()
         .stream()
         .filter(p -> p.getKey().isPublic())
         .map(p -> p.getKey().systemPropertyKey() + " = " + this.maskSensitiveValue((Entry<ParameterSpec, String>)p))
         .sorted()
         .collect(Collectors.joining(System.lineSeparator()));
   }

   private String maskSensitiveValue(Entry<ParameterSpec, String> p) {
      return p.getKey().isSensitive() ? "********" : p.getValue();
   }

   private GlobalParameters buildGlobalParameters(Path tempDir) throws ParameterValidationException {
      ConfigFormat configFormat = ParameterParser.parsePluginConfigurationFormat(this.parameters.get(ParameterSpec.PLUGIN_CONFIG_FORMAT));

      Path configPath = switch (configFormat) {
         case XML_FORMAT -> ParameterParser.resolveXMLPluginConfigPath(this.target.configRoot(), this.parameters.get(ParameterSpec.PLUGIN_CONFIG));
         case YAML_FORMAT -> ParameterParser.resolveYAMLPluginConfigPath(this.target.configRoot(), this.parameters.get(ParameterSpec.PLUGIN_CONFIG));
         default -> throw new MatchException(null, null);
      };
      return new GlobalParameters(
         this.parameters.get(ParameterSpec.JAVA_OPTS),
         tempDir,
         ParameterParser.resolvePath(this.target.configRoot(), this.parameters.get(ParameterSpec.LICENSE)),
         ParameterParser.resolvePath(this.target.configRoot(), this.parameters.get(ParameterSpec.PID_FILE)),
         this.parameters.get(ParameterSpec.ENVIRONMENT_ID),
         this.target,
         configPath,
         configFormat,
         this.buildSensitiveValueConfig(),
         this.buildIamMode(),
         ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.DISABLE_ANALYTICS_OVERRIDE)
      );
   }

   private URI buildSensitiveValueConfig() throws ParameterValidationException {
      URI uri = URI.create(this.parameters.get(ParameterSpec.SENSITIVE_VALUES_CONFIG));
      switch (uri.getScheme()) {
         case "provider":
         case "secrets":
            return URI.create(
               uri.getScheme() + ":" + ParameterParser.resolvePath(this.target.configRoot(), uri.getSchemeSpecificPart()).toString().replace('\\', '/')
            );
         default:
            throw new ParameterValidationException("Invalid format in parameter value for " + ParameterSpec.SENSITIVE_VALUES_CONFIG + ": " + uri);
      }
   }

   private IamMode buildIamMode() throws ParameterValidationException {
      String value = this.parameters.get(ParameterSpec.MODE);
      return Strings.isNullOrEmpty(value)
         ? IamMode.DEFAULT
         : Stream.of(IamMode.values())
            .filter(it -> it.name().equalsIgnoreCase(value))
            .findFirst()
            .orElseThrow(() -> new ParameterValidationException("Invalid parameter value for " + ParameterSpec.MODE + ":" + value));
   }

   private LogParameters buildLogging() throws ParameterValidationException {
      return new LogParameters(
         this.parameters.get(ParameterSpec.LOG_LEVEL),
         this.parameters.get(ParameterSpec.LOG_LABEL),
         this.buildMainLog(),
         this.buildReportingLog(),
         this.buildAuditLog()
      );
   }

   private LogParameters.MainLogParameters buildMainLog() throws ParameterValidationException {
      return new LogParameters.MainLogParameters(
         ParameterParser.resolvePath(this.target.configRoot(), this.parameters.get(ParameterSpec.LOG_MAIN_DIR)),
         ParameterParser.safeParsePositiveInteger(this.parameters, ParameterSpec.LOG_MAIN_ROTATE_MAX_DAYS),
         this.parameters.get(ParameterSpec.LOG_MAIN_PATTERN)
      );
   }

   private LogParameters.StructuredLogParameters buildReportingLog() throws ParameterValidationException {
      return new LogParameters.StructuredLogParameters(
         ParameterParser.resolvePath(this.target.configRoot(), this.parameters.get(ParameterSpec.LOG_STRUCTURED_FILE_DIR)),
         ParameterParser.parseUrl(this.parameters.get(ParameterSpec.LOG_ELASTICSEARCH_URL), "http", "https", "es")
      );
   }

   private LogParameters.AuditLogParameters buildAuditLog() throws ParameterValidationException {
      return !ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.AUDIT_LOG_SIGNING_ENABLED)
         ? null
         : new LogParameters.AuditLogParameters(
            ParameterParser.resolvePath(this.target.configRoot(), this.parameters.get(ParameterSpec.AUDIT_LOG_SIGNING_CONFIG))
         );
   }

   private WebServerParameters buildWebServer(Path webServerWorkDir) throws ParameterValidationException {
      WebServerParameters webServer = new WebServerParameters(
         this.buildHttpConnector(), this.buildHttpsConnector(), webServerWorkDir, this.buildWebserverLogParameters(), this.buildAccessLogParameters()
      );
      ParameterParser.validateWebserverParameters(webServer);
      return webServer;
   }

   private WebServerParameters.ConnectorParameters buildHttpConnector() throws ParameterValidationException {
      return !ParameterParser.csvContainsElement(this.parameters.get(ParameterSpec.WEB_SERVER_CONNECTORS), "http")
         ? null
         : WebServerParameters.ConnectorParameters.builder()
            .address(this.parameters.get(ParameterSpec.WEB_SERVER_HTTP_ADDRESS))
            .port(ParameterParser.safeParsePositiveInteger(this.parameters, ParameterSpec.WEB_SERVER_HTTP_PORT))
            .maxThreads(ParameterParser.safeParsePositiveInteger(this.parameters, ParameterSpec.WEB_SERVER_MAX_THREADS))
            .maxHttpHeaderSize(ParameterParser.safeParsePositiveInteger(this.parameters, ParameterSpec.WEB_SERVER_MAX_HTTP_HEADER_SIZE))
            .compression(ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.WEB_SERVER_COMPRESSION))
            .build();
   }

   private WebServerParameters.ConnectorParameters buildHttpsConnector() throws ParameterValidationException {
      return !ParameterParser.csvContainsElement(this.parameters.get(ParameterSpec.WEB_SERVER_CONNECTORS), "https")
         ? null
         : WebServerParameters.ConnectorParameters.builder()
            .address(this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_ADDRESS))
            .port(ParameterParser.safeParsePositiveInteger(this.parameters, ParameterSpec.WEB_SERVER_HTTPS_PORT))
            .maxThreads(ParameterParser.safeParsePositiveInteger(this.parameters, ParameterSpec.WEB_SERVER_MAX_THREADS))
            .maxHttpHeaderSize(ParameterParser.safeParsePositiveInteger(this.parameters, ParameterSpec.WEB_SERVER_MAX_HTTP_HEADER_SIZE))
            .compression(ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.WEB_SERVER_COMPRESSION))
            .secureParameters(
               WebServerParameters.SecureConnectorParameters.builder()
                  .tlsProtocols(ParameterParser.parseTlsProtocols(this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_TLS_PROTOCOLS)))
                  .cipherSuites(this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_TLS_CIPHERS))
                  .keyStore(
                     WebServerParameters.secureStore(
                        this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_KEYSTORE_TYPE),
                        ParameterParser.resolvePath(this.target.configRoot(), this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_KEYSTORE_FILE)),
                        this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_KEYSTORE_PASSWORD)
                     )
                  )
                  .trustStore(this.httpsTrustStore())
                  .clientAuth(ParameterParser.parseClientAuth(this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_CLIENTAUTH)))
                  .build()
            )
            .build();
   }

   private SecureStore httpsTrustStore() {
      return this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_TRUSTSTORE_FILE) == null
         ? null
         : WebServerParameters.secureStore(
            this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_TRUSTSTORE_TYPE),
            ParameterParser.resolvePath(this.target.configRoot(), this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_TRUSTSTORE_FILE)),
            this.parameters.get(ParameterSpec.WEB_SERVER_HTTPS_TRUSTSTORE_PASSWORD)
         );
   }

   private WebServerLogParameters buildWebserverLogParameters() throws ParameterValidationException {
      return this.buildLogParameters(
         ParameterSpec.WEB_SERVER_LOG_CONSOLE_ENABLED,
         ParameterSpec.WEB_SERVER_LOG_FILE_ENABLED,
         ParameterSpec.WEB_SERVER_LOG_STRUCTURED_FILE_ENABLED,
         ParameterSpec.WEB_SERVER_LOG_CONSOLE_FORMAT,
         ParameterSpec.WEB_SERVER_LOG_PATTERN,
         ParameterSpec.WEB_SERVER_LOG_DIR,
         ParameterSpec.WEB_SERVER_LOG_FILE_PREFIX,
         ParameterSpec.WEB_SERVER_LOG_FILE_SUFFIX,
         ParameterSpec.WEB_SERVER_LOG_ROTATE_MAX_DAYS,
         Optional.of(ParameterSpec.WEB_SERVER_LOG_CONSOLE_LEVEL),
         Optional.of(ParameterSpec.WEB_SERVER_LOG_FILE_LEVEL)
      );
   }

   private WebServerLogParameters buildAccessLogParameters() throws ParameterValidationException {
      return this.buildLogParameters(
         ParameterSpec.WEB_SERVER_ACCESS_LOG_CONSOLE_ENABLED,
         ParameterSpec.WEB_SERVER_ACCESS_LOG_FILE_ENABLED,
         ParameterSpec.WEB_SERVER_ACCESS_LOG_STRUCTURED_FILE_ENABLED,
         ParameterSpec.WEB_SERVER_LOG_CONSOLE_FORMAT,
         ParameterSpec.WEB_SERVER_ACCESS_LOG_PATTERN,
         ParameterSpec.WEB_SERVER_ACCESS_LOG_DIR,
         ParameterSpec.WEB_SERVER_ACCESS_LOG_FILE_PREFIX,
         ParameterSpec.WEB_SERVER_ACCESS_LOG_FILE_SUFFIX,
         ParameterSpec.WEB_SERVER_ACCESS_LOG_ROTATE_MAX_DAYS,
         Optional.empty(),
         Optional.empty()
      );
   }

   private WebServerLogParameters buildLogParameters(
      ParameterSpec paramConsoleEnabled,
      ParameterSpec paramSimpleFileEnabled,
      ParameterSpec paramStructuredFileEnabled,
      ParameterSpec paramConsoleFormat,
      ParameterSpec paramPattern,
      ParameterSpec paramDirectory,
      ParameterSpec paramPrefix,
      ParameterSpec paramSuffix,
      ParameterSpec paramMaxDays,
      Optional<ParameterSpec> paramConsoleLogLevel,
      Optional<ParameterSpec> paramFileLogLevel
   ) throws ParameterValidationException {
      boolean simpleFileEnabled = ParameterParser.safeParseBoolean(this.parameters, paramSimpleFileEnabled);
      Path directory = ParameterParser.resolveNullablePath(this.target.configRoot(), this.parameters.get(paramDirectory));
      String prefix = Strings.nullToEmpty(this.parameters.get(paramPrefix));
      String suffix = Strings.nullToEmpty(this.parameters.get(paramSuffix));
      ParameterParser.validateWebserverLogDirectory(simpleFileEnabled, directory);
      ParameterParser.validateWebserverLogFileName(simpleFileEnabled, prefix, suffix);
      WebServerLogParameters.WebServerLogParametersBuilder webServerLogBuilder = WebServerLogParameters.builder()
         .simpleFileOutputEnabled(simpleFileEnabled)
         .structuredFileOutputEnabled(ParameterParser.safeParseBoolean(this.parameters, paramStructuredFileEnabled))
         .consoleOutputEnabled(ParameterParser.safeParseBoolean(this.parameters, paramConsoleEnabled))
         .consoleFormat(ParameterParser.parseWebServerLogFormat(this.parameters.get(paramConsoleFormat)))
         .directory(directory)
         .prefix(prefix)
         .suffix(suffix)
         .maxDays(ParameterParser.safeParsePositiveInteger(this.parameters, paramMaxDays))
         .pattern(ParameterParser.parseWebServerLogPattern(this.parameters.get(paramPattern)));
      paramConsoleLogLevel.ifPresent(
         consoleLogLevel -> webServerLogBuilder.consoleLogLevel(ParameterParser.parseWebServerLogLevel(this.parameters.get(consoleLogLevel)))
      );
      paramFileLogLevel.ifPresent(
         fileLogLevel -> webServerLogBuilder.fileLogLevel(ParameterParser.parseWebServerLogLevel(this.parameters.get(ParameterSpec.WEB_SERVER_LOG_FILE_LEVEL)))
      );
      return webServerLogBuilder.build();
   }

   private AdminappParameters buildAdmin() throws ParameterValidationException {
      return new AdminappParameters(
         ParameterParser.parseUrlPath(this.parameters.get(ParameterSpec.ADMINAPP_URL_PATH)),
         this.buildLog4jConfigs(ParameterSpec.ADMINAPP_LOG4J_CONFIGS),
         this.parameters.get(ParameterSpec.ADMINAPP_APPLICATION_LISTENER),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.ADMINAPP_CLASSPATH)),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.ADMINAPP_CLASSPATH_EXT)),
         ParameterParser.buildLayoutDirs(this.target.configRoot(), this.parameters.get(ParameterSpec.ADMINAPP_LAYOUT_DIRS))
      );
   }

   private LoginappParameters buildLogin(Path layoutWorkDir) throws ParameterValidationException {
      return new LoginappParameters(
         ParameterParser.parseUrlPath(this.parameters.get(ParameterSpec.LOGINAPP_URL_PATH)),
         this.buildLog4jConfigs(ParameterSpec.LOGINAPP_LOG4J_CONFIGS),
         this.parameters.get(ParameterSpec.LOGINAPP_APPLICATION_LISTENER),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.LOGINAPP_CLASSPATH)),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.LOGINAPP_CLASSPATH_EXT)),
         ParameterParser.buildLayoutDirs(this.target.configRoot(), this.parameters.get(ParameterSpec.LOGINAPP_LAYOUT_DIRS)),
         ParameterParser.resolveNullablePath(this.target.configRoot(), this.parameters.get(ParameterSpec.LOGINAPP_REST_UI_CUSTOMIZATIONS)),
         ParameterParser.resolveNullablePath(this.target.configRoot(), this.parameters.get(ParameterSpec.LOGINAPP_UI_RESOURCE_SETS)),
         layoutWorkDir,
         ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.LOGINAPP_COOKIES_ALLOWEQUALSINVALUE_ENABLED),
         ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.LOGINAPP_COOKIES_ALLOWHTTPSEPSINV0_ENABLED)
      );
   }

   private TransactionApprovalParameters buildTransactionApproval() throws ParameterValidationException {
      return new TransactionApprovalParameters(
         ParameterParser.parseUrlPath(this.parameters.get(ParameterSpec.TRANSACTION_APPROVAL_URL_PATH)),
         this.buildLog4jConfigs(ParameterSpec.TRANSACTION_APPROVAL_LOG4J_CONFIGS),
         this.parameters.get(ParameterSpec.TRANSACTION_APPROVAL_LISTENER),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.TRANSACTION_APPROVAL_CLASSPATH)),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.TRANSACTION_APPROVAL_CLASSPATH_EXT))
      );
   }

   private ApiPolicyServiceParameters buildApiPolicyService() throws ParameterValidationException {
      return new ApiPolicyServiceParameters(
         ParameterParser.parseUrlPath(this.parameters.get(ParameterSpec.API_POLICY_SERVICE_URL_PATH)),
         this.buildLog4jConfigs(ParameterSpec.API_POLICY_SERVICE_LOG4J_CONFIGS),
         this.parameters.get(ParameterSpec.API_POLICY_SERVICE_LISTENER),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.API_POLICY_SERVICE_CLASSPATH)),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.API_POLICY_SERVICE_CLASSPATH_EXT))
      );
   }

   private ServiceContainerParameters buildServiceContainer() throws ParameterValidationException {
      return new ServiceContainerParameters(
         ParameterParser.parseUrlPath(this.parameters.get(ParameterSpec.SERVICE_CONTAINER_URL_PATH)),
         this.buildLog4jConfigs(ParameterSpec.SERVICE_CONTAINER_LOG4J_CONFIGS),
         this.parameters.get(ParameterSpec.SERVICE_CONTAINER_APPLICATION_LISTENER),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.SERVICE_CONTAINER_CLASSPATH)),
         ParameterParser.requireValidClasspath(this.target.configRoot(), this.parameters.get(ParameterSpec.SERVICE_CONTAINER_CLASSPATH_EXT)),
         ParameterParser.buildLayoutDirs(this.target.configRoot(), this.parameters.get(ParameterSpec.SERVICE_CONTAINER_LAYOUT_DIRS))
      );
   }

   private List<Log4jConfig> buildLog4jConfigs(ParameterSpec parameter) throws ParameterValidationException {
      List<Log4jConfig> log4jConfigs = new ArrayList<>(ParameterParser.parseLog4jConfigs(this.target.configRoot(), this.parameters.get(parameter)));
      if (this.isLogStructuredFileEnabled()) {
         log4jConfigs.addFirst(Log4jConfig.referencedBy(ParameterVariableSpec.LOG4J_STRUCTURED_FILE));
      }

      if (ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.LOG_STRUCTURED_STDOUT_ENABLED)) {
         log4jConfigs.addFirst(Log4jConfig.referencedBy(ParameterVariableSpec.LOG4J_STRUCTURED_STDOUT));
      }

      if (ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.LOG_MAIN_ENABLED)) {
         log4jConfigs.addFirst(Log4jConfig.referencedBy(ParameterVariableSpec.LOG4J_MAIN));
      }

      return log4jConfigs;
   }

   private boolean isLogStructuredFileEnabled() throws ParameterValidationException {
      return ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.LOG_STRUCTURED_FILE_ENABLED)
         ? true
         : ParameterParser.parseUrl(this.parameters.get(ParameterSpec.LOG_ELASTICSEARCH_URL), "http", "https", "es") != null;
   }

   private ClientSslConnectionParameters buildClientSslConnectionParameters() {
      return new ClientSslConnectionParameters(this.httpsTrustStore());
   }

   private MetricsParameters buildMetrics() {
      return Optional.ofNullable(this.parameters.get(ParameterSpec.METRICS_PORT))
         .map(unused -> ParameterParser.safeParsePositiveInteger(this.parameters, ParameterSpec.METRICS_PORT))
         .map(MetricsParameters::new)
         .orElse(null);
   }

   private H2Parameters buildH2() throws ParameterValidationException {
      return new H2Parameters(
         ParameterParser.parseUrlPath(this.parameters.get(ParameterSpec.H2_URL_PATH)),
         ParameterParser.resolvePath(this.target.configRoot(), this.parameters.get(ParameterSpec.H2_DATA_DIR)),
         ParameterParser.parseJdbcUrl(this.parameters.get(ParameterSpec.H2_JDBC_URL)),
         this.parameters.get(ParameterSpec.H2_JDBC_USER),
         this.parameters.get(ParameterSpec.H2_JDBC_PASSWORD),
         ParameterParser.safeParseBoolean(this.parameters, ParameterSpec.H2_SKIP_ADMIN_PASSWORD_CHANGE)
      );
   }

   private HealthCheckParameters buildHealthCheck() throws ParameterValidationException {
      return new HealthCheckParameters(
         this.parameters.get(ParameterSpec.HEALTH_ADDRESS),
         Optional.ofNullable(this.parameters.get(ParameterSpec.HEALTH_PORT))
            .map(unused -> ParameterParser.safeParsePositiveInteger(this.parameters, ParameterSpec.HEALTH_PORT))
      );
   }
}
