package de.kif.backend.route

import de.kif.backend.authenticateOrRedirect
import de.kif.backend.checkPassword
import de.kif.backend.hashPassword
import de.kif.backend.prefix
import de.kif.backend.repository.UserRepository
import de.kif.backend.view.TableTemplate
import de.kif.backend.view.respondMain
import de.kif.common.Search
import de.kif.common.model.Permission
import de.kif.common.model.User
import io.ktor.application.call
import io.ktor.html.insert
import io.ktor.request.receiveParameters
import io.ktor.response.respondRedirect
import io.ktor.routing.Route
import io.ktor.routing.get
import io.ktor.routing.post
import io.ktor.util.toMap
import kotlinx.css.CSSBuilder
import kotlinx.css.Display
import kotlinx.html.*
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set

fun Route.user() {
    get("/users") {
        authenticateOrRedirect(Permission.USER) {
            val search = call.parameters["search"] ?: ""
            val list = UserRepository.all()
            respondMain {
                content {
                    insert(TableTemplate()) {
                        searchValue = search

                        action {
                            a("$prefix/user/new") {
                                button(classes = "form-btn btn-primary") {
                                    +"Nutzer hinzufügen"
                                }
                            }
                        }

                        header {
                            th {
                                +"Name"
                            }
                            th {
                                +"Rechte"
                            }
                            th(classes = "action") {
                                +"Aktion"
                            }
                        }

                        for (u in list) {
                            val s = u.createSearch()
                            entry {
                                attributes["style"] = CSSBuilder().apply {
                                    display = if (Search.match(search, s)) Display.tableRow else Display.none
                                }.toString()
                                attributes["data-search"] = s.stringify()
                                td {
                                    +u.username
                                }
                                td {
                                    +u.permissions.joinToString(", ") { it.germanInfo.toLowerCase().capitalize() }
                                }
                                td(classes = "action") {
                                    a("$prefix/user/${u.id}") {
                                        i("material-icons") { +"edit" }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    get("/user/{id}") {
        authenticateOrRedirect(Permission.USER) { user ->
            val userId = call.parameters["id"]?.toLongOrNull() ?: return@get
            val editUser = UserRepository.get(userId) ?: return@get
            respondMain {
                content {
                    h1 { +"Nutzer bearbeiten" }
                    form(method = FormMethod.post) {
                        div("form-group") {
                            label {
                                htmlFor = "username"
                                +"Name"
                            }
                            input(
                                name = "username",
                                classes = "form-control"
                            ) {
                                id = "username"
                                placeholder = "Name"
                                value = editUser.username
                            }
                        }

                        div("form-switch-group") {
                            for (permission in Permission.values()) {
                                val name = permission.name.toLowerCase()
                                div("form-group form-switch") {
                                    input(
                                        name = "permission-$name",
                                        classes = "form-control",
                                        type = InputType.checkBox
                                    ) {
                                        id = "permission-$name"
                                        checked = permission in editUser.permissions

                                        if (!user.checkPermission(permission)) {
                                            readonly = true
                                        }
                                    }
                                    label {
                                        htmlFor = "permission-$name"
                                        +permission.germanInfo.toLowerCase().capitalize()
                                    }
                                }
                            }
                        }

                        div("form-group") {
                            label {
                                htmlFor = "current-password"
                                +"Passwort ändern"
                            }
                            input(
                                name = "current-password",
                                classes = "form-control",
                                type = InputType.password
                            ) {
                                id = "current-password"
                                placeholder = "Aktuelles Passwort"
                                value = ""
                            }
                            input(
                                name = "new-password-1",
                                classes = "form-control",
                                type = InputType.password
                            ) {
                                id = "new-password-1"
                                placeholder = "Neues Passwort"
                                value = ""
                            }
                            input(
                                name = "new-password-2",
                                classes = "form-control",
                                type = InputType.password
                            ) {
                                id = "new-password-2"
                                placeholder = "Neues Passwort wiederholen"
                                value = ""
                            }
                        }

                        div("form-group") {
                            a("$prefix/users") {
                                button(classes = "form-btn") {
                                    +"Abbrechen"
                                }
                            }
                            button(type = ButtonType.submit, classes = "form-btn btn-primary") {
                                +"Speichern"
                            }
                        }
                    }
                    a("$prefix/user/${editUser.id}/delete") {
                        button(classes = "form-btn btn-danger") {
                            +"Löschen"
                        }
                    }
                }
            }
        }
    }

    post("/user/{id}") {
        authenticateOrRedirect(Permission.USER) { user ->
            val userId = call.parameters["id"]?.toLongOrNull() ?: return@post
            val params = call.receiveParameters().toMap().mapValues { (_, list) ->
                list.firstOrNull()
            }

            var editUser = UserRepository.get(userId) ?: return@post

            params["username"]?.let { editUser = editUser.copy(username = it) }

            val currentPassword = params["current-password"]
            val newPassword1 = params["new-password-1"]
            val newPassword2 = params["new-password-2"]

            if (currentPassword != null && newPassword1 != null && newPassword2 != null && currentPassword.isNotBlank() && newPassword1.isNotBlank() && newPassword2.isNotBlank()) {
                if (checkPassword(currentPassword, editUser.password) && newPassword1 == newPassword2) {
                    editUser = editUser.copy(password = hashPassword(newPassword1))
                }
            }

            val permissions = parsePermissions(user, params)
            editUser = editUser.copy(permissions = permissions)

            UserRepository.update(editUser)

            call.respondRedirect("$prefix/users")
        }
    }

    get("/user/new") {
        authenticateOrRedirect(Permission.USER) { user ->
            respondMain {
                content {
                    h1 { +"Nutzer erstellen" }
                    form(method = FormMethod.post) {
                        div("form-group") {
                            label {
                                htmlFor = "username"
                                +"Name"
                            }
                            input(
                                name = "username",
                                classes = "form-control"
                            ) {
                                id = "username"
                                placeholder = "Name"
                                value = ""
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "password"
                                +"Passwort"
                            }
                            input(
                                name = "password",
                                classes = "form-control",
                                type = InputType.password
                            ) {
                                id = "password"
                                placeholder = "Passwort"
                                value = ""
                            }
                        }

                        div("form-switch-group") {
                            for (permission in Permission.values()) {
                                val name = permission.name.toLowerCase()
                                div("form-group form-switch") {
                                    input(
                                        name = "permission-$name",
                                        classes = "form-control",
                                        type = InputType.checkBox
                                    ) {
                                        id = "permission-$name"
                                        checked = false

                                        if (!user.checkPermission(permission)) {
                                            readonly = true
                                        }
                                    }
                                    label {
                                        htmlFor = "permission-$name"
                                        +permission.germanInfo.toLowerCase().capitalize()
                                    }
                                }
                            }
                        }

                        div("form-group") {
                            a("$prefix/users") {
                                button(classes = "form-btn") {
                                    +"Abbrechen"
                                }
                            }
                            button(type = ButtonType.submit, classes = "form-btn btn-primary") {
                                +"Erstellen"
                            }
                        }
                    }
                }
            }
        }
    }

    post("/user/new") {
        authenticateOrRedirect(Permission.USER) { user ->
            val params = call.receiveParameters().toMap().mapValues { (_, list) ->
                list.firstOrNull()
            }

            val username = params["username"] ?: return@post
            val password = params["password"] ?: return@post

            val permissions = parsePermissions(user, params)

            val newUser = User(null, username, hashPassword(password), permissions)

            UserRepository.create(newUser)

            call.respondRedirect("$prefix/users")
        }
    }

    get("/user/{id}/delete") {
        authenticateOrRedirect(Permission.USER) { user ->
            val userId = call.parameters["id"]?.toLongOrNull() ?: return@get
            val deleteUser = UserRepository.get(userId) ?: return@get

            if (user.checkPermission(Permission.USER) &&
                (Permission.ADMIN !in deleteUser.permissions || Permission.ADMIN in user.permissions)
            ) {
                UserRepository.delete(userId)
            }

            call.respondRedirect("$prefix/users")
        }
    }
}

fun parsePermissions(user: User, params: Map<String, String?>): Set<Permission> {
    return Permission.values().filter { permission ->
        val name = permission.name.toLowerCase()
        user.checkPermission(permission) && params["permission-$name"] == "on"
    }.toSet()
}