/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.plugins

import de.steamwar.sql.SWException
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.UserPerm
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.application.hooks.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import kotlinx.serialization.Serializable

@Serializable
data class SWUserSession(val userId: Int)

data class SWAuthPrincipal(val user: SteamwarUser) : Principal

class SWAuthConfig {
    var permission: UserPerm? = null
    var allowedMethods = mutableListOf<HttpMethod>()
    var userCheck: SWAuthPrincipal.(ApplicationRequest) -> Boolean = { true }
    var mustAuth: Boolean = false

    fun allowMethod(method: HttpMethod) {
        allowedMethods.add(method)
    }

    fun allowMethods(methods: List<HttpMethod>) {
        allowedMethods.addAll(methods)
    }

    fun userCheck(check: SWAuthPrincipal.(ApplicationRequest) -> Boolean) {
        userCheck = check
    }
}

val SWPermissionCheck = createRouteScopedPlugin("SWAuth", ::SWAuthConfig) {
    pluginConfig.apply {
        on(AuthenticationChecked) { call ->
            if (call.request.httpMethod in allowedMethods) {
                if (mustAuth) {
                    val token = call.principal<SWAuthPrincipal>()

                    if (token == null) {
                        call.respond(HttpStatusCode.Unauthorized)
                    }
                }

                return@on
            }

            val token = call.principal<SWAuthPrincipal>()

            if (token == null) {
                call.respond(HttpStatusCode.Unauthorized)
                return@on
            }

            if (permission != null && !token.user.hasPerm(permission!!)) {
                call.respond(HttpStatusCode.Forbidden)
                return@on
            }

            if (!token.userCheck(call.request)) {
                call.respond(HttpStatusCode.Forbidden)
                return@on
            }
        }
    }
}

val ErrorLogger = createApplicationPlugin("SWLogger") {
    on(CallFailed) { call, cause ->
        val msg = """
{
URI: ${call.request.uri}
Method: ${call.request.httpMethod.value}
Headers: ${call.request.headers.entries().joinToString("\n    ") { "${it.key}: ${it.value}" }}

Message: ${cause.message}
}
        """

        SWException.log(msg, cause.stackTraceToString())
        call.response.headers.append("X-Caught", "1")
    }

    /*
    onCallRespond { call ->
        if ((call.response.status() ?: HttpStatusCode.OK).isSuccess()) {
            return@onCallRespond
        }

        val msg = """
            URI: ${call.request.uri}
            Method: ${call.request.httpMethod.value}
            Response: ${call.response.status()?.value}
            IP: ${call.request.headers["X-Forwarded-For"] ?: call.request.local.remoteHost}
            UserAgent: ${call.request.headers["User-Agent"]}
        """.trimIndent()

        val stack = """
            Headers:
            ${call.request.headers.entries().joinToString("\n    ") { "${it.key}: ${it.value}" }}
            Body:
            ${call.request.receiveChannel().toByteArray().decodeToString()}
        """.trimIndent()

        SWException.log(msg, stack)
    }*/
}