package de.krisenvorrat.server

import de.krisenvorrat.server.db.DatabaseFactory
import de.krisenvorrat.shared.model.MessageDto
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.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test

/**
 * Tests für UTF-8-Unterstützung im Chat (Umlaute, Sonderzeichen, Emojis).
 */
class Utf8MessagingTest {

    private val json = Json {
        ignoreUnknownKeys = true
        encodeDefaults = true
    }

    private val adminToken = createTestAccessToken(
        userId = TEST_ADMIN_ID,
        username = TEST_ADMIN_USERNAME,
        isAdmin = true
    )

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

    private suspend fun ApplicationTestBuilder.createUser(username: String, password: String): String {
        client.post("/api/admin/users") {
            bearerAuth(adminToken)
            contentType(ContentType.Application.Json)
            setBody("""{"username":"$username","password":"$password"}""")
        }
        // Get user ID by listing all users
        val listResponse = client.get("/api/admin/users") {
            bearerAuth(adminToken)
        }
        val users = json.decodeFromString<JsonArray>(listResponse.bodyAsText())
        return users.first { it.jsonObject["username"]?.jsonPrimitive?.content == username }
            .jsonObject["id"]!!.jsonPrimitive.content
    }

    @Test
    fun test_sendMessage_withUmlauts_preservesCharacters() = testApp {
        // Given
        val bobId = createUser("bob", "bob123")
        val aliceId = createUser("alice", "alice123")
        val aliceToken = createTestAccessToken(userId = aliceId, username = "alice")
        val bodyText = "Schöne Grüße aus München – äöüÄÖÜß"

        // When
        val response = client.post("/api/messages") {
            bearerAuth(aliceToken)
            contentType(ContentType.Application.Json)
            setBody("""{"receiverId":"$bobId","body":"$bodyText","sentAt":1700000000000}""")
        }

        // Then
        assertEquals(HttpStatusCode.Created, response.status)
        val responseBody = response.bodyAsText()
        assertTrue("Response should contain umlauts", responseBody.contains("Schöne Grüße aus München"))
        assertTrue("Response should contain äöüÄÖÜß", responseBody.contains("äöüÄÖÜß"))
    }

    @Test
    fun test_sendMessage_withEmojis_preservesCharacters() = testApp {
        // Given
        val bobId = createUser("bob", "bob123")
        val aliceId = createUser("alice", "alice123")
        val aliceToken = createTestAccessToken(userId = aliceId, username = "alice")
        val bodyText = "Guten Morgen! \uD83D\uDE00\uD83C\uDF89❤\uFE0F\uD83D\uDC4D"

        // When
        val response = client.post("/api/messages") {
            bearerAuth(aliceToken)
            contentType(ContentType.Application.Json)
            setBody("""{"receiverId":"$bobId","body":"$bodyText","sentAt":1700000001000}""")
        }

        // Then
        assertEquals(HttpStatusCode.Created, response.status)
        val msg = json.decodeFromString<MessageDto>(response.bodyAsText())
        assertEquals(bodyText, msg.body)
    }

    @Test
    fun test_sendMessage_withMixedContent_preservesAll() = testApp {
        // Given
        val bobId = createUser("bob", "bob123")
        val aliceId = createUser("alice", "alice123")
        val aliceToken = createTestAccessToken(userId = aliceId, username = "alice")
        val bodyText = "Schöner Tag \uD83D\uDE00! Grüße & Küsse \uD83D\uDC8B"

        // When
        val response = client.post("/api/messages") {
            bearerAuth(aliceToken)
            contentType(ContentType.Application.Json)
            setBody("""{"receiverId":"$bobId","body":"$bodyText","sentAt":1700000002000}""")
        }

        // Then
        assertEquals(HttpStatusCode.Created, response.status)
        val msg = json.decodeFromString<MessageDto>(response.bodyAsText())
        assertEquals(bodyText, msg.body)
    }

    @Test
    fun test_getConversation_withUmlauts_returnsCorrectCharacters() = testApp {
        // Given
        val bobId = createUser("bob", "bob123")
        val aliceId = createUser("alice", "alice123")
        val aliceToken = createTestAccessToken(userId = aliceId, username = "alice")
        val bodyText = "Tschüss und auf Wiedersehen! \uD83C\uDDE9\uD83C\uDDEA"
        client.post("/api/messages") {
            bearerAuth(aliceToken)
            contentType(ContentType.Application.Json)
            setBody("""{"receiverId":"$bobId","body":"$bodyText","sentAt":1700000003000}""")
        }

        // When
        val response = client.get("/api/messages/$bobId") {
            bearerAuth(aliceToken)
        }

        // Then
        assertEquals(HttpStatusCode.OK, response.status)
        val responseBody = response.bodyAsText()
        assertTrue("Conversation should contain umlauts", responseBody.contains("Tschüss"))
        assertTrue("Conversation should contain flag emoji", responseBody.contains("\uD83C\uDDE9\uD83C\uDDEA"))
    }

    @Test
    fun test_messageResponse_hasUtf8ContentType() = testApp {
        // Given
        val bobId = createUser("bob", "bob123")
        val aliceId = createUser("alice", "alice123")
        val aliceToken = createTestAccessToken(userId = aliceId, username = "alice")

        // When
        val response = client.post("/api/messages") {
            bearerAuth(aliceToken)
            contentType(ContentType.Application.Json)
            setBody("""{"receiverId":"$bobId","body":"Test","sentAt":1700000004000}""")
        }

        // Then
        assertEquals(HttpStatusCode.Created, response.status)
        assertEquals(
            ContentType.Application.Json.withCharset(Charsets.UTF_8),
            response.contentType()
        )
    }

    @Test
    fun test_sendMessage_withMultiCodepointEmoji_preservesIntact() = testApp {
        // Given – Multi-Codepoint emoji: family, skin tone modifiers
        val bobId = createUser("bob", "bob123")
        val aliceId = createUser("alice", "alice123")
        val aliceToken = createTestAccessToken(userId = aliceId, username = "alice")
        val bodyText = "Familie: \uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC67\u200D\uD83D\uDC66 und \uD83D\uDC4B\uD83C\uDFFD"

        // When
        val response = client.post("/api/messages") {
            bearerAuth(aliceToken)
            contentType(ContentType.Application.Json)
            setBody("""{"receiverId":"$bobId","body":"$bodyText","sentAt":1700000005000}""")
        }

        // Then
        assertEquals(HttpStatusCode.Created, response.status)
        val msg = json.decodeFromString<MessageDto>(response.bodyAsText())
        assertEquals(bodyText, msg.body)
    }
}
