/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.data.api.dto

import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

/**
 * Decode tests for the public site/identity DTOs (`/public/status`,
 * `/public/settings`). Covers the `StatusDto.isMaintenance` derivation, nested
 * branding/registration/push blocks, and the additive-field tolerance.
 */
class PublicDtoTest {

    private val json = Json {
        ignoreUnknownKeys = true
        explicitNulls = false
        coerceInputValues = true
    }

    @Test fun statusDecodesVersionAndMaintenanceFlag() {
        val dto = json.decodeFromString<StatusDto>(
            """{"mode":"MAINTENANCE","status_message":"back soon",
                "version":{"service":"web","api":"v1","server":"1.2.3"}}""",
        )
        assertTrue(dto.isMaintenance) // case-insensitive
        assertEquals("back soon", dto.statusMessage)
        assertEquals("1.2.3", dto.version.server)
    }

    @Test fun liveStatusIsNotMaintenance() {
        assertFalse(json.decodeFromString<StatusDto>("""{"mode":"live"}""").isMaintenance)
    }

    @Test fun statusDefaultsWhenEmpty() {
        val dto = json.decodeFromString<StatusDto>("{}")
        assertEquals("live", dto.mode)
        assertFalse(dto.isMaintenance)
        assertEquals("", dto.version.api)
    }

    @Test fun settingsDecodesBrandRegistrationAndPush() {
        val dto = json.decodeFromString<SettingsDto>(
            """{
                "site_title":"UOMysticmoon","status_message":"welcome",
                "registration":{"password":true,"sso":false},
                "gameAccountSignup":true,
                "brand":{"name":"UOMysticmoon","shortName":"UOM","accent":"#7f99bd",
                         "logo":"/logo.png","hero":"/hero.png","contactEmail":"a@b.c","url":"https://x"},
                "push":{"ntfyUrl":"https://ntfy.example.com"}
            }""",
        )
        assertEquals("UOMysticmoon", dto.siteTitle)
        assertTrue(dto.registration.password)
        assertFalse(dto.registration.sso)
        assertTrue(dto.gameAccountSignup)
        assertEquals("#7f99bd", dto.brand.accent)
        assertEquals("/logo.png", dto.brand.logo)
        assertEquals("https://ntfy.example.com", dto.push.ntfyUrl)
    }

    @Test fun settingsDefaultsOnOlderBackend() {
        // A backend that predates push/branding: nested blocks fall back to defaults.
        val dto = json.decodeFromString<SettingsDto>("""{"site_title":"Bare"}""")
        assertFalse(dto.registration.password)
        assertEquals("", dto.brand.name)
        assertEquals(null, dto.push.ntfyUrl)
        // …and one that predates admin theming: both M12 fields are simply absent
        // (THEMING_AND_NAV.md §2 — absence means the shipped defaults).
        assertEquals(null, dto.theme)
        assertEquals(null, dto.navPublic)
    }

    @Test fun settingsDecodesTheThemeAndNavRows() {
        val dto = json.decodeFromString<SettingsDto>(
            """{
                "theme":{"--accent":"#c8a45c","--radius-card":"3px"},
                "nav_public":"{\"/wiki\":{\"label\":\"Codex\"}}",
                "theme_visual":"{\"preset\":\"fantasy\"}"
            }""",
        )
        assertEquals("#c8a45c", (dto.theme as JsonObject)["--accent"]?.jsonPrimitive?.content)
        // nav_public stays a raw string here: settings.value is TEXT, so it is
        // parsed a second time by SiteAppearance.
        assertEquals("""{"/wiki":{"label":"Codex"}}""", dto.navPublic)
    }

    @Test fun anUnexpectedThemeKindStillDecodesTheRest() {
        // `theme` is a raw JsonElement precisely so a value we did not expect
        // cannot fail the decode and take brand/push with it.
        val dto = json.decodeFromString<SettingsDto>(
            """{"theme":"nonsense","brand":{"name":"UOMysticmoon"}}""",
        )
        assertEquals("UOMysticmoon", dto.brand.name)
    }
}
