package wang.yaojia.webterm.api.routes

import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import wang.yaojia.webterm.testsupport.FakeHttpTransport
import wang.yaojia.webterm.wire.HostEndpoint
import wang.yaojia.webterm.wire.HttpMethod
import java.util.UUID

/** End-to-end decode of each model from a well-formed body (exercises the custom serializers). */
class HappyPathDecodeTest {
    private companion object {
        const val BASE = "http://h:3000"
        val ID: UUID = UUID.fromString("11111111-2222-4333-8444-555555555555")
        const val ID_STR = "11111111-2222-4333-8444-555555555555"
    }

    private val transport = FakeHttpTransport()
    private val client = ApiClient(HostEndpoint.fromBaseUrl(BASE)!!, transport)

    @Test
    fun previewDecodesGeometryAndOpaqueData() = runTest {
        transport.queueSuccess(
            url = "$BASE/live-sessions/$ID_STR/preview",
            body = """{"id":"$ID_STR","cols":100,"rows":30,"data":"[0mhello"}""".toByteArray(),
        )
        val preview = client.preview(ID)
        assertEquals(ID, preview.id)
        assertEquals(100, preview.cols)
        assertEquals("[0mhello", preview.data)
    }

    @Test
    fun uiConfigDecodesAllowAutoMode() = runTest {
        transport.queueSuccess(url = "$BASE/config/ui", body = """{"allowAutoMode":true}""".toByteArray())
        assertTrue(client.uiConfig().allowAutoMode)
    }

    @Test
    fun liveSessionsDecodesTelemetry() = runTest {
        val body = """
            [{"id":"$ID_STR","createdAt":10,"clientCount":1,"status":"working","exited":false,"cols":80,"rows":24,
              "telemetry":{"at":99,"model":"opus","costUsd":0.42,"linesAdded":10,"pr":{"number":7,"url":"http://x"}}}]
        """.trimIndent()
        transport.queueSuccess(url = "$BASE/live-sessions", body = body.toByteArray())

        val session = client.liveSessions().single()
        assertEquals("opus", session.telemetry?.model)
        assertEquals(0.42, session.telemetry?.costUsd)
        assertEquals(7, session.telemetry?.pr?.number)
    }

    @Test
    fun projectDetailDecodesWorktreesSessionsAndClaudeMd() = runTest {
        val body = """
            {"name":"repo","path":"/r","isGit":true,"branch":"main","dirty":true,
             "worktrees":[{"path":"/r","branch":"main","isMain":true,"isCurrent":true},
                          {"path":"/r-wt","head":"abc123","isMain":false,"isCurrent":false}],
             "sessions":[{"id":"$ID_STR","title":"claude","status":"waiting","clientCount":1,"createdAt":5,"exited":false}],
             "hasClaudeMd":true,"claudeMd":"# Repo"}
        """.trimIndent()
        transport.queueSuccess(url = "$BASE/projects/detail?path=%2Fr", body = body.toByteArray())

        val detail = client.projectDetail("/r")
        assertEquals("main", detail.branch)
        assertEquals(2, detail.worktrees.size)
        assertTrue(detail.worktrees[0].isMain)
        assertEquals("abc123", detail.worktrees[1].head)
        assertEquals("claude", detail.sessions.single().title)
        assertEquals("# Repo", detail.claudeMd)
    }

    @Test
    fun prefsRoundTripsThroughGetAndPutEcho() = runTest {
        transport.queueSuccess(
            url = "$BASE/prefs",
            body = """{"favourites":["/a"],"collapsed":{"g":true},"vendor":1}""".toByteArray(),
        )
        val prefs = client.prefs()
        assertEquals(listOf("/a"), prefs.favourites)

        // PUT returns the server's sanitized echo — treat IT as truth (server dropped "vendor").
        transport.queueSuccess(
            method = HttpMethod.PUT,
            url = "$BASE/prefs",
            body = """{"favourites":["/a","/b"],"collapsed":{}}""".toByteArray(),
        )
        val echoed = client.putPrefs(prefs.withFavourites(listOf("/a", "/b")))
        assertEquals(listOf("/a", "/b"), echoed.favourites)
    }

    @Test
    fun prefsNonObjectBodyThrowsInvalidResponseBody() = runTest {
        transport.queueSuccess(url = "$BASE/prefs", body = "[]".toByteArray())
        assertEquals(
            ApiClientError.InvalidResponseBody,
            runCatching { client.prefs() }.exceptionOrNull(),
        )
    }
}
