package de.kif.backend.route

import de.kif.backend.authenticateOrRedirect
import de.kif.backend.prefix
import de.kif.backend.repository.TrackRepository
import de.kif.backend.repository.WorkGroupRepository
import de.kif.backend.view.TableTemplate
import de.kif.backend.view.respondMain
import de.kif.common.Search
import de.kif.common.model.*
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.set

private const val separator = "###"

fun Route.workGroup() {
    get("workgroups") {
        authenticateOrRedirect(Permission.WORK_GROUP) {
            val search = call.parameters["search"] ?: ""
            val list = WorkGroupRepository.all()
            respondMain {
                content {
                    insert(TableTemplate()) {
                        searchValue = search

                        action {
                            a("$prefix/tracks") {
                                button(classes = "form-btn") {
                                    +"Tracks bearbeiten"
                                }
                            }
                            a("$prefix/workgroup/new") {
                                button(classes = "form-btn btn-primary") {
                                    +"Arbeitskreis hinzufügen"
                                }
                            }
                        }

                        header {
                            th {
                                +"Name"
                            }
                            th {
                                +"Dauer"
                            }
                            th {
                                +"Interessierte"
                            }
                            th {
                                +"Track"
                            }
                            th {
                                +"Beamer"
                            }
                            th {
                                +"Resolution"
                            }
                            th {
                                +"Sprache"
                            }
                            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()
                                attributes["data-edit"] = "workgroup"
                                attributes["data-id"] = u.id.toString()

                                td {
                                    span {
                                        attributes["data-edit-type"] = "workgroup-name"

                                        +u.name
                                    }
                                }
                                td {
                                    span {
                                        attributes["data-edit-type"] = "workgroup-length"

                                        +u.length.toString()
                                    }
                                }
                                td {
                                    span {
                                        attributes["data-edit-type"] = "workgroup-interested"

                                        +u.interested.toString()
                                    }
                                }
                                td {
                                    span {
                                        attributes["data-edit-type"] = "workgroup-track"

                                        +(u.track?.name ?: "")
                                    }
                                }
                                td {
                                    span {
                                        attributes["data-edit-type"] = "workgroup-projector"

                                        +u.projector.toString()
                                    }
                                }
                                td {
                                    span {
                                        attributes["data-edit-type"] = "workgroup-resolution"

                                        +u.resolution.toString()
                                    }
                                }
                                td {
                                    span {
                                        attributes["data-edit-type"] = "workgroup-language"

                                        +u.language.localeName
                                    }
                                }
                                td(classes = "action") {
                                    a("$prefix/workgroup/${u.id}") {
                                        i("material-icons") { +"edit" }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    get("/workgroup/{id}") {
        authenticateOrRedirect(Permission.WORK_GROUP) {
            val workGroupId = call.parameters["id"]?.toLongOrNull() ?: return@get
            val editWorkGroup = WorkGroupRepository.get(workGroupId) ?: return@get
            val tracks = TrackRepository.all()

            val workGroups = editWorkGroup.constraints.mapNotNull {
                it.workGroup
            }.distinct().associateWith {
                WorkGroupRepository.get(it)!!
            }

            respondMain {
                content {
                    h1 { +"Arbeitskreis bearbeiten" }
                    form(method = FormMethod.post) {
                        div("form-group") {
                            label {
                                htmlFor = "name"
                                +"Name"
                            }
                            input(
                                name = "name",
                                classes = "form-control"
                            ) {
                                id = "name"
                                placeholder = "Name"
                                value = editWorkGroup.name
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "description"
                                +"Beschreibung"
                            }
                            textArea(rows = "10", classes = "form-control") {
                                name = "description"
                                id = "description"

                                +editWorkGroup.description
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "interested"
                                +"Interessierte"
                            }
                            input(
                                name = "interested",
                                classes = "form-control",
                                type = InputType.number
                            ) {
                                id = "interested"
                                value = editWorkGroup.interested.toString()

                                min = "0"
                                max = "1337"
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "track"
                                +"Track"
                            }
                            select(
                                classes = "form-control"
                            ) {
                                name = "track"

                                option {
                                    selected = (editWorkGroup.track?.id ?: -1) < 0
                                    value = "-1"
                                    +"None"
                                }
                                for (track in tracks) {
                                    option {
                                        selected = editWorkGroup.track?.id == track.id
                                        value = track.id.toString()
                                        +track.name
                                    }
                                }
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "length"
                                +"Dauer in Minuten"
                            }
                            input(
                                name = "length",
                                classes = "form-control",
                                type = InputType.number
                            ) {
                                id = "length"
                                value = editWorkGroup.length.toString()

                                min = "0"
                                max = "1440"
                                step = "5"
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "language"
                                +"Sprache"
                            }
                            select(
                                classes = "form-control"
                            ) {
                                name = "language"

                                for (language in Language.values()) {
                                    option {
                                        selected = editWorkGroup.language == language
                                        value = language.code
                                        +language.localeName
                                    }
                                }
                            }
                        }

                        div("form-group") {
                            attributes["data-hint"] = "Use '$separator' to separate multiple leaders"

                            label {
                                htmlFor = "leader"
                                +"Leiter"
                            }
                            input(
                                name = "leader",
                                classes = "form-control"
                            ) {
                                id = "leader"
                                placeholder = "Leiter"

                                value = editWorkGroup.leader.joinToString(" $separator ")
                            }
                        }

                        div("form-switch-group") {
                            div("form-group form-switch") {
                                input(
                                    name = "projector",
                                    classes = "form-control",
                                    type = InputType.checkBox
                                ) {
                                    id = "projector"
                                    checked = editWorkGroup.projector
                                }
                                label {
                                    htmlFor = "projector"
                                    +"Beamer"
                                }
                            }
                            div("form-group form-switch") {
                                input(
                                    name = "resolution",
                                    classes = "form-control",
                                    type = InputType.checkBox
                                ) {
                                    id = "resolution"
                                    checked = editWorkGroup.resolution
                                }
                                label {
                                    htmlFor = "resolution"
                                    +"Resolution"
                                }
                            }

                            div("form-group form-switch") {
                                input(
                                    name = "internet",
                                    classes = "form-control",
                                    type = InputType.checkBox
                                ) {
                                    id = "internet"
                                    checked = editWorkGroup.internet
                                }
                                label {
                                    htmlFor = "internet"
                                    +"Internet"
                                }
                            }

                            div("form-group form-switch") {
                                input(
                                    name = "whiteboard",
                                    classes = "form-control",
                                    type = InputType.checkBox
                                ) {
                                    id = "whiteboard"
                                    checked = editWorkGroup.whiteboard
                                }
                                label {
                                    htmlFor = "whiteboard"
                                    +"Whiteboard"
                                }
                            }

                            div("form-group form-switch") {
                                input(
                                    name = "blackboard",
                                    classes = "form-control",
                                    type = InputType.checkBox
                                ) {
                                    id = "blackboard"
                                    checked = editWorkGroup.blackboard
                                }
                                label {
                                    htmlFor = "blackboard"
                                    +"Tafel"
                                }
                            }

                            div("form-group form-switch") {
                                input(
                                    name = "accessible",
                                    classes = "form-control",
                                    type = InputType.checkBox
                                ) {
                                    id = "accessible"
                                    checked = editWorkGroup.accessible
                                }
                                label {
                                    htmlFor = "accessible"
                                    +"Barrierefreier Zugang"
                                }
                            }
                        }

                        div("form-group work-group-constraints") {
                            label {
                                +"Constraints"
                            }
                            span("form-btn work-group-constraints-add") {
                                i("material-icons") { +"add" }
                            }
                            div("work-group-constraints-add-list") {

                            }
                            for ((index, constraint) in editWorkGroup.constraints.withIndex()) {
                                div("input-group") {
                                    when (constraint.type) {
                                        ConstraintType.OnlyOnDay -> {
                                            span("form-btn") {
                                                +"An Tag"
                                            }
                                            input(
                                                name = "constraint-only-on-day-$index",
                                                classes = "form-control",
                                                type = InputType.number
                                            ) {
                                                value = constraint.day.toString()

                                                min = "-1337"
                                                max = "1337"

                                                placeholder = "Tag"
                                            }
                                        }
                                        ConstraintType.NotOnDay -> {
                                            span("form-btn") {
                                                +"Nicht an Tag"
                                            }
                                            input(
                                                name = "constraint-not-on-day-$index",
                                                classes = "form-control",
                                                type = InputType.number
                                            ) {
                                                value = constraint.day.toString()

                                                min = "-1337"
                                                max = "1337"

                                                placeholder = "Tag"
                                            }
                                        }
                                        ConstraintType.OnlyBeforeTime -> {
                                            span("form-btn") {
                                                +"Vor Zeit"
                                            }
                                            input(
                                                name = "constraint-only-before-time-day-$index",
                                                classes = "form-control"
                                            ) {
                                                value = constraint.day?.toString() ?: ""

                                                placeholder = "Tag (optional)"
                                            }
                                            input(
                                                name = "constraint-only-before-time-$index",
                                                classes = "form-control",
                                                type = InputType.number
                                            ) {
                                                value = constraint.time.toString()

                                                min = "-1337"
                                                max = "133700"

                                                placeholder = "Minuten"
                                            }
                                        }
                                        ConstraintType.OnlyAfterTime -> {
                                            span("form-btn") {
                                                +"Nach Zeit"
                                            }
                                            input(
                                                name = "constraint-only-after-time-day-$index",
                                                classes = "form-control"
                                            ) {
                                                value = constraint.day?.toString() ?: ""

                                                placeholder = "Tag (optional)"
                                            }
                                            input(
                                                name = "constraint-only-after-time-$index",
                                                classes = "form-control",
                                                type = InputType.number
                                            ) {
                                                value = constraint.time.toString()

                                                min = "-1337"
                                                max = "133700"

                                                placeholder = "Minuten"
                                            }
                                        }
                                        ConstraintType.NotAtSameTime -> {
                                            span("form-btn") {
                                                +"Nicht parallel"
                                            }
                                            select(
                                                classes = "form-control"
                                            ) {
                                                name = "constraint-not-at-same-time-$index"

                                                option {
                                                    selected = true
                                                    value = constraint.workGroup.toString()
                                                    +(workGroups[constraint.workGroup!!]?.name ?: "")
                                                }
                                            }
                                        }
                                        ConstraintType.OnlyAfterWorkGroup -> {
                                            span("form-btn") {
                                                +"Nach AK"
                                            }
                                            select(
                                                classes = "form-control"
                                            ) {
                                                name = "constraint-only-after-work-group-$index"

                                                option {
                                                    selected = true
                                                    value = constraint.workGroup.toString()
                                                    +(workGroups[constraint.workGroup!!]?.name ?: "")
                                                }
                                            }
                                        }
                                    }

                                }
                            }
                        }

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

    post("/workgroup/{id}") {
        authenticateOrRedirect(Permission.WORK_GROUP) {
            val workGroupId = call.parameters["id"]?.toLongOrNull() ?: return@post
            val params = call.receiveParameters().toMap().mapValues { (_, list) ->
                list.firstOrNull()
            }
            var editWorkGroup = WorkGroupRepository.get(workGroupId) ?: return@post

            params["name"]?.let { editWorkGroup = editWorkGroup.copy(name = it) }
            params["description"]?.let { editWorkGroup = editWorkGroup.copy(description = it) }
            params["interested"]?.toIntOrNull()?.let { editWorkGroup = editWorkGroup.copy(interested = it) }
            params["track"]?.toLongOrNull()?.let {
                val track = TrackRepository.get(it)
                editWorkGroup = editWorkGroup.copy(track = track)
            }
            params["projector"]?.let { editWorkGroup = editWorkGroup.copy(projector = it == "on") }
            params["resolution"]?.let { editWorkGroup = editWorkGroup.copy(resolution = it == "on") }
            params["internet"]?.let { editWorkGroup = editWorkGroup.copy(internet = it == "on") }
            params["whiteboard"]?.let { editWorkGroup = editWorkGroup.copy(whiteboard = it == "on") }
            params["blackboard"]?.let { editWorkGroup = editWorkGroup.copy(blackboard = it == "on") }
            params["accessible"]?.let { editWorkGroup = editWorkGroup.copy(accessible = it == "on") }

            params["length"]?.toIntOrNull()?.let { editWorkGroup = editWorkGroup.copy(length = it) }
            params["language"]?.let {
                editWorkGroup =
                    editWorkGroup.copy(language = Language.values().find { l -> l.code == it } ?: Language.GERMAN)
            }
            params["leader"]?.let {
                val leader = it.split("\\s*$separator+\\s*".toRegex())
                editWorkGroup = editWorkGroup.copy(leader = leader)
            }

            val constraints = parseConstraintParam(params)

            editWorkGroup = editWorkGroup.copy(constraints = constraints)

            WorkGroupRepository.update(editWorkGroup)

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

    get("/workgroup/new") {
        authenticateOrRedirect(Permission.WORK_GROUP) {
            val tracks = TrackRepository.all()
            respondMain {
                content {
                    h1 { +"Arbeitskreis erstellen" }
                    form(method = FormMethod.post) {
                        div("form-group") {
                            label {
                                htmlFor = "name"
                                +"Name"
                            }
                            input(
                                name = "name",
                                classes = "form-control"
                            ) {
                                id = "name"
                                placeholder = "Name"
                                value = ""
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "description"
                                +"Beschreibung"
                            }
                            textArea(rows = "10", classes = "form-control") {
                                name = "description"
                                id = "description"

                                +""
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "interested"
                                +"Interessierte"
                            }
                            input(
                                name = "interested",
                                classes = "form-control",
                                type = InputType.number
                            ) {
                                id = "interested"
                                value = "0"

                                min = "0"
                                max = "1337"
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "track"
                                +"Track"
                            }
                            select(
                                classes = "form-control"
                            ) {
                                name = "track"

                                option {
                                    selected = true
                                    value = "-1"
                                    +"None"
                                }
                                for (track in tracks) {
                                    option {
                                        value = track.id.toString()
                                        +track.name
                                    }
                                }
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "length"
                                +"Dauer"
                            }
                            input(
                                name = "length",
                                classes = "form-control",
                                type = InputType.number
                            ) {
                                id = "length"
                                value = "0"

                                min = "0"
                                max = "1440"
                                step = "5"
                            }
                        }
                        div("form-group") {
                            label {
                                htmlFor = "language"
                                +"Sprache"
                            }
                            select(
                                classes = "form-control"
                            ) {
                                name = "language"

                                for (language in Language.values()) {
                                    option {
                                        selected = language == Language.GERMAN
                                        value = language.code
                                        +language.localeName
                                    }
                                }
                            }
                        }

                        div("form-group") {
                            attributes["data-hint"] = "Use '$separator' to separate multiple leaders"

                            label {
                                htmlFor = "leader"
                                +"Leiter"
                            }
                            input(
                                name = "leader",
                                classes = "form-control"
                            ) {
                                id = "leader"
                                placeholder = "Leiter"

                                value = ""
                            }
                        }


                        div("form-switch-group") {
                            div("form-group form-switch") {
                                input(
                                    name = "projector",
                                    classes = "form-control",
                                    type = InputType.checkBox
                                ) {
                                    id = "projector"
                                    checked = false
                                }
                                label {
                                    htmlFor = "projector"
                                    +"Beamer"
                                }
                            }
                            div("form-group form-switch") {
                                input(
                                    name = "resolution",
                                    classes = "form-control",
                                    type = InputType.checkBox
                                ) {
                                    id = "resolution"
                                    checked = false
                                }
                                label {
                                    htmlFor = "resolution"
                                    +"Resolution"
                                }
                            }

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

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

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

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

                        div("form-group work-group-constraints") {
                            label {
                                +"Constraints"
                            }
                            span("form-btn work-group-constraints-add") {
                                i("material-icons") { +"add" }
                            }
                            div("work-group-constraints-add-list") {

                            }
                        }

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

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

            val name = params["name"] ?: return@post
            val description = params["description"] ?: return@post
            val interested = (params["interested"] ?: return@post).toIntOrNull() ?: 0
            val track = (params["track"] ?: return@post).toLongOrNull()?.let { TrackRepository.get(it) }
            val projector = params["projector"] == "on"
            val resolution = params["resolution"] == "on"
            val length = (params["length"] ?: return@post).toIntOrNull() ?: 0
            val language = (params["language"] ?: return@post).let {
                Language.values().find { l -> l.code == it } ?: Language.GERMAN
            }
            val leader = params["leader"]?.split("\\s*$separator+\\s*".toRegex()) ?: return@post

            val internet = params["internet"] == "on"
            val whiteboard = params["whiteboard"] == "on"
            val blackboard = params["blackboard"] == "on"
            val accessible = params["accessible"] == "on"

            val constraints = parseConstraintParam(params)

            val workGroup = WorkGroup(
                null,
                name = name,
                description = description,
                interested = interested,
                track = track,
                projector = projector,
                resolution = resolution,
                internet = internet,
                whiteboard = whiteboard,
                blackboard = blackboard,
                accessible = accessible,
                length = length,
                language = language,
                leader = leader,
                constraints = constraints
            )

            WorkGroupRepository.create(workGroup)

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

    get("/workgroup/{id}/delete") {
        authenticateOrRedirect(Permission.WORK_GROUP) {
            val workGroupId = call.parameters["id"]?.toLongOrNull() ?: return@get

            WorkGroupRepository.delete(workGroupId)

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

private fun parseConstraintParam(params: Map<String, String?>) = params.map { (key, value) ->
    val id = key.substringAfterLast("-").toIntOrNull() ?: -1
    id to when {
        key.startsWith("constraint-only-on-day") -> {
            value?.toIntOrNull()?.let { Constraint(ConstraintType.OnlyOnDay, day = it) }
        }
        key.startsWith("constraint-not-on-day") -> {
            value?.toIntOrNull()?.let { Constraint(ConstraintType.NotOnDay, day = it) }
        }
        key.startsWith("constraint-only-after-time") -> {
            if ("day" in key) {
                Constraint(ConstraintType.OnlyAfterTime, day = value?.toIntOrNull())
            } else {
                value?.toIntOrNull()?.let { Constraint(ConstraintType.OnlyAfterTime, time = it) }
            }
        }
        key.startsWith("constraint-only-before-time") -> {
            if ("day" in key) {
                Constraint(ConstraintType.OnlyBeforeTime, day = value?.toIntOrNull())
            } else {
                value?.toIntOrNull()?.let { Constraint(ConstraintType.OnlyBeforeTime, time = it) }
            }
        }
        key.startsWith("constraint-not-at-same-time") -> {
            value?.toLongOrNull()?.let { Constraint(ConstraintType.NotAtSameTime, workGroup = it) }
        }
        key.startsWith("constraint-only-after-work-group") -> {
            value?.toLongOrNull()?.let { Constraint(ConstraintType.OnlyAfterWorkGroup, workGroup = it) }
        }
        else -> null
    }
}.groupBy({ it.first }) {
    it.second
}.mapNotNull { (_, c) ->
    when {
        c.isEmpty() -> null
        c.size == 1 -> c.first()
        c.size == 2 -> {
            val c1 = c[0] ?: return@mapNotNull null
            val c2 = c[1] ?: return@mapNotNull null

            when {
                c1.type != c2.type -> null
                c1.type == ConstraintType.OnlyBeforeTime -> Constraint(
                    ConstraintType.OnlyBeforeTime,
                    day = c1.day ?: c2.day,
                    time = c1.time ?: c2.time
                )
                c1.type == ConstraintType.OnlyAfterTime -> Constraint(
                    ConstraintType.OnlyAfterTime,
                    day = c1.day ?: c2.day,
                    time = c1.time ?: c2.time
                )
                else -> null
            }
        }
        else -> null
    }
}
