package de.krisenvorrat.server.routes

import de.krisenvorrat.server.model.AssignInventoryRequest
import de.krisenvorrat.server.model.CreateUserRequest
import de.krisenvorrat.server.model.ErrorResponse
import de.krisenvorrat.server.model.UpdatePasswordRequest
import de.krisenvorrat.server.repository.InventoryRepository
import de.krisenvorrat.server.repository.UserRepository
import de.krisenvorrat.server.security.UserPrincipal
import io.ktor.http.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import org.mindrot.jbcrypt.BCrypt
import java.util.UUID

internal fun Route.adminRoutes(userRepository: UserRepository, inventoryRepository: InventoryRepository) {
    route("/api/admin/users") {
        get {
            val principal = call.principal<UserPrincipal>()
                ?: return@get call.respond(HttpStatusCode.Unauthorized, ErrorResponse(status = 401, message = "Unauthorized"))
            if (!principal.isAdmin) {
                call.respond(HttpStatusCode.Forbidden, ErrorResponse(status = 403, message = "Admin access required"))
                return@get
            }
            call.respond(HttpStatusCode.OK, userRepository.listAll())
        }

        post {
            val principal = call.principal<UserPrincipal>()
                ?: return@post call.respond(HttpStatusCode.Unauthorized, ErrorResponse(status = 401, message = "Unauthorized"))
            if (!principal.isAdmin) {
                call.respond(HttpStatusCode.Forbidden, ErrorResponse(status = 403, message = "Admin access required"))
                return@post
            }
            val request = call.receive<CreateUserRequest>()
            if (request.username.isBlank() || request.password.isBlank()) {
                call.respond(HttpStatusCode.BadRequest, ErrorResponse(status = 400, message = "Username and password must not be empty"))
                return@post
            }
            if (request.username.length > 255) {
                call.respond(HttpStatusCode.BadRequest, ErrorResponse(status = 400, message = "Username too long (max 255 characters)"))
                return@post
            }
            val passwordHash = BCrypt.hashpw(request.password, BCrypt.gensalt())
            val created = userRepository.create(UUID.randomUUID().toString(), request.username, passwordHash)
            if (!created) {
                call.respond(HttpStatusCode.Conflict, ErrorResponse(status = 409, message = "Username already exists"))
                return@post
            }
            call.respond(HttpStatusCode.Created, mapOf("message" to "User created"))
        }

        delete("/{id}") {
            val principal = call.principal<UserPrincipal>()
                ?: return@delete call.respond(HttpStatusCode.Unauthorized, ErrorResponse(status = 401, message = "Unauthorized"))
            if (!principal.isAdmin) {
                call.respond(HttpStatusCode.Forbidden, ErrorResponse(status = 403, message = "Admin access required"))
                return@delete
            }
            val id = call.parameters["id"] ?: return@delete call.respond(
                HttpStatusCode.BadRequest, ErrorResponse(status = 400, message = "Missing user id")
            )
            if (id == principal.userId) {
                call.respond(HttpStatusCode.Conflict, ErrorResponse(status = 409, message = "Cannot delete your own account"))
                return@delete
            }
            val userRow = userRepository.findById(id)
            val oldInventoryId = userRow?.inventoryId
            val deleted = userRepository.deleteById(id)
            if (!deleted) {
                call.respond(HttpStatusCode.NotFound, ErrorResponse(status = 404, message = "User not found"))
            } else {
                if (oldInventoryId != null) {
                    inventoryRepository.cleanupOrphanedInventory(oldInventoryId)
                }
                call.respond(HttpStatusCode.NoContent)
            }
        }

        put("/{id}") {
            val principal = call.principal<UserPrincipal>()
                ?: return@put call.respond(HttpStatusCode.Unauthorized, ErrorResponse(status = 401, message = "Unauthorized"))
            if (!principal.isAdmin) {
                call.respond(HttpStatusCode.Forbidden, ErrorResponse(status = 403, message = "Admin access required"))
                return@put
            }
            val id = call.parameters["id"] ?: return@put call.respond(
                HttpStatusCode.BadRequest, ErrorResponse(status = 400, message = "Missing user id")
            )
            val request = call.receive<UpdatePasswordRequest>()
            if (request.password.isBlank()) {
                call.respond(HttpStatusCode.BadRequest, ErrorResponse(status = 400, message = "Password must not be empty"))
                return@put
            }
            val passwordHash = BCrypt.hashpw(request.password, BCrypt.gensalt())
            val updated = userRepository.updatePassword(id, passwordHash)
            if (!updated) {
                call.respond(HttpStatusCode.NotFound, ErrorResponse(status = 404, message = "User not found"))
            } else {
                call.respond(HttpStatusCode.OK, mapOf("message" to "Password updated"))
            }
        }

        // Assign user to an existing inventory
        put("/{id}/inventory") {
            val principal = call.principal<UserPrincipal>()
                ?: return@put call.respond(HttpStatusCode.Unauthorized, ErrorResponse(status = 401, message = "Unauthorized"))
            if (!principal.isAdmin) {
                call.respond(HttpStatusCode.Forbidden, ErrorResponse(status = 403, message = "Admin access required"))
                return@put
            }
            val id = call.parameters["id"] ?: return@put call.respond(
                HttpStatusCode.BadRequest, ErrorResponse(status = 400, message = "Missing user id")
            )
            val request = call.receive<AssignInventoryRequest>()
            if (userRepository.findById(id) == null) {
                call.respond(HttpStatusCode.NotFound, ErrorResponse(status = 404, message = "User not found"))
                return@put
            }
            if (!inventoryRepository.inventoryExists(request.inventoryId)) {
                call.respond(HttpStatusCode.NotFound, ErrorResponse(status = 404, message = "Inventory not found"))
                return@put
            }
            val oldInventoryId = inventoryRepository.assignUserToInventory(id, request.inventoryId)
            if (oldInventoryId != null && oldInventoryId != request.inventoryId) {
                inventoryRepository.cleanupOrphanedInventory(oldInventoryId)
            }
            call.respond(HttpStatusCode.OK, mapOf("inventoryId" to request.inventoryId))
        }

        // Create a new inventory for a user (disconnect from shared inventory)
        post("/{id}/inventory/new") {
            val principal = call.principal<UserPrincipal>()
                ?: return@post call.respond(HttpStatusCode.Unauthorized, ErrorResponse(status = 401, message = "Unauthorized"))
            if (!principal.isAdmin) {
                call.respond(HttpStatusCode.Forbidden, ErrorResponse(status = 403, message = "Admin access required"))
                return@post
            }
            val id = call.parameters["id"] ?: return@post call.respond(
                HttpStatusCode.BadRequest, ErrorResponse(status = 400, message = "Missing user id")
            )
            if (userRepository.findById(id) == null) {
                call.respond(HttpStatusCode.NotFound, ErrorResponse(status = 404, message = "User not found"))
                return@post
            }
            val newInventoryId = inventoryRepository.createInventory()
            val oldInventoryId = inventoryRepository.assignUserToInventory(id, newInventoryId)
            if (oldInventoryId != null && oldInventoryId != newInventoryId) {
                inventoryRepository.cleanupOrphanedInventory(oldInventoryId)
            }
            call.respond(HttpStatusCode.Created, mapOf("inventoryId" to newInventoryId))
        }
    }

    // List all inventories with their assigned users
    get("/api/admin/inventories") {
        val principal = call.principal<UserPrincipal>()
            ?: return@get call.respond(HttpStatusCode.Unauthorized, ErrorResponse(status = 401, message = "Unauthorized"))
        if (!principal.isAdmin) {
            call.respond(HttpStatusCode.Forbidden, ErrorResponse(status = 403, message = "Admin access required"))
            return@get
        }
        call.respond(HttpStatusCode.OK, inventoryRepository.listInventoriesWithUsers())
    }

    // Aggregated inventory statistics for admin dashboard
    get("/api/admin/stats") {
        val principal = call.principal<UserPrincipal>()
            ?: return@get call.respond(HttpStatusCode.Unauthorized, ErrorResponse(status = 401, message = "Unauthorized"))
        if (!principal.isAdmin) {
            call.respond(HttpStatusCode.Forbidden, ErrorResponse(status = 403, message = "Admin access required"))
            return@get
        }
        call.respond(HttpStatusCode.OK, inventoryRepository.getAggregatedStats())
    }
}

