package xyz.etztech.stonks

import com.natpryce.konfig.*
import java.io.FileInputStream
import java.util.*
import kotlinx.coroutines.*
import org.h2.tools.Server
import org.jetbrains.exposed.exceptions.ExposedSQLException
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import xyz.etztech.stonks.api.initApiServer
import xyz.etztech.stonks.dsl.LiveStatistics
import xyz.etztech.stonks.dsl.Statistics
import xyz.etztech.stonks.statisticsimporter.StatisticsImporter

fun main() =
        runBlocking {
            println("Starting Stonks...")

            val fis = FileInputStream("./stonks.config")
            val config = Properties()

            config.load(fis)

            val databaseBaseDir = config.getProperty("databaseBaseDir")
            val databaseName = config.getProperty("databaseName")
            val h2StartWebServer = config.getProperty("h2StartWebServer").toBoolean()
            val h2tWebServerPort = config.getProperty("h2tWebServerPort").toInt()
            val h2TcpServerPort = config.getProperty("h2TcpServerPort").toInt()
            val apiServerPort = config.getProperty("apiServerPort").toInt()
            val statisticsUpdateInterval = config.getProperty("statisticsUpdateInterval").toLong()
            val minecraftStatsFolder = config.getProperty("minecraftStatsFolder")

            val database =
                    initH2Server(
                            databaseBaseDir,
                            databaseName,
                            h2TcpServerPort,
                            h2StartWebServer,
                            h2tWebServerPort)

            initApiServer(apiServerPort, database)

            initPeriodicFetching(statisticsUpdateInterval, minecraftStatsFolder, database)

            delay(60 * 1000L)

            println("END")
        }

fun initH2Server(
        databaseBaseDir: String,
        databaseName: String,
        h2TcpServerPort: Int,
        h2StartWebServer: Boolean,
        h2WebServerPort: Int
): Database {
    val webServer =
            Server.createWebServer(
                    "-trace", "-baseDir", databaseBaseDir, "-webPort", h2WebServerPort.toString())

    val tcpServer =
            Server.createTcpServer(
                    "-trace", "-baseDir", databaseBaseDir, "-webPort", h2TcpServerPort.toString())

    if (h2StartWebServer) {
        webServer.start()
        println("H2 web interface started: ${webServer.getURL()}")
    }

    tcpServer.start()
    println("H2 TCP endpoint started: ${tcpServer.getURL()}")

    val database = Database.connect("jdbc:h2:$databaseBaseDir/$databaseName", "org.h2.Driver")

    transaction {
        addLogger(StdOutSqlLogger)
        SchemaUtils.create(Statistics)
        SchemaUtils.create(LiveStatistics)

        // Create indexes with explicit SQL because I can't figure out how to do it with exposed
        // Wrap it in a try block because it throws an exception if index already exists
        try {
            TransactionManager.current()
                    .exec("CREATE INDEX idx_playerid ON Statistics (\"PlayerId\")")
        } catch (e: ExposedSQLException) {}

        try {
            TransactionManager.current()
                    .exec("CREATE INDEX idx_type_name ON Statistics (\"Type\", \"Name\")")
        } catch (e: ExposedSQLException) {}

        try {
            TransactionManager.current()
                    .exec("CREATE INDEX idx_playerid ON LiveStatistics (\"PlayerId\")")
        } catch (e: ExposedSQLException) {}

        try {
            TransactionManager.current()
                    .exec("CREATE INDEX idx_type_name ON LiveStatistics (\"Type\", \"Name\")")
        } catch (e: ExposedSQLException) {}
    }

    return database
}

suspend fun initPeriodicFetching(interval: Long, folder: String, db: Database) =
        coroutineScope {
            launch {
                while (true) {
                    StatisticsImporter.importStatistics(folder, db)
                    delay(interval)
                }
            }
        }
