package de.walamana.views

import at.favre.lib.crypto.bcrypt.BCrypt
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import de.walamana.models.MinistrantenDao
import de.walamana.plugins.Security
import de.walamana.plugins.getJWTEnvironment
import de.walamana.plugins.mid
import de.walamana.plugins.username
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.Serializable
import java.util.*
import kotlin.math.max
import kotlin.math.min

@Serializable
data class AuthenticationRequest(
    val username: String,
    val password: String
)

@Serializable
data class AuthenticationResult(
    val success: Boolean,
    val token: String? = null,
    val privileges: List<String>? = null,
)

@Serializable
data class AuthenticationUpdateRequest(
    val newPassword: String
)

@Serializable
data class PasswordResetRequest(
    val username: String
)

fun Route.configureAuthenticationRoutes() {
    route("/auth") {
        post {
            val request = call.receive<AuthenticationRequest>()
            val jwtEnv = application.getJWTEnvironment();

            val ministrant = Security.authenticateUser(application, request.username, request.password)
            if(ministrant == null){
                call.respond(AuthenticationResult(false))
                return@post
            }

            val token = Security.createToken(jwtEnv, ministrant)
            val expiry = Security.DEFAULT_EXPIRY().toGMTString()

            call.response.header(
                "Set-Cookie",
                "token=$token; HttpOnly; Expires=$expiry"
            )

            call.respond(AuthenticationResult(true, token, ministrant.privileges))
        }

        authenticate {
            put("update") {
                val principal = call.principal<JWTPrincipal>()!!
                val request = call.receive<AuthenticationUpdateRequest>()

                Security.setPassword(principal.payload.username(), request.newPassword)

                call.respond(hashMapOf("success" to true))
            }
            post("reset") {
                val principal = call.principal<JWTPrincipal>()!!
                if(principal.payload.username() != "admin") {
                    // TODO: fail
                    return@post
                }
                val request = call.receive<PasswordResetRequest>()
                var newPassword = "";
                for(i in 0..2) {
                    newPassword += BCrypt.withDefaults()
                        .hash(Math.random().toInt() * 3 + 4, (Math.random().toString() + Math.random().toString()).toCharArray())
                };
                newPassword = newPassword.substring(3..10)

                Security.setPassword(request.username, newPassword)


                call.respond(hashMapOf("password" to newPassword))


            }
        }
    }
}
