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

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

/**
 * Decode/encode tests for the player self-service game-data DTOs
 * (`/player/shard/…`): account linking, roster, the full character sheet, vendors,
 * sales, and own-houses. Only the fields the text-only v1 renders are asserted.
 */
class PlayerGameDataDtoTest {

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

    @Test fun linkRequestAndResultRoundTrip() {
        assertTrue(json.encodeToString(ShardLinkRequest("ABC123")).contains("\"code\":\"ABC123\""))
        val result = json.decodeFromString<ShardLinkResultDto>("""{"linked":true,"account":"acct1"}""")
        assertTrue(result.linked)
        assertEquals("acct1", result.account)
        assertTrue(json.encodeToString(CreateGameAccountRequest("acct1", "pw")).contains("\"account\":\"acct1\""))
    }

    @Test fun linkedAccountDecodes() {
        val dto = json.decodeFromString<ShardLinkDto>(
            """{"account":"acct1","userId":42,"charName":"Gwen","linkedAt":"2026-07-20T00:00:00Z"}""",
        )
        assertEquals("acct1", dto.account)
        assertEquals(42L, dto.userId)
    }

    @Test fun rosterDecodesCharacters() {
        val dto = json.decodeFromString<RosterDto>(
            """{"acct":"acct1","chars":[
                {"slot":0,"serial":"0x24C","name":"Gwen","body":401,"online":true},
                {"slot":1,"serial":"0x24D","name":"Alt","online":false}]}""",
        )
        assertEquals(2, dto.chars.size)
        assertTrue(dto.chars[0].online)
        assertEquals("0x24C", dto.chars[0].serial)
    }

    @Test fun charSheetDecodesStatsSkillsEquipmentTitlesGuild() {
        val dto = json.decodeFromString<CharProfileDto>(
            """{
                "serial":"0x24C","name":"Gwen","title":"the Brave","online":true,"acct":"acct1",
                "stats":{"str":100,"dex":90,"int":80,"hits":95,"hitsMax":100,
                         "resist":{"phys":70,"fire":50,"cold":45,"pois":40,"energy":35}},
                "skills":[{"n":"Swords","base":100.0,"value":110.0,"cap":120.0}],
                "equipment":[{"serial":"0x9","layer":"OneHanded","itemId":5044,"hue":0}],
                "titles":{"selected":0,"reward":["1049643"],"fameKarma":"Glorious"},
                "guild":{"name":"Knights","abbr":"KoT"},
                "governorOf":["Britain"]
            }""",
        )
        assertEquals("Gwen", dto.name)
        assertEquals(100, dto.stats!!.str)
        assertEquals(70, dto.stats!!.resist!!.phys)
        assertEquals(110.0, dto.skills.first().value!!, 0.0)
        assertEquals("OneHanded", dto.equipment.first().layer)
        assertEquals("Glorious", dto.titles!!.fameKarma)
        assertEquals("Knights", dto.guild!!.name)
        assertEquals(listOf("Britain"), dto.governorOf)
    }

    @Test fun charSheetToleratesMinimalPayload() {
        val dto = json.decodeFromString<CharProfileDto>("""{"serial":"0x1","name":"Bare"}""")
        assertEquals(null, dto.stats)
        assertTrue(dto.skills.isEmpty())
        assertTrue(dto.equipment.isEmpty())
        assertFalse(dto.online)
    }

    @Test fun vendorSnapshotAndListingsDecode() {
        val dto = json.decodeFromString<VendorSnapshotDto>(
            """{"acct":"acct1","vendors":[
                {"serial":"0x9","shopName":"Wares","holdGold":5000,"map":"Felucca","x":1,"y":2,
                 "listings":[{"serial":"0xA","itemId":3862,"amount":5,"price":250,"forSale":true}]}]}""",
        )
        val vendor = dto.vendors.first()
        assertEquals("Wares", vendor.shopName)
        assertEquals(5000L, vendor.holdGold)
        val listing = vendor.listings.first()
        assertEquals(250L, listing.price)
        assertTrue(listing.forSale)
    }

    @Test fun vendorSaleDecodes() {
        val dto = json.decodeFromString<VendorSaleDto>(
            """{"t":1700000000000,"itemType":"katana","amount":1,"price":1000,"commission":50,"ownerAcct":"acct1"}""",
        )
        assertEquals(1000L, dto.price)
        assertEquals(50, dto.commission)
    }

    @Test fun playerHouseDecodesDecayFields() {
        val dto = json.decodeFromString<PlayerHouseDto>(
            """{"serial":"0x40001","stage":"LikeNew","region":"Britain","name":"Keep",
                "isIdoc":false,"builtOn":"2026-01-01T00:00:00Z","lastRefreshed":"2026-07-22T00:00:00Z"}""",
        )
        assertEquals("LikeNew", dto.stage)
        assertEquals("Keep", dto.name)
        assertFalse(dto.isIdoc)
    }
}
