package de.kif.backend.route

import de.kif.backend.Configuration
import de.kif.backend.Resources
import de.kif.backend.authenticateOrRedirect
import de.kif.backend.isAuthenticated
import de.kif.backend.repository.PostRepository
import de.kif.backend.util.markdownToHtml
import de.kif.backend.view.MainTemplate
import de.kif.backend.view.MenuTemplate
import de.kif.backend.view.respondMain
import de.kif.common.formatDateTime
import de.kif.common.model.Permission
import de.kif.common.model.Post
import io.ktor.application.call
import io.ktor.html.respondHtmlTemplate
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.response.respond
import io.ktor.response.respondRedirect
import io.ktor.routing.Route
import io.ktor.routing.get
import io.ktor.routing.post
import kotlinx.html.*
import java.io.File

fun DIV.createPost(post: Post, editable: Boolean = false, additionalClasses: String = "") {
    var classes = "post"
    if (additionalClasses.isNotBlank()) {
        classes += " $additionalClasses"
    }
    if (post.image == null) {
        classes += " post-no-image"
    }
    div(classes) {
        attributes["data-id"] = post.id.toString()
        attributes["data-pinned"] = post.pinned.toString()

        a("/p/${post.url}", classes = "post-name") {
            +post.name
        }
        if (editable) {
            a("/post/${post.id}", classes = "post-edit") {
                i("material-icons") { +"edit" }
            }
        }

        div("post-column") {
            div("post-column-left") {
                figure(classes = "post-image") {
                    if (post.image != null) {
                        attributes["style"] = "background-image: url(\"/images/${post.image}\")"
                    }
                }
            }
            div("post-column-right") {
                div("post-content") {
                    unsafe {
                        raw(markdownToHtml(post.content))
                    }
                }
                div("post-footer") {
                    +formatDateTime(post.createdAt)
                }
            }
        }
    }
}

fun Route.overview() {
    get("") {
        val user = isAuthenticated(Permission.POST)
        val editable = user != null

        val postList = PostRepository.all().asReversed()

        respondMain {
            menuTemplate {
                this.user = user
                active = MenuTemplate.Tab.BOARD
            }
            content {
                div("overview") {
                    div("overview-main") {
                        for (post in postList) {
                            createPost(post, editable, "overview-post")
                        }
                    }
                    div("overview-side") {
                        if (editable) {
                            div("overview-new") {
                                a("post/new", classes = "form-btn") {
                                    +"New"
                                }
                            }
                        }
                        div("overview-twitter") {
                            +"The Twitter Wall"
                        }
                    }
                }
            }
        }
    }

    get("/p/{url}") {
        val user = isAuthenticated(Permission.POST)
        val editable = user != null

        val post = PostRepository.getByUrl(call.parameters["url"] ?: "")

        if (post == null) {
            call.respond(HttpStatusCode.NotFound, "Not found")
            return@get
        }

        respondMain {
            menuTemplate {
                this.user = user
                active = MenuTemplate.Tab.BOARD
            }
            content {
                div("overview") {
                    createPost(post, editable)
                }
            }
        }
    }

    get("/post/{id}") {
        authenticateOrRedirect(Permission.POST) { user ->
            val postId = call.parameters["id"]?.toLongOrNull() ?: return@get
            val editPost = PostRepository.get(postId) ?: return@get
            respondMain {
                menuTemplate {
                    this.user = user
                    active = MenuTemplate.Tab.BOARD
                }
                content {
                    h1 { +"Edit post" }
                    div("post-edit-container") {
                        div("post-edit-left") {
                            form(method = FormMethod.post, encType = FormEncType.multipartFormData) {
                                div("form-group") {
                                    label {
                                        htmlFor = "name"
                                        +"Name"
                                    }
                                    input(
                                        name = "name",
                                        classes = "form-control"
                                    ) {
                                        id = "name"
                                        placeholder = "Name"
                                        value = editPost.name
                                    }
                                }

                                div("form-group") {
                                    label {
                                        htmlFor = "url"
                                        +"Url"
                                    }
                                    input(
                                        name = "url",
                                        classes = "form-control"
                                    ) {
                                        id = "url"
                                        placeholder = "Url"
                                        value = editPost.url
                                    }
                                }

                                label {
                                    htmlFor = "image"
                                    +"Image"
                                }
                                div("post-edit-image-box") {
                                    div {
                                        figure(classes = "post-edit-image") {
                                            if (editPost.image != null) {
                                                attributes["style"] =
                                                    "background-image: url(\"/images/${editPost.image}\")"
                                            }
                                        }
                                    }
                                    div {
                                        div("form-group") {
                                            input(
                                                name = "image",
                                                classes = "form-btn",
                                                type = InputType.file
                                            ) {
                                                id = "image"
                                                value = "Upload image"
                                                accept =
                                                    Configuration.General
                                                        .allowedUploadExtensionSet
                                                        .joinToString(",") {
                                                            ".$it"
                                                        }
                                            }
                                        }

                                        div("form-group form-switch") {
                                            input(
                                                name = "image-delete",
                                                classes = "form-control",
                                                type = InputType.checkBox
                                            ) {
                                                id = "image-delete"
                                                checked = false
                                            }
                                            label {
                                                htmlFor = "image-delete"
                                                +"Delete image"
                                            }
                                        }
                                    }
                                }

                                div("form-switch-group") {
                                    div("form-group form-switch") {
                                        input(
                                            name = "pinned",
                                            classes = "form-control",
                                            type = InputType.checkBox
                                        ) {
                                            id = "pinned"
                                            checked = editPost.pinned
                                        }
                                        label {
                                            htmlFor = "pinned"
                                            +"Pinned"
                                        }
                                    }
                                    div("form-group form-switch") {
                                        input(
                                            name = "hide-on-projector",
                                            classes = "form-control",
                                            type = InputType.checkBox
                                        ) {
                                            id = "hide-on-projector"
                                            checked = editPost.hideOnProjector
                                        }
                                        label {
                                            htmlFor = "hide-on-projector"
                                            +"Hide on projector"
                                        }
                                    }
                                }

                                div("form-group") {
                                    label {
                                        htmlFor = "content"
                                        +"Content"
                                    }
                                    textArea(rows = "10", classes = "form-control") {
                                        name = "content"
                                        id = "content"

                                        +editPost.content
                                    }
                                }

                                div("form-group") {
                                    a("/") {
                                        button(classes = "form-btn") {
                                            +"Cancel"
                                        }
                                    }
                                    button(type = ButtonType.submit, classes = "form-btn btn-primary") {
                                        +"Save"
                                    }
                                }
                            }
                            a("/post/${editPost.id}/delete") {
                                button(classes = "form-btn btn-danger") {
                                    +"Delete"
                                }
                            }
                        }
                        div("post-edit-right post-content") {

                        }
                    }
                }
            }
        }
    }

    post("/post/{id}") {
        authenticateOrRedirect(Permission.POST) { user ->
            val postId = call.parameters["id"]?.toLongOrNull() ?: return@post

            var imageUploadName: String? = null

            val params = mutableMapOf<String, String>()
            call.receiveMultipart().forEachPart { part ->
                val name = part.name ?: return@forEachPart
                when (part) {
                    is PartData.FormItem -> {
                        params[name] = part.value
                    }
                    is PartData.FileItem -> {
                        val extension = File(part.originalFileName).extension

                        if (extension.toLowerCase() !in Configuration.General.allowedUploadExtensionSet) return@forEachPart

                        var uploadName = Post.generateUrl() + "." + extension

                        while (true) {
                            if (Resources.existsUpload(uploadName)) {
                                uploadName = Post.generateUrl() + "." + extension
                            } else {
                                break
                            }
                        }

                        Resources.createUpload(uploadName, part.streamProvider())
                        imageUploadName = uploadName
                    }
                }
            }

            var post = PostRepository.get(postId) ?: return@post

            params["name"]?.let { post = post.copy(name = it) }
            params["url"]?.let { post = post.copy(url = it) }
            params["content"]?.let { post = post.copy(content = it) }
            params["pinned"]?.let { post = post.copy(pinned = it == "on") }
            params["hide-on-projector"]?.let { post = post.copy(hideOnProjector = it == "on") }

            if (params["image-delete"] == "on") {
                val currentImage = post.image
                if (currentImage != null) {
                    post = post.copy(image = null)
                    Resources.deleteUpload(currentImage)
                }

                val upload = imageUploadName
                if (upload != null) {
                    Resources.deleteUpload(upload)
                }
            } else {
                imageUploadName?.let {
                    val currentImage = post.image
                    if (currentImage != null) {
                        Resources.deleteUpload(currentImage)
                    }

                    post = post.copy(image = it)
                }
            }

            PostRepository.update(post)

            call.respondRedirect("/")
        }
    }

    get("/post/new") {
        authenticateOrRedirect(Permission.POST) { user ->
            respondMain {
                menuTemplate {
                    this.user = user
                    active = MenuTemplate.Tab.BOARD
                }
                content {
                    h1 { +"Create post" }
                    div("post-edit-container") {
                        div("post-edit-left") {
                            form(method = FormMethod.post, encType = FormEncType.multipartFormData) {
                                div("form-group") {
                                    label {
                                        htmlFor = "name"
                                        +"Name"
                                    }
                                    input(
                                        name = "name",
                                        classes = "form-control"
                                    ) {
                                        id = "name"
                                        placeholder = "Name"
                                        value = ""
                                    }
                                }
                                div("form-group") {
                                    label {
                                        htmlFor = "url"
                                        +"Url"
                                    }
                                    input(
                                        name = "url",
                                        classes = "form-control"
                                    ) {
                                        id = "url"
                                        placeholder = "Url"
                                        value = Post.generateUrl()
                                    }
                                }

                                label {
                                    htmlFor = "image"
                                    +"Image"
                                }
                                div("post-edit-image-box") {
                                    div {
                                        figure(classes = "post-edit-image") {}
                                    }
                                    div {
                                        div("form-group") {
                                            input(
                                                name = "image",
                                                classes = "form-btn",
                                                type = InputType.file
                                            ) {
                                                id = "image"
                                                value = "Upload image"
                                                accept =
                                                    Configuration.General
                                                        .allowedUploadExtensionSet
                                                        .joinToString(",") {
                                                            ".$it"
                                                        }
                                            }
                                        }
                                    }
                                }

                                div("form-switch-group") {
                                    div("form-group form-switch") {
                                        input(
                                            name = "pinned",
                                            classes = "form-control",
                                            type = InputType.checkBox
                                        ) {
                                            id = "pinned"
                                            checked = false
                                        }
                                        label {
                                            htmlFor = "pinned"
                                            +"Pinned"
                                        }
                                    }
                                    div("form-group form-switch") {
                                        input(
                                            name = "hide-on-projector",
                                            classes = "form-control",
                                            type = InputType.checkBox
                                        ) {
                                            id = "hide-on-projector"
                                            checked = false
                                        }
                                        label {
                                            htmlFor = "hide-on-projector"
                                            +"Hide on projector"
                                        }
                                    }
                                }

                                div("form-group") {
                                    label {
                                        htmlFor = "content"
                                        +"Content"
                                    }
                                    textArea(rows = "10", classes = "form-control") {
                                        name = "content"
                                        id = "content"

                                        +""
                                    }
                                }

                                div("form-group") {
                                    a("/") {
                                        button(classes = "form-btn") {
                                            +"Cancel"
                                        }
                                    }
                                    button(type = ButtonType.submit, classes = "form-btn btn-primary") {
                                        +"Create"
                                    }
                                }
                            }
                        }
                        div("post-edit-right post-content") {

                        }
                    }
                }
            }
        }
    }

    post("/post/new") {
        authenticateOrRedirect(Permission.POST) { user ->
            var imageUploadName: String? = null

            val params = mutableMapOf<String, String>()
            call.receiveMultipart().forEachPart { part ->
                val name = part.name ?: return@forEachPart
                when (part) {
                    is PartData.FormItem -> {
                        params[name] = part.value
                    }
                    is PartData.FileItem -> {
                        val extension = File(part.originalFileName).extension

                        if (extension.toLowerCase() !in Configuration.General.allowedUploadExtensionSet) return@forEachPart

                        var uploadName = Post.generateUrl() + "." + extension

                        while (true) {
                            if (Resources.existsUpload(uploadName)) {
                                uploadName = Post.generateUrl() + "." + extension
                            } else {
                                break
                            }
                        }

                        Resources.createUpload(uploadName, part.streamProvider())
                        imageUploadName = uploadName
                    }
                }
            }

            val name = params["name"] ?: return@post
            val content = params["content"] ?: return@post
            val url = params["url"] ?: return@post
            val pinned = params["pinned"] == "on"
            val hideOnProjector = params["hide-on-projector"] == "on"

            val post = Post(null, name, content, url, imageUploadName, pinned, hideOnProjector)

            PostRepository.create(post)

            call.respondRedirect("/")
        }
    }

    get("/post/{id}/delete") {
        authenticateOrRedirect(Permission.POST) { user ->
            val postId = call.parameters["id"]?.toLongOrNull() ?: return@get

            PostRepository.delete(postId)

            call.respondRedirect("/")
        }
    }
}
