package com.airlock.iam.system.property;

import com.airlock.iam.system.analytics.AnalyticsMigrationMode;
import com.airlock.iam.system.analytics.AnalyticsMode;
import com.airlock.iam.system.config.ConfigFormat;
import java.net.URI;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public final class IamSystemProperties {
   public static final String XML_CONFIG_FILE_BASE_NAME = "medusa-configuration";
   public static final String XML_CONFIG_FILE_BASE_EXTENSION = ".xml";
   public static final String YAML_CONFIG_FILE_BASE_NAME = "iam-config";
   public static final String YAML_CONFIG_FILE_BASE_EXTENSION = ".yaml";
   public static final String ACTIVATED_CONFIGS_DIR_NAME = ".activated-configs";
   public static final String YAML_FORMAT_IDENTIFIER = "yaml";
   public static final String INSTANCE_XML_PLUGIN_CONFIG_FILE_NAME = "medusa-configuration.xml";
   public static final String INSTANCE_YAML_PLUGIN_CONFIG_FILE_NAME = "iam-config.yaml";
   public static final String YAML_CONFIG_SNIPPET_FILE_NAME = "iam-config-snippet.yaml";
   public static final String PLUGIN_CONFIG_FORMAT = "iam.config.format";
   public static final String SENSITIVE_VALUES_CONFIG = "iam.sensitive-values.config";
   public static final String LUA_SOCKET_DIRECTORY_PROPERTY = "iam.scriptable.lua.socket.dir";
   public static final String SHORTNAME_MAPPINGS_KEY = "iam.internal.config.shortname.mappings.dir";
   public static final String LICENSE = "iam.license";
   private static final String INSTANCES_DIR_NAME = "instances";
   private static final String IAM_MODULES = "iam.modules";
   private static final String INSTANCE_NAME = "iam.instance.name";
   private static final String ROOT_DIR = "iam.rootdir";
   private static final String WORK_DIR = "iam.workdir";
   private static final String CONFIG = "iam.config";
   private static final String ACTIVATED_CONFIGS_DIR = "iam.activated-configs.dir";
   private static final String ENVIRONMENT_ID = "iam.environment.id";
   private static final String IAM_MODE = "iam.mode";
   private static final String AUDIT_LOGGER_ENABLED = "iam.audit-log.signing.enabled";
   private static final String AUDIT_LOGGER_CONFIG = "iam.audit-log.signing.config";
   private static final String SERVICECONTAINER_URL_PATH = "iam.service-container.url.path";
   private static final String CONFIG_TEMPLATES = "iam.internal.config.template.dir";
   private static final String H2_JDBC_URL = "iam.h2.jdbc.url";
   private static final String H2_JDBC_USER = "iam.h2.jdbc.user";
   private static final String H2_JDBC_PASSWORD = "iam.h2.jdbc.password";
   private static final String LOG_LABEL = "iam.log.label";
   private static final String OLD_CONFIG_EDITOR_ENABLED = "iam.config-editor.enabled";
   private static final String OLD_CONFIG_EDITOR_STANDALONE = "iam.config-editor.standalone";
   private static final String DISABLE_ANALYTICS_OVERRIDE = "iam.analytics.disabled";
   private static final String LUA_BINARY_PATH = "iam.internal.scripting.lua.binary";
   private static final String LUA_LIBRARY_ENTRY_POINT = "iam.internal.scripting.lua.entrypoint";
   private static final String LUA_BUNDLED_SCRIPT_LIBRARIES = "iam.internal.scripting.lua.path";
   private static final String LUA_BUNDLED_SHARED_OBJECT_LIBRARIES = "iam.internal.scripting.lua.cpath";
   private static final String STRUCTURED_LOG_FILE_DIR = "iam.log.structured-file.dir";
   private static final String IAM_INTERNAL_DOCKER = "IAM_INTERNAL_DOCKER";
   private static final String ANALYTICS_MODE = "iam.analytics.mode";
   private static final String IAM_METRICS_PORT = "iam.metrics.port";

   public static Path getRootDirectory() {
      return getInitializedSystemProperty("iam.rootdir").map(IamSystemProperties::toPath).orElseThrow();
   }

   public static Path getWorkDirectory() {
      return getInitializedSystemProperty("iam.workdir").map(IamSystemProperties::toPath).orElseThrow();
   }

   public static Path getInstancesDirectory() {
      return getRootDirectory().resolve("instances");
   }

   public static String getInstanceName() {
      return getInitializedSystemProperty("iam.instance.name").orElseThrow();
   }

   public static Path getPluginConfig() {
      return isYAMLConfigFormatEnabled() ? getYAMLPluginConfigPath() : getXMLPluginConfigPath();
   }

   public static String getPluginConfigBaseName() {
      return isYAMLConfigFormatEnabled() ? "iam-config" : "medusa-configuration";
   }

   public static String getPluginConfigBaseName(ConfigFormat format) {
      return format == ConfigFormat.YAML_FORMAT ? "iam-config" : "medusa-configuration";
   }

   public static String getPluginConfigExtension() {
      return isYAMLConfigFormatEnabled() ? ".yaml" : ".xml";
   }

   public static Path getActiveInstanceDirectory() {
      return getInstancesDirectory().resolve(getInstanceName());
   }

   public static Path getConfigDirectory() {
      Path pluginConfig = getInitializedSystemProperty("iam.config").map(IamSystemProperties::toPath).orElseThrow();
      if (!pluginConfig.endsWith("medusa-configuration.xml")) {
         throw new AssertionError("Plugin configuration file must be named \"medusa-configuration.xml\": " + pluginConfig);
      } else {
         return getRootDirectory().resolve(pluginConfig.getParent());
      }
   }

   public static Path getPluginConfigDraft() {
      return isYAMLConfigFormatEnabled()
         ? getConfigDirectory().resolve(".draftiam-config.yaml")
         : getConfigDirectory().resolve(".draftmedusa-configuration.xml");
   }

   private static Path getXMLPluginConfigPath() {
      return getConfigDirectory().resolve("medusa-configuration.xml");
   }

   private static Path getYAMLPluginConfigPath() {
      return getConfigDirectory().resolve("iam-config.yaml");
   }

   public static Optional<Path> getActivatedConfigsDirectory() {
      if (System.getProperty("iam.activated-configs.dir") == null) {
         checkInitialized("iam.activated-configs.dir");
      }

      Optional<String> dir = Optional.ofNullable(System.getProperty("iam.activated-configs.dir")).map(String::strip);
      return dir.isPresent() && dir.get().isEmpty() ? Optional.empty() : Optional.of(getConfigDirectory().resolve(dir.orElse(".activated-configs")));
   }

   public static Optional<Path> getLicense() {
      return getNonEmptySystemProperty("iam.license").map(IamSystemProperties::toPath);
   }

   public static Optional<String> getInstanceEnvironment() {
      return getInitializedSystemProperty("iam.environment.id");
   }

   public static Optional<URI> getSensitiveValuesConfig() {
      return getInitializedSystemProperty("iam.sensitive-values.config").map(URI::create);
   }

   public static boolean isStrictMode() {
      return getInitializedSystemProperty("iam.mode").map("STRICT"::equalsIgnoreCase).orElse(false);
   }

   public static Optional<Path> getAuditLoggerConfig() {
      boolean enabled = getInitializedSystemProperty("iam.audit-log.signing.enabled").map(Boolean::parseBoolean).orElse(false);
      return enabled ? getInitializedSystemProperty("iam.audit-log.signing.config").map(IamSystemProperties::toPath) : Optional.empty();
   }

   public static Optional<Path> getConfigTemplates() {
      return getInitializedSystemProperty("iam.internal.config.template.dir").map(IamSystemProperties::toPath);
   }

   public static Optional<Path> getShortnameMappingsDir() {
      return getNonEmptySystemProperty("iam.internal.config.shortname.mappings.dir").map(IamSystemProperties::toPath);
   }

   public static AnalyticsMigrationMode getAnalyticsMigrationMode() {
      return getNonEmptySystemProperty("iam.analytics.mode")
         .flatMap(AnalyticsMode::toAnalyticsMode)
         .map(AnalyticsMigrationMode::fromAnalyticsMode)
         .orElse(AnalyticsMigrationMode.UNDEFINED);
   }

   public static Optional<String> getServiceContainerUrlPath() {
      return getInitializedSystemProperty("iam.service-container.url.path");
   }

   public static String getEmbeddedH2JdbcUrl() {
      return getInitializedSystemProperty("iam.h2.jdbc.url").orElse("jdbc:h2:tcp://localhost:9001/iamdb");
   }

   public static String getEmbeddedH2JdbcUser() {
      return getInitializedSystemProperty("iam.h2.jdbc.user").orElse("airlock");
   }

   public static String getEmbeddedH2JdbcPassword() {
      return getInitializedSystemProperty("iam.h2.jdbc.password").orElse("password");
   }

   public static Optional<String> getLogLabel() {
      return getNonEmptySystemProperty("iam.log.label");
   }

   public static boolean isOldConfigEditorEnabled() {
      return getInitializedSystemProperty("iam.config-editor.enabled").map(Boolean::parseBoolean).orElse(true);
   }

   public static boolean isOldConfigEditorStandalone() {
      return getInitializedSystemProperty("iam.config-editor.standalone").map(Boolean::parseBoolean).orElse(false);
   }

   public static boolean isYAMLConfigFormatEnabled() {
      return getInitializedSystemProperty("iam.config.format").map("yaml"::equals).orElse(false);
   }

   public static ConfigFormat activeConfigFormat() {
      return isYAMLConfigFormatEnabled() ? ConfigFormat.YAML_FORMAT : ConfigFormat.XML_FORMAT;
   }

   public static boolean hasDisableAnalyticsOverride() {
      return getInitializedSystemProperty("iam.analytics.disabled").map(Boolean::parseBoolean).orElse(false);
   }

   public static boolean isRunningInDocker() {
      return Boolean.parseBoolean(System.getenv("IAM_INTERNAL_DOCKER"));
   }

   public static Optional<Path> getStructuredLogFileDirectory() {
      return getInitializedSystemProperty("iam.log.structured-file.dir").map(IamSystemProperties::toPath);
   }

   public static Path getLuaBinaryPath() {
      return getInitializedSystemProperty("iam.internal.scripting.lua.binary").map(IamSystemProperties::toPath).orElseThrow();
   }

   public static Path getLuaLibraryEntryPoint() {
      return getInitializedSystemProperty("iam.internal.scripting.lua.entrypoint").map(IamSystemProperties::toPath).orElseThrow();
   }

   public static List<Path> getLuaBundledScriptLibrariesPaths() {
      return getInitializedSystemProperty("iam.internal.scripting.lua.path").map(IamSystemProperties::splitLuaPaths).orElseThrow();
   }

   public static List<Path> getLuaBundledSharedObjectsPaths() {
      return getInitializedSystemProperty("iam.internal.scripting.lua.cpath").map(IamSystemProperties::splitLuaPaths).orElseThrow();
   }

   public static Path getLuaSocketDirectoryProperty() {
      return getInitializedSystemProperty("iam.scriptable.lua.socket.dir").map(IamSystemProperties::toPath).orElseThrow();
   }

   public static boolean isMetricsEnabled() {
      return getInitializedSystemProperty("iam.metrics.port").isPresent();
   }

   private static List<Path> splitLuaPaths(String pathString) {
      return Arrays.stream(pathString.split(";")).map(IamSystemProperties::toPath).toList();
   }

   private static Optional<String> getInitializedSystemProperty(String key) {
      if (System.getProperty(key) == null) {
         checkInitialized(key);
      }

      return getNonEmptySystemProperty(key);
   }

   private static Optional<String> getNonEmptySystemProperty(String key) {
      return Optional.of(System.getProperty(key, "")).filter(value -> !value.isEmpty());
   }

   private static void checkInitialized(String property) {
      if (!"true".equals(System.getProperty("iam.internal.properties.initialized"))) {
         throw new IllegalStateException("IAM Properties not initialized while attempting to load property: " + property);
      }
   }

   private static Path toPath(String path) {
      return Path.of(path).toAbsolutePath();
   }

   private IamSystemProperties() {
      throw new AssertionError();
   }
}
