package de.krisenvorrat.server

import de.krisenvorrat.server.db.DatabaseFactory
import de.krisenvorrat.server.model.CreateUserRequest
import de.krisenvorrat.server.model.ErrorResponse
import de.krisenvorrat.server.model.UpdatePasswordRequest
import de.krisenvorrat.server.model.UserDto
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.config.*
import io.ktor.server.testing.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test

class AdminTest {

    private val json = Json { ignoreUnknownKeys = true }
    private val adminToken = createTestAccessToken(
        userId = TEST_ADMIN_ID,
        username = TEST_ADMIN_USERNAME,
        isAdmin = true
    )
    private val userToken = createTestAccessToken(
        userId = TEST_USER_ID,
        username = TEST_USERNAME,
        isAdmin = false
    )

    private fun testApp(block: suspend ApplicationTestBuilder.() -> Unit) = testApplication {
        environment {
            config = MapApplicationConfig(*testMapConfig().toTypedArray())
        }
        application {
            DatabaseFactory.init(
                jdbcUrl = "jdbc:h2:mem:admin_test_${System.nanoTime()};DB_CLOSE_DELAY=-1",
                driver = "org.h2.Driver",
                adminPassword = "test-admin-pw"
            )
            configurePlugins()
        }
        block()
    }

    @Test
    fun test_listUsers_asAdmin_returnsUserList() = testApp {
        val response = client.get("/api/admin/users") { bearerAuth(adminToken) }
        assertEquals(HttpStatusCode.OK, response.status)
        val users = json.decodeFromString<List<UserDto>>(response.bodyAsText())
        assertTrue(users.isNotEmpty())
        assertTrue(users.any { it.username == "admin" })
    }

    @Test
    fun test_listUsers_asNonAdmin_returns403() = testApp {
        val response = client.get("/api/admin/users") { bearerAuth(userToken) }
        assertEquals(HttpStatusCode.Forbidden, response.status)
        val error = json.decodeFromString<ErrorResponse>(response.bodyAsText())
        assertEquals(403, error.status)
    }

    @Test
    fun test_listUsers_noToken_returns401() = testApp {
        val response = client.get("/api/admin/users")
        assertEquals(HttpStatusCode.Unauthorized, response.status)
    }

    @Test
    fun test_createUser_asAdmin_returns201() = testApp {
        val request = json.encodeToString(CreateUserRequest("newuser", "password123"))
        val response = client.post("/api/admin/users") {
            bearerAuth(adminToken)
            contentType(ContentType.Application.Json)
            setBody(request)
        }
        assertEquals(HttpStatusCode.Created, response.status)

        // Verify the user is in the list
        val listResponse = client.get("/api/admin/users") { bearerAuth(adminToken) }
        val users = json.decodeFromString<List<UserDto>>(listResponse.bodyAsText())
        assertTrue(users.any { it.username == "newuser" })
    }

    @Test
    fun test_createUser_asNonAdmin_returns403() = testApp {
        val request = json.encodeToString(CreateUserRequest("newuser", "password123"))
        val response = client.post("/api/admin/users") {
            bearerAuth(userToken)
            contentType(ContentType.Application.Json)
            setBody(request)
        }
        assertEquals(HttpStatusCode.Forbidden, response.status)
    }

    @Test
    fun test_deleteUser_asAdmin_returns204() = testApp {
        // Create a user first
        val createRequest = json.encodeToString(CreateUserRequest("todelete", "password123"))
        client.post("/api/admin/users") {
            bearerAuth(adminToken)
            contentType(ContentType.Application.Json)
            setBody(createRequest)
        }

        // Get the user id
        val listResponse = client.get("/api/admin/users") { bearerAuth(adminToken) }
        val users = json.decodeFromString<List<UserDto>>(listResponse.bodyAsText())
        val toDelete = users.first { it.username == "todelete" }

        // Delete
        val deleteResponse = client.delete("/api/admin/users/${toDelete.id}") { bearerAuth(adminToken) }
        assertEquals(HttpStatusCode.NoContent, deleteResponse.status)

        // Verify deleted
        val afterDelete = client.get("/api/admin/users") { bearerAuth(adminToken) }
        val usersAfter = json.decodeFromString<List<UserDto>>(afterDelete.bodyAsText())
        assertTrue(usersAfter.none { it.username == "todelete" })
    }

    @Test
    fun test_deleteUser_notFound_returns404() = testApp {
        val response = client.delete("/api/admin/users/non-existent-id") { bearerAuth(adminToken) }
        assertEquals(HttpStatusCode.NotFound, response.status)
    }

    @Test
    fun test_updatePassword_asAdmin_returns200() = testApp {
        // Create a user first
        val createRequest = json.encodeToString(CreateUserRequest("tochange", "oldpassword"))
        client.post("/api/admin/users") {
            bearerAuth(adminToken)
            contentType(ContentType.Application.Json)
            setBody(createRequest)
        }

        // Get user id
        val listResponse = client.get("/api/admin/users") { bearerAuth(adminToken) }
        val users = json.decodeFromString<List<UserDto>>(listResponse.bodyAsText())
        val user = users.first { it.username == "tochange" }

        // Update password
        val updateRequest = json.encodeToString(UpdatePasswordRequest("newpassword"))
        val updateResponse = client.put("/api/admin/users/${user.id}") {
            bearerAuth(adminToken)
            contentType(ContentType.Application.Json)
            setBody(updateRequest)
        }
        assertEquals(HttpStatusCode.OK, updateResponse.status)
    }
}
