package space.nstart.pcp_tgu_service.integration.api

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.sun.net.httpserver.HttpServer
import io.micrometer.core.instrument.simple.SimpleMeterRegistry
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mock
import space.nstart.pcp_tgu_service.config.ClassifierProperties
import space.nstart.pcp_tgu_service.config.ExternalApiProperties
import space.nstart.pcp_tgu_service.config.PlanningProperties
import space.nstart.pcp_tgu_service.domain.PlatformCatalogItem
import space.nstart.pcp_tgu_service.service.OrdinisAvailabilityMetrics
import space.nstart.pcp_tgu_service.service.PlatformService
import java.net.InetSocketAddress
import java.time.Instant
import java.util.Collections

class ExternalPointsClientTest {

    @Test
    fun `fetchAllSpacecraftPoints uses allowed platforms and keeps noradId as spacecraftId`() {
        val requestedPaths = Collections.synchronizedList(mutableListOf<String>())

        withServer(requestedPaths) { baseUrl ->
            val client = ExternalPointsClient(
                externalApiProperties = ExternalApiProperties(baseUrl = baseUrl),
                platformService = FixedPlatformService(
                    listOf(
                        platform(nsiId = "allowed-1", noradId = 25544L),
                        platform(nsiId = "allowed-2", noradId = 41386L)
                    )
                ),
                planningProperties = planningProperties(),
                visibilityPayloadParser = VisibilityPayloadParser(jacksonObjectMapper().findAndRegisterModules())
            )

            val points = client.fetchAllSpacecraftPoints()

            assertEquals(1, points.size)
            assertEquals("25544", points.single().spacecraftId)
            assertEquals(listOf("7"), points.single().points.map { it.pointId })
            assertEquals(1, points.single().points.single().observationWindows.size)
            assertEquals(
                listOf(
                    "/api/satellites/25544/rva",
                    "/api/satellites/41386/rva"
                ),
                requestedPaths.map { path -> path.substringBefore("?") }
            )
        }
    }

    private class FixedPlatformService(
        private val allowedPlatforms: List<PlatformCatalogItem>
    ) : PlatformService(
        mock(PlatformsClassifierClient::class.java),
        ClassifierProperties(
            platformsUrl = "http://localhost",
            platformsCacheTtl = java.time.Duration.ofMinutes(15),
            allowedPlatformStatuses = setOf("OPERATIONAL", "STANDBY")
        ),
        jacksonObjectMapper().findAndRegisterModules(),
        OrdinisAvailabilityMetrics(SimpleMeterRegistry())
    ) {
        override fun loadPlatforms(status: String?): List<PlatformCatalogItem> =
            error("ExternalPointsClient must use loadAllowedPlatforms")

        override fun loadAllowedPlatforms(now: Instant): List<PlatformCatalogItem> = allowedPlatforms
    }

    private fun withServer(requestedPaths: MutableList<String>, block: (String) -> Unit) {
        val server = HttpServer.create(InetSocketAddress(0), 0)
        server.createContext("/") { exchange ->
            requestedPaths.add(exchange.requestURI.toString())
            val responseBody = when (exchange.requestURI.path) {
                "/api/satellites/25544/rva" -> """[{"noradId":25544,"stationId":7,"revolution":1,"onStart":{"time":"2026-05-26T10:00:00Z","elevation":1.0},"onMaximum":{"time":"2026-05-26T10:05:00Z","elevation":45.0},"onStop":{"time":"2026-05-26T10:10:00Z","elevation":1.0}}]"""
                else -> """{"error":"failed"}"""
            }
            val status = if (exchange.requestURI.path == "/api/satellites/25544/rva") 200 else 500
            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)
        }
    }

    private fun platform(nsiId: String, noradId: Long): PlatformCatalogItem =
        PlatformCatalogItem(
            nsiId = nsiId,
            businessKey = "business-$nsiId",
            name = "Satellite $nsiId",
            status = "OPERATIONAL",
            noradId = noradId,
            mission = "MISSION",
            validFrom = Instant.parse("2026-01-01T00:00:00Z"),
            validTo = Instant.parse("9999-12-31T23:59:59Z")
        )

    private fun planningProperties(): PlanningProperties =
        PlanningProperties(
            notificationBeforeStartMinutes = 30,
            targetPlanDurationMinutes = 24 * 60L,
            targetPlanDurationDeltaMinutes = 60L,
            externalUpdateEnabled = false,
            externalPointsLookaheadDays = 3,
            inMemoryHistoryDays = 14,
            externalUpdateFixedDelayMs = 0,
            notificationCheckFixedDelayMs = 30_000,
            minReissueLeadMinutes = 5,
            reissuePrepMinutes = 30,
            workerLockTtlSeconds = 300,
            waitingDecisionGraceMinutes = 30,
            layinFreezeMinutes = 10
        )
}
