package top.r394realms.ltdmanagertest.whitelist

import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import top.r3944realms.ltdmanager.module.WhitelistAuditModule.AuditEntry
import top.r3944realms.ltdmanager.module.WhitelistAuditModule.AuditState

class WhitelistAuditStateTest {

    private val json = Json { ignoreUnknownKeys = true; coerceInputValues = true }

    // ====== AuditState ======

    @Test
    fun `empty AuditState round-trips correctly`() {
        val state = AuditState()
        val serialized = json.encodeToString(state)
        val deserialized = json.decodeFromString<AuditState>(serialized)

        assertTrue(deserialized.entries.isEmpty())
    }

    @Test
    fun `AuditState round-trip with single entry`() {
        val entry = AuditEntry(
            qq = "123456789",
            playerId = 42,
            playerName = "Steve",
            detectedTime = 1717891200000L,
            rejectedTime = 1717891200000L,
            warningSentTime = 0L,
            reactivated = false,
        )
        val state = AuditState(entries = mapOf("123456789" to entry))

        val serialized = json.encodeToString(state)
        val deserialized = json.decodeFromString<AuditState>(serialized)

        assertEquals(1, deserialized.entries.size)
        val restored = deserialized.entries["123456789"]!!
        assertEquals("123456789", restored.qq)
        assertEquals(42, restored.playerId)
        assertEquals("Steve", restored.playerName)
        assertEquals(1717891200000L, restored.detectedTime)
        assertEquals(1717891200000L, restored.rejectedTime)
        assertEquals(0L, restored.warningSentTime)
        assertFalse(restored.reactivated)
    }

    @Test
    fun `AuditState round-trip with reactivated entry`() {
        val entry = AuditEntry(
            qq = "111",
            playerId = 1,
            playerName = "Alex",
            detectedTime = 1000L,
            rejectedTime = 1000L,
            warningSentTime = 5000L,
            reactivated = true,
        )
        val state = AuditState(entries = mapOf("111" to entry))

        val serialized = json.encodeToString(state)
        val deserialized = json.decodeFromString<AuditState>(serialized)

        assertTrue(deserialized.entries["111"]!!.reactivated)
        assertEquals(5000L, deserialized.entries["111"]!!.warningSentTime)
    }

    @Test
    fun `AuditState round-trip with multiple entries`() {
        val entries = mapOf(
            "aaa" to AuditEntry(qq = "aaa", playerId = 1, playerName = "A", detectedTime = 1L),
            "bbb" to AuditEntry(qq = "bbb", playerId = 2, playerName = "B", detectedTime = 2L, rejectedTime = 2L),
            "ccc" to AuditEntry(qq = "ccc", playerId = 3, playerName = "C", detectedTime = 3L, rejectedTime = 3L, warningSentTime = 3L, reactivated = true),
        )
        val state = AuditState(entries = entries)

        val serialized = json.encodeToString(state)
        val deserialized = json.decodeFromString<AuditState>(serialized)

        assertEquals(3, deserialized.entries.size)
        assertFalse(deserialized.entries["aaa"]!!.reactivated)
        assertEquals(0L, deserialized.entries["aaa"]!!.rejectedTime)
        assertTrue(deserialized.entries["ccc"]!!.reactivated)
    }

    // ====== AuditEntry defaults ======

    @Test
    fun `AuditEntry defaults are zero-empty-false`() {
        val entry = AuditEntry(
            qq = "test",
            playerId = 1,
            playerName = "test",
            detectedTime = 0L,
        )
        assertEquals(0L, entry.rejectedTime)
        assertEquals(0L, entry.warningSentTime)
        assertFalse(entry.reactivated)
    }

    @Test
    fun `AuditEntry copy preserves fields`() {
        val entry = AuditEntry(
            qq = "qq", playerId = 5, playerName = "P",
            detectedTime = 100L, rejectedTime = 200L, warningSentTime = 300L, reactivated = true,
        )
        val copied = entry.copy(warningSentTime = 400L, reactivated = false)

        assertEquals("qq", copied.qq)
        assertEquals(5, copied.playerId)
        assertEquals(100L, copied.detectedTime)
        assertEquals(200L, copied.rejectedTime)
        assertEquals(400L, copied.warningSentTime)
        assertFalse(copied.reactivated)
    }

    // ====== Deserialization with missing fields ======

    @Test
    fun `deserialize AuditState with missing optional fields uses defaults`() {
        val jsonStr = """
            {"entries": {"test": {"qq":"test","playerId":1,"playerName":"T","detectedTime":0}}}
        """.trimIndent()

        val state = json.decodeFromString<AuditState>(jsonStr)
        val entry = state.entries["test"]!!

        assertEquals(0L, entry.rejectedTime)
        assertEquals(0L, entry.warningSentTime)
        assertFalse(entry.reactivated)
    }

    @Test
    fun `deserialize empty JSON object as AuditState`() {
        val state = json.decodeFromString<AuditState>("{}")
        assertTrue(state.entries.isEmpty())
    }
}
