package de.kif.backend.route

import de.kif.backend.authenticate
import de.kif.backend.authenticateOrRedirect
import de.kif.backend.prefix
import de.kif.backend.repository.*
import de.kif.backend.route.api.error
import de.kif.backend.util.Backup
import de.kif.backend.util.WikiImporter
import de.kif.backend.view.respondMain
import de.kif.common.RepositoryType
import de.kif.common.model.Permission
import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.PartData
import io.ktor.http.content.forEachPart
import io.ktor.http.content.streamProvider
import io.ktor.request.receiveMultipart
import io.ktor.request.receiveParameters
import io.ktor.response.respondRedirect
import io.ktor.response.respondText
import io.ktor.routing.Route
import io.ktor.routing.get
import io.ktor.routing.post
import io.ktor.util.toMap
import kotlinx.html.*
import mu.KotlinLogging

private val logger = KotlinLogging.logger {}

fun Route.account() {
    get("/account") {
        authenticateOrRedirect { user ->
            respondMain {
                content {
                    h1 { +"Konto" }
                    div {
                        +"Hallo ${user.username}!"
                        br {}
                        +"Du hast folgende Rechte: ${user.permissions.joinToString(", ") { it.germanInfo }}"
                        br {}
                        a("$prefix/logout") {
                            button(classes = "form-btn") {
                                +"Ausloggen"
                            }
                        }
                    }

                    a(href = "$prefix/account/backup", classes = "form-btn") {
                        +"Sicherung"
                    }

                    if (user.checkPermission(Permission.ADMIN)) {
                        a(href = "$prefix/account/import", classes = "form-btn") {
                            +"Aus Wiki importieren"
                        }
                    }
                }
            }
        }
    }

    get("/account/backup") {
        authenticateOrRedirect { user ->
            respondMain {
                content {
                    h1 { +"Sicherung" }

                    div("account-backup") {
                        if (user.checkPermission(Permission.ROOM)) {
                            a("$prefix/account/backup/rooms.json", classes = "form-btn") {
                                attributes["download"] = "rooms-backup.json"
                                +"Räume sichern"
                            }
                        }
                        if (user.checkPermission(Permission.USER)) {
                            a("$prefix/account/backup/users.json", classes = "form-btn") {
                                attributes["download"] = "users-backup.json"
                                +"Nutzer sichern"
                            }
                        }
                        if (user.checkPermission(Permission.POST)) {
                            a("$prefix/account/backup/posts.json", classes = "form-btn") {
                                attributes["download"] = "posts-backup.json"
                                +"Beiträge sichern"
                            }
                        }
                        if (user.checkPermission(Permission.WORK_GROUP)) {
                            a("$prefix/account/backup/work-groups.json", classes = "form-btn") {
                                attributes["download"] = "work-groups-backup.json"
                                +"Arbeitskreise sichern"
                            }
                        }
                        if (
                            user.checkPermission(Permission.WORK_GROUP) &&
                            user.checkPermission(Permission.ROOM) &&
                            user.checkPermission(Permission.SCHEDULE)
                        ) {
                            a("$prefix/account/backup/schedules.json", classes = "form-btn") {
                                attributes["download"] = "schedules-backup.json"
                                +"Zeitplan sichern"
                            }
                        }

                        if (user.checkPermission(Permission.ADMIN)) {
                            a("$prefix/account/backup.json", classes = "form-btn") {
                                attributes["download"] = "backup.json"
                                +"Alles sichern"
                            }
                        }
                    }

                    if (user.checkPermission(Permission.ADMIN)) {
                        h1 { +"Wiederherstellen" }

                        div("account-import") {
                            form(
                                action = "$prefix/account/restore",
                                method = FormMethod.post,
                                encType = FormEncType.multipartFormData
                            ) {
                                div("form-group") {
                                    label {
                                        htmlFor = "backup"
                                        +"Aus Datei (JSON)"
                                    }
                                    input(
                                        name = "backup",
                                        classes = "form-btn",
                                        type = InputType.file
                                    ) {
                                        id = "backup"
                                        value = "Select backup image"
                                        accept = ".json"
                                    }
                                }

                                div("form-switch-group") {
                                    div("form-group form-switch") {
                                        input(
                                            name = "reset",
                                            classes = "form-control",
                                            type = InputType.checkBox
                                        ) {
                                            id = "reset"
                                            checked = false
                                        }
                                        label {
                                            htmlFor = "reset"
                                            +"Zurücksetzen" // TODO: unverständlich
                                        }
                                    }
                                }

                                div("form-group") {
                                    button(type = ButtonType.submit, classes = "form-btn btn-primary") {
                                        +"Wiederherstellen"
                                    }
                                }
                            }
                        }
                    }

                    div("account-delete") {
                        h1 { +"Löschen -- Gefährlicher Bereich --" }

                        if (user.checkPermission(Permission.ROOM)) {
                            a("$prefix/account/backup/delete/rooms", classes = "form-btn btn-danger") {
                                +"Räume löschen"
                            }
                        }
                        if (user.checkPermission(Permission.USER)) {
                            a("$prefix/account/backup/delete/users", classes = "form-btn btn-danger") {
                                +"Nutzer löschen"
                            }
                        }
                        if (user.checkPermission(Permission.POST)) {
                            a("$prefix/account/backup/delete/posts", classes = "form-btn btn-danger") {
                                +"Beiträge löschen"
                            }
                        }
                        if (user.checkPermission(Permission.WORK_GROUP)) {
                            a("$prefix/account/backup/delete/work-groups", classes = "form-btn btn-danger") {
                                +"Arbeitskreise löschen"
                            }
                        }
                        if (
                            user.checkPermission(Permission.WORK_GROUP) &&
                            user.checkPermission(Permission.ROOM) &&
                            user.checkPermission(Permission.SCHEDULE)
                        ) {
                            a("$prefix/account/backup/delete/schedules", classes = "form-btn btn-danger") {
                                +"Zeitplan löschen"
                            }
                        }

                        if (user.checkPermission(Permission.ADMIN)) {
                            a("$prefix/account/backup/delete/all", classes = "form-btn btn-danger") {
                                +"Alles löschen"
                            }
                        }
                    }
                }
            }
        }
    }

    get("/account/import") {
        authenticateOrRedirect(Permission.ADMIN) { user ->
            val tracks = TrackRepository.all()
            val wikiSections = WikiImporter.loadSections()

            respondMain {
                content {
                    h1 { +"Aus Wiki importieren" }

                    if (user.checkPermission(Permission.ADMIN)) {
                        div("account-import-wiki") {
                            span { +"Arbeitskreise aus dem KIF-Wiki importieren" }

                            form(action = "$prefix/account/import", method = FormMethod.post) {
                                for ((index, section) in wikiSections.withIndex()) {
                                    div("form-group") {
                                        label {
                                            htmlFor = "section-$index-track"
                                            +section
                                        }
                                        select(
                                            classes = "form-control"
                                        ) {
                                            name = "section-$index-track"

                                            option {
                                                selected = false
                                                value = "-1"
                                                +"Nicht importieren"
                                            }
                                            option {
                                                selected = true
                                                value = "null"
                                                +"Importieren"
                                            }
                                            for (track in tracks) {
                                                option {
                                                    selected = false
                                                    value = track.id.toString()
                                                    +track.name
                                                }
                                            }
                                        }
                                        input(type = InputType.hidden, name = "section-$index-name") {
                                            value = section
                                        }
                                    }
                                }

                                div("form-switch-group") {
                                    div("form-group form-switch") {
                                        input(
                                            name = "skip-existing",
                                            classes = "form-control",
                                            type = InputType.checkBox
                                        ) {
                                            id = "skip-existing"
                                            checked = false
                                        }
                                        label {
                                            htmlFor = "skip-existing"
                                            +"Existierende Arbeitskreise überspringen"
                                        }
                                    }
                                    div("form-group form-switch") {
                                        input(
                                            name = "delete-existing",
                                            classes = "form-control",
                                            type = InputType.checkBox
                                        ) {
                                            id = "delete-existing"
                                            checked = false
                                        }
                                        label {
                                            htmlFor = "delete-existing"
                                            +"Existierende Arbeitskreise löschen"
                                        }
                                    }
                                }

                                button(type = ButtonType.submit, classes = "form-btn btn-primary") {
                                    +"Importieren"
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    get("/account/backup/rooms.json") {
        authenticate(Permission.ROOM) {
            call.respondText(Backup.backup(RepositoryType.ROOM), ContentType.Application.Json, HttpStatusCode.OK)
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup/users.json") {
        authenticate(Permission.USER) {
            call.respondText(Backup.backup(RepositoryType.USER), ContentType.Application.Json, HttpStatusCode.OK)
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup/posts.json") {
        authenticate(Permission.POST) {
            call.respondText(Backup.backup(RepositoryType.POST), ContentType.Application.Json, HttpStatusCode.OK)
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup/work-groups.json") {
        authenticate(Permission.WORK_GROUP) {
            call.respondText(Backup.backup(RepositoryType.WORK_GROUP), ContentType.Application.Json, HttpStatusCode.OK)
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup/schedules.json") {
        authenticate(Permission.ROOM, Permission.WORK_GROUP, Permission.SCHEDULE) {
            call.respondText(Backup.backup(RepositoryType.SCHEDULE), ContentType.Application.Json, HttpStatusCode.OK)
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup.json") {
        authenticate(Permission.ADMIN) {
            call.respondText(Backup.backup(*RepositoryType.values()), ContentType.Application.Json, HttpStatusCode.OK)
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    post("/account/restore") {
        authenticate(Permission.ADMIN) {
            var reset = false
            var import = ""

            call.receiveMultipart().forEachPart { part ->
                val name = part.name ?: return@forEachPart
                when (part) {
                    is PartData.FormItem -> {
                        if (name == "reset" && part.value == "on") {
                            reset = true
                        }
                    }
                    is PartData.FileItem -> {
                        import = part.streamProvider().bufferedReader().readText()
                    }
                }
            }

            if (reset) {
                Backup.restore(import)
            } else {
                Backup.import(import)
            }

            call.respondRedirect("$prefix/account")
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    post("/account/import") {
        authenticate(Permission.ADMIN) {
            val params = call.receiveParameters().toMap().mapValues { (_, list) ->
                list.firstOrNull()
            }

            val skipExisting = params["skip-existing"] == "on"
            val deleteExisting = params["delete-existing"] == "on"

            val map = mutableMapOf<Int, Pair<String, Long?>>()

            for ((key, value) in params) {
                if (key.startsWith("section") && value != null) {
                    val numberedType = key.substringAfter("-")
                    val number = numberedType.substringBefore("-").toIntOrNull() ?: continue
                    val type = numberedType.substringAfter("-")
                    var pair = map.getOrElse(number) { "" to null }
                    if (type == "name") {
                        pair = value to pair.second
                    } else if (type == "track") {
                        pair = pair.first to value.toLongOrNull()
                    }
                    map[number] = pair
                }
            }

            val sections = map.values.toMap()

            var existingWorkGroups = WorkGroupRepository.all()

            if (deleteExisting) {
                for (wg in existingWorkGroups) {
                    if (wg.id != null) WorkGroupRepository.delete(wg.id)
                }
                existingWorkGroups = emptyList()
            }

            val existingWorkGroupNames = existingWorkGroups.map { it.name }.toSet()

            val importedWorkGroups = WikiImporter.import(sections = sections)

            var counter = 0
            for (workGroup in importedWorkGroups) {
                if (skipExisting && workGroup.name in existingWorkGroupNames) continue

                WorkGroupRepository.create(workGroup)
                counter++
            }

            logger.info { "Import $counter from ${importedWorkGroups.size} work groups!" }

            call.respondRedirect("$prefix/account")
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }


    get("/account/backup/delete/rooms") {
        authenticate(Permission.ROOM) {
            ScheduleRepository.all().forEach {
                if (it.id != null) ScheduleRepository.delete(it.id)
            }
            RoomRepository.all().forEach {
                if (it.id != null) RoomRepository.delete(it.id)
            }
            call.respondRedirect("$prefix/")
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup/delete/users") {
        authenticate(Permission.USER) {
            UserRepository.all().forEach {
                if (it.id != null) UserRepository.delete(it.id)
            }
            call.respondRedirect("$prefix/")
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup/delete/posts") {
        authenticate(Permission.POST) {
            PostRepository.all().forEach {
                if (it.id != null) PostRepository.delete(it.id)
            }
            call.respondRedirect("$prefix/")
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup/delete/work-groups") {
        authenticate(Permission.WORK_GROUP) {
            ScheduleRepository.all().forEach {
                if (it.id != null) ScheduleRepository.delete(it.id)
            }
            TrackRepository.all().forEach {
                if (it.id != null) TrackRepository.delete(it.id)
            }
            WorkGroupRepository.all().forEach {
                if (it.id != null) WorkGroupRepository.delete(it.id)
            }
            call.respondRedirect("$prefix/")
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup/delete/schedules") {
        authenticate(Permission.ROOM, Permission.WORK_GROUP, Permission.SCHEDULE) {
            ScheduleRepository.all().forEach {
                if (it.id != null) ScheduleRepository.delete(it.id)
            }
            call.respondRedirect("$prefix/")
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }

    get("/account/backup/delete/all") {
        authenticate(Permission.ADMIN) {
            ScheduleRepository.all().forEach {
                if (it.id != null) ScheduleRepository.delete(it.id)
            }
            TrackRepository.all().forEach {
                if (it.id != null) TrackRepository.delete(it.id)
            }
            WorkGroupRepository.all().forEach {
                if (it.id != null) WorkGroupRepository.delete(it.id)
            }
            RoomRepository.all().forEach {
                if (it.id != null) RoomRepository.delete(it.id)
            }
            UserRepository.all().forEach {
                if (it.id != null) UserRepository.delete(it.id)
            }
            PostRepository.all().forEach {
                if (it.id != null) PostRepository.delete(it.id)
            }
            call.respondRedirect("$prefix/")
        } onFailure {
            call.error(HttpStatusCode.Unauthorized)
        }
    }
}
