package wang.yaojia.webterm.testsupport

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.wire.HttpMethod
import wang.yaojia.webterm.wire.HttpRequest
import java.io.IOException

/** Ports iOS `FakeHTTPTransportTests`, plus FIFO/method-scope and failure re-throw. */
class FakeHttpTransportTest {
    private companion object {
        const val ORIGIN = "http://192.168.1.5:3000"
        const val LIST_URL = "http://192.168.1.5:3000/live-sessions"
        const val UNQUEUED_URL = "http://192.168.1.5:3000/other"
    }

    @Test
    fun replaysQueuedResponsesRecordsRequestsAndFailsLoudlyWhenUnqueued() = runTest {
        // Arrange
        val transport = FakeHttpTransport()
        transport.queueSuccess(url = LIST_URL, body = "[]".toByteArray())

        val request = HttpRequest(
            method = HttpMethod.GET,
            url = LIST_URL,
            headers = mapOf("Origin" to ORIGIN),
        )

        // Act
        val response = transport.send(request)

        // Assert: the queued response came back for that route.
        assertEquals("[]", String(response.body))
        assertEquals(FakeHttpTransport.DEFAULT_OK_STATUS, response.status)

        // Assert: the request was recorded verbatim, headers included (Origin-iff-guarded tests).
        val recorded = transport.recordedRequests
        assertEquals(1, recorded.size)
        assertEquals(LIST_URL, recorded[0].url)
        assertEquals(ORIGIN, recorded[0].headers["Origin"])

        // Assert: an unqueued route throws an explicit, identifying error — and is still recorded.
        val thrown = runCatching {
            transport.send(HttpRequest(HttpMethod.GET, UNQUEUED_URL))
        }.exceptionOrNull()
        assertEquals(FakeHttpTransportError.NoQueuedResponse(HttpMethod.GET, UNQUEUED_URL), thrown)
        assertEquals(2, transport.recordedRequests.size)
    }

    @Test
    fun queuesAreFifoPerRouteAndMethodScoped() = runTest {
        val transport = FakeHttpTransport()
        val url = "http://h/live-sessions/abc"
        transport.queueSuccess(method = HttpMethod.GET, url = url, body = "first".toByteArray())
        transport.queueSuccess(method = HttpMethod.GET, url = url, body = "second".toByteArray())
        transport.queueSuccess(method = HttpMethod.DELETE, url = url, status = 204)

        assertEquals("first", String(transport.send(HttpRequest(HttpMethod.GET, url)).body))
        assertEquals("second", String(transport.send(HttpRequest(HttpMethod.GET, url)).body))
        assertEquals(204, transport.send(HttpRequest(HttpMethod.DELETE, url)).status)
    }

    @Test
    fun queuedFailureIsRethrown() = runTest {
        val transport = FakeHttpTransport()
        val url = "http://h/x"
        transport.queueFailure(url = url, error = IOException("connection refused"))

        val thrown = runCatching { transport.send(HttpRequest(HttpMethod.GET, url)) }.exceptionOrNull()
        assertTrue(thrown is IOException)
        assertEquals("connection refused", thrown?.message)
    }
}
