package space.nstart.pcp_tgu_service.integration.api

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.sun.net.httpserver.HttpServer
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import space.nstart.pcp_tgu_service.config.ClassifierProperties
import space.nstart.pcp_tgu_service.dto.NsiPlatformDto
import java.net.InetSocketAddress
import java.nio.file.Files
import java.nio.file.Path
import java.time.Duration

class PlatformsClassifierClientTest {

    @Test
    fun `sample platforms json is deserialized as root array and ignores unknown fields`() {
        val objectMapper = jacksonObjectMapper().findAndRegisterModules()
        val json = Files.readString(Path.of("platforms.json"))

        val platforms = objectMapper.readValue(json, Array<NsiPlatformDto>::class.java).toList()

        val operationalPlatform = platforms.first { platform -> platform.data?.status == "OPERATIONAL" }
        assertNotNull(operationalPlatform.id)
        assertNotNull(operationalPlatform.businessKey)
        assertNotNull(operationalPlatform.data?.name)
        assertEquals("OPERATIONAL", operationalPlatform.data?.status)
        assertNotNull(operationalPlatform.data?.noradId)
        assertNotNull(operationalPlatform.validFrom)
        assertNotNull(operationalPlatform.validTo)
    }

    @Test
    fun `fetchPlatforms reads root array response`() {
        withServer("""[{"id":"nsi-id","businessKey":"business-key","data":{"name":"Satellite","status":"OPERATIONAL","norad_id":25544,"mission":"MISSION","orbit":{"type":"SSO"}},"validFrom":"2026-01-01T00:00:00Z","validTo":"9999-12-31T23:59:59Z","_meta":{"locale":"en-US"}}]""") { baseUrl ->
            val client = PlatformsClassifierClient(
                ClassifierProperties(
                    platformsUrl = baseUrl,
                    platformsCacheTtl = Duration.ofMinutes(15),
                    allowedPlatformStatuses = setOf("OPERATIONAL", "STANDBY")
                )
            )

            val platforms = client.fetchPlatforms()

            assertEquals(1, platforms.size)
            assertEquals("nsi-id", platforms.single().id)
            assertEquals("business-key", platforms.single().businessKey)
            assertEquals("Satellite", platforms.single().data?.name)
            assertEquals("OPERATIONAL", platforms.single().data?.status)
            assertEquals(25544L, platforms.single().data?.noradId)
            assertEquals("MISSION", platforms.single().data?.mission)
        }
    }

    @Test
    fun `fetchPlatforms reports Ordinis unavailability on empty body`() {
        withServer("") { baseUrl ->
            val client = PlatformsClassifierClient(
                ClassifierProperties(
                    platformsUrl = baseUrl,
                    platformsCacheTtl = Duration.ofMinutes(15),
                    allowedPlatformStatuses = setOf("OPERATIONAL", "STANDBY")
                )
            )

            val exception = assertThrows(OrdinisUnavailableException::class.java) {
                client.fetchPlatforms()
            }

            assertEquals(baseUrl, exception.classifierUrl)
            assertEquals("Classifier response body is empty: $baseUrl", exception.cause?.message)
        }
    }

    @Test
    fun `fetchPlatforms reports Ordinis unavailability on response error`() {
        withServer("""{"error":"down"}""", status = 503) { baseUrl ->
            val client = PlatformsClassifierClient(
                ClassifierProperties(
                    platformsUrl = baseUrl,
                    platformsCacheTtl = Duration.ofMinutes(15),
                    allowedPlatformStatuses = setOf("OPERATIONAL", "STANDBY")
                )
            )

            val exception = assertThrows(OrdinisUnavailableException::class.java) {
                client.fetchPlatforms()
            }

            assertEquals(baseUrl, exception.classifierUrl)
            assertEquals(503, exception.statusCode)
        }
    }

    private fun withServer(responseBody: String, status: Int = 200, block: (String) -> Unit) {
        val server = HttpServer.create(InetSocketAddress(0), 0)
        server.createContext("/") { exchange ->
            val responseBytes = responseBody.toByteArray()
            exchange.responseHeaders.add("Content-Type", "application/json")
            exchange.sendResponseHeaders(status, responseBytes.size.toLong())
            exchange.responseBody.use { body -> body.write(responseBytes) }
        }
        server.start()
        try {
            block("http://localhost:${server.address.port}")
        } finally {
            server.stop(0)
        }
    }
}
