package cz.senslog.analytics.app;

import io.vertx.core.json.JsonObject;

import java.util.Objects;
import java.util.function.Function;

public final class PropertyConfig {
    private static PropertyConfig INSTANCE = null;

    private final HttpServer httpServerConfig;
    private final Database dbConfig;
    private final Auth authConfig;
    private final Modules modulesConfig;

    public static PropertyConfig getInstance() {
        return getInstance(System::getenv);
    }

    private static PropertyConfig getInstance(Function<String, String> getEnv) {
        if (INSTANCE == null) {
            INSTANCE = new PropertyConfig(
                    new HttpServer(getEnv), new Database(getEnv),
                    new Auth(getEnv), new Modules(getEnv)
            );
        }
        return INSTANCE;
    }

    private PropertyConfig(HttpServer httpServer, Database dbConfig, Auth authConfig, Modules modulesConfig) {
        this.httpServerConfig = httpServer;
        this.dbConfig = dbConfig;
        this.authConfig = authConfig;
        this.modulesConfig = modulesConfig;
    }

    public static class HttpServer {

        private HttpServer(Function<String, String> getEnv) {
            this.getEnv = getEnv;
        }

        private final Function<String, String> getEnv;

        public int getPort() {
            String portStr = getEnv.apply("SERVER_HTTP_PORT");
            return portStr != null ? Integer.parseInt(portStr) : 8080;
        }
    }

    public static class Database {

        private Database(Function<String, String> getEnv) {
            this.getEnv = getEnv;
        }

        private final Function<String, String> getEnv;

        public int getPort() {
            String portStr = getEnv.apply("DATABASE_PORT");
            return portStr != null ? Integer.parseInt(portStr) : 5432;
        }

        public String getHost() {
            String host = getEnv.apply("DATABASE_HOST");
            return host != null ? host : "127.0.0.1";
        }

        public String getDatabase() {
            String name = getEnv.apply("DATABASE_NAME");
            return Objects.requireNonNull(name, "System environmental variable 'DATABASE_NAME' is not set.");
        }

        public String getUser() {
            String user = getEnv.apply("DATABASE_USER");
            return Objects.requireNonNull(user, "System environmental variable 'DATABASE_USER' is not set.");
        }

        public String getPassword() {
            String passwd = getEnv.apply("DATABASE_PASSWORD");
            return Objects.requireNonNull(passwd, "System environmental variable 'DATABASE_PASSWORD' is not set.");
        }

        public int getPoolSize() {
            String poolSizeStr = getEnv.apply("DATABASE_POOL_SIZE");
            return poolSizeStr != null ? Integer.parseInt(poolSizeStr) : 5;
        }
    }

    public static class Auth {

        private Auth(Function<String, String> getEnv) {
            this.getEnv = getEnv;
        }

        private final Function<String, String> getEnv;

        public boolean getDisabled() {
            return Boolean.parseBoolean(getEnv.apply("AUTH_DISABLED"));
        }
        public String getKeyStorePath() {
            String path = getEnv.apply("AUTH_KEYSTORE_PATH");
            return Objects.requireNonNull(path, "System environmental variable 'AUTH_KEYSTORE_PATH' is not set.");
        }

        public String getKeyStoreType() {
            String type = getEnv.apply("AUTH_KEYSTORE_TYPE");
            return Objects.requireNonNull(type, "System environmental variable 'AUTH_KEYSTORE_TYPE' is not set.");
        }

        public String getKeyStorePassword() {
            String password = getEnv.apply("AUTH_KEYSTORE_PASSWORD");
            return Objects.requireNonNull(password, "System environmental variable 'AUTH_KEYSTORE_PASSWORD' is not set.");
        }
    }

    public static class Modules {
        private Modules(Function<String, String> getEnv) {
            this.getEnv = getEnv;
        }

        private final Function<String, String> getEnv;

        public String getAlertServiceHost() {
            String url = getEnv.apply("MODULE_ALERT_SERVICE_HOST");
            return Objects.requireNonNull(url, "System environmental variable 'MODULE_ALERT_SERVICE_HOST' is not set.");
        }

        public int getAlertServicePort() {
            String port = getEnv.apply("MODULE_ALERT_SERVICE_PORT");
            return port != null ? Integer.parseInt(port) : 80;
        }

        public String getAlertServicePathMessage() {
            String url = getEnv.apply("MODULE_ALERT_SERVICE_PATH_MESSAGE");
            return Objects.requireNonNull(url, "System environmental variable 'MODULE_ALERT_SERVICE_PATH_MESSAGE' is not set.");
        }

        public String getAlertServicePathInfo() {
            String url = getEnv.apply("MODULE_ALERT_SERVICE_PATH_INFO");
            return Objects.requireNonNull(url, "System environmental variable 'MODULE_ALERT_SERVICE_PATH_INFO' is not set.");
        }

        public boolean getAlertServiceDisabled() {
            String disabled = getEnv.apply("MODULE_ALERT_SERVICE_DISABLED");
            return Boolean.parseBoolean(disabled);
        }
    }

    public HttpServer httpServerConfig() {
        return httpServerConfig;
    }

    public Database dbConfig() {
        return dbConfig;
    }

    public Auth authConfig() {
        return authConfig;
    }

    public Modules modulesConfig() {
        return modulesConfig;
    }

    public JsonObject server() {
        return JsonObject.of(
                "http.port", httpServerConfig.getPort()
        );
    }

    public JsonObject auth() {
        return JsonObject.of(
                "keystore.path", authConfig.getKeyStorePath(),
                "keystore.type", authConfig.getKeyStoreType(),
                "keystore.password", authConfig.getKeyStorePassword(),
                "disabled", authConfig.getDisabled()
        );
    }

    public JsonObject db() {
        return JsonObject.of(
            "port", dbConfig.getPort(),
            "host", dbConfig.getHost(),
            "database", dbConfig.getDatabase(),
            "user", dbConfig.getUser(),
            "password", dbConfig.getPassword(),
            "pool.size", dbConfig.getPoolSize()
        );
    }

    public JsonObject modules() {
        return JsonObject.of(
                "alert.service.disabled", modulesConfig.getAlertServiceDisabled(),
                "alert.service.host", modulesConfig.getAlertServiceHost(),
                "alert.service.port", modulesConfig.getAlertServicePort(),
                "alert.service.path.message", modulesConfig.getAlertServicePathMessage(),
                "alert.service.path.info", modulesConfig.getAlertServicePathInfo()
        );
    }
}
