package de.kif.backend

import com.uchuhimo.konf.Config
import com.uchuhimo.konf.ConfigSpec
import com.uchuhimo.konf.Item
import java.io.FileNotFoundException
import java.nio.file.Files
import java.nio.file.Paths
import java.text.SimpleDateFormat
import java.time.ZoneId
import java.util.*
import kotlin.reflect.KProperty

object Configuration {

    private val config: Config

    class ConfigDelegate<T>(val item: Item<T>) {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return config[item]
        }
    }

    fun <T> c(item: Item<T>) = ConfigDelegate(item)

    private object ServerSpec : ConfigSpec("server") {
        val host by required<String>()
        val port by required<Int>()
    }

    object Server {
        val host by c(ServerSpec.host)
        val port by c(ServerSpec.port)
    }

    private object PathSpec : ConfigSpec("path") {
        val web by required<String>()
        val sessions by required<String>()
        val uploads by required<String>()
        val database by required<String>()
    }

    object Path {
        val web by c(PathSpec.web)
        val webPath: java.nio.file.Path by lazy { Paths.get(web).toAbsolutePath() }

        val sessions by c(PathSpec.sessions)
        val sessionsPath: java.nio.file.Path by lazy { Paths.get(sessions).toAbsolutePath() }

        val uploads by c(PathSpec.uploads)
        val uploadsPath: java.nio.file.Path by lazy { Paths.get(uploads).toAbsolutePath() }

        val database by c(PathSpec.database)
        val databasePath: java.nio.file.Path by lazy { Paths.get(database).toAbsolutePath() }
    }

    private object ScheduleSpec : ConfigSpec("schedule") {
        val reference by required<String>()
    }

    object Schedule {
        val reference by c(ScheduleSpec.reference)
        val referenceDate: Date by lazy {
            val sdf = SimpleDateFormat("yyyy-MM-dd")
            sdf.timeZone = TimeZone.getTimeZone(ZoneId.of("UTC"))
            sdf.parse(reference)
        }
    }

    private object SecuritySpec : ConfigSpec("security") {
        val sessionName by required<String>("session_name")
        val signKey by required<String>("sign_key")
    }

    object Security {
        val sessionName by c(SecuritySpec.sessionName)
        val signKey by c(SecuritySpec.signKey)
    }

    private object GeneralSpec : ConfigSpec("general") {
        val allowedUploadExtensions by required<String>("allowed_upload_extensions")
        val wikiUrl by required<String>("wiki_url")
    }

    object General {
        val allowedUploadExtensions by c(GeneralSpec.allowedUploadExtensions)
        val allowedUploadExtensionSet by lazy {
            allowedUploadExtensions.split(",").map { it.trim().toLowerCase() }.toSet()
        }
        val wikiUrl by c(GeneralSpec.wikiUrl)
    }

    private object TwitterSpec : ConfigSpec("twitter") {
        val username by required<String>("username")
        val password by required<String>("password")
    }

    object Twitter {
        val username by c(TwitterSpec.username)
        val password by c(TwitterSpec.password)
    }

    init {
        var config = Config {
            addSpec(ServerSpec)
            addSpec(PathSpec)
            addSpec(ScheduleSpec)
            addSpec(SecuritySpec)
            addSpec(GeneralSpec)
            addSpec(TwitterSpec)
        }.from.toml.resource("portal.toml")

        for (file in Files.list(Paths.get("."))) {
            if (file.fileName.toString().endsWith(".toml")) {
                try {
                    config = config.from.toml.file(file.toFile())
                } catch (_: FileNotFoundException) {
                }
            }
        }

        this.config = config.from.env()
            .from.systemProperties()
    }
}