package cz.senslog.analytics.app;

import cz.senslog.analytics.module.api.Module;
import cz.senslog.analytics.server.HttpVertxServer;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions;
import io.vertx.sqlclient.RowSet;
import io.vertx.sqlclient.SqlConnectOptions;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.Duration;
import java.time.ZoneId;
import java.util.Properties;

public final class Application {

    private static final Logger logger = LogManager.getLogger(Application.class);

    private static final String DEFAULT_UNKNOWN = "unknown";
    private static final long START_UP;
    public static String COMPILED_VERSION;
    public static String BUILD_VERSION;
    public static String PROJECT_NAME;

    static {
        START_UP = System.currentTimeMillis();
        try {
            InputStream input = new FileInputStream("gradle.properties");
            Properties prop = new Properties();
            prop.load(input);
            PROJECT_NAME = prop.getProperty("projectName", DEFAULT_UNKNOWN);
            COMPILED_VERSION = prop.getProperty("projectVersion", DEFAULT_UNKNOWN);
            BUILD_VERSION = prop.getProperty("buildVersion", DEFAULT_UNKNOWN);
        } catch (IOException ex) {
            logger.catching(ex);
            terminate(ex.getMessage());
        }
    }

    public static long uptime() {
        return System.currentTimeMillis() - START_UP;
    }

    public static String uptimeFormatted() {
        Duration duration = Duration.ofMillis(uptime());
        long HH = duration.toHours();
        long MM = duration.toMinutesPart();
        long SS = duration.toSecondsPart();
        return String.format("%02d:%02d:%02d", HH, MM, SS);
    }

    public static void terminate(String message) {
        logger.error(message);
        System.exit(1);
    }

    public static void start() {
        logger.info("Starting app '{}', version '{}', build '{}', timezone '{}'.", PROJECT_NAME, COMPILED_VERSION, BUILD_VERSION, ZoneId.systemDefault());

        PropertyConfig config = PropertyConfig.getInstance();
        DeploymentOptions options = new DeploymentOptions().setConfig(JsonObject.of(
                "server", config.server(),
                "auth", config.auth(),
                "modules", config.modules()
        ));

        PropertyConfig.Database dbConfig = config.dbConfig();
        Pool dbPool = Pool.pool(new SqlConnectOptions()
                .setPort(dbConfig.getPort())
                .setHost(dbConfig.getHost())
                .setDatabase(dbConfig.getDatabase())
                .setUser(dbConfig.getUser())
                .setPassword(dbConfig.getPassword()), new PoolOptions()
                .setMaxSize(dbConfig.getPoolSize()));

        dbPool.query("SELECT version()").execute().map(RowSet::iterator)
                .map(it -> it.hasNext() ? it.next().getString(0) : null)
                .onSuccess(version -> logger.info("Successful database connection to {}.", version))
                .onFailure(fail -> terminate(fail.getMessage()));


        AbstractVerticle[] modules = Module.createModules(dbPool);
        Vertx.vertx().deployVerticle(VertxDeployer.deploy(new HttpVertxServer(), modules), options, res -> {
            if(res.succeeded()) {
                logger.info("Deployment id is: {}", res.result());
                logger.info("Started in {} second.", uptime() / 1000.0);
            } else {
                logger.error("Deployment failed! The reason is '{}'", res.cause().getMessage());
            }
        });
    }
}