package space.nstart.pcp.slots_service.controller

import org.junit.jupiter.api.Test
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.doThrow
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.test.context.bean.override.mockito.MockitoBean
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.MovementModel
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.PositionDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.StationDTO
import space.nstart.pcp.slots_service.dto.groupstate.GroupStateAvailabilityDTO
import space.nstart.pcp.slots_service.dto.groupstate.GroupStateDetailsDTO
import space.nstart.pcp.slots_service.dto.groupstate.GroupStateMatrixCellDTO
import space.nstart.pcp.slots_service.dto.groupstate.GroupStateMatrixRowDTO
import space.nstart.pcp.slots_service.dto.groupstate.GroupStateSatelliteDTO
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmAvailabilityDTO
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmDetailsDTO
import space.nstart.pcp.slots_service.dto.pdcm.SatellitePdcmSummaryDTO
import space.nstart.pcp.slots_service.dto.tgu.TguPlanResponse
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.SlotCalculationSummaryDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteCreateDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteGroupCreateDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteGroupDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteSlotProfileDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteVisualizationDTO
import space.nstart.pcp.slots_service.service.BallisticsService
import space.nstart.pcp.slots_service.service.GroupStateService
import space.nstart.pcp.slots_service.service.SatelliteCatalogService
import space.nstart.pcp.slots_service.service.SatellitePdcmService
import space.nstart.pcp.slots_service.service.SlotService
import space.nstart.pcp.slots_service.service.StationService
import space.nstart.pcp.slots_service.service.TguPlanningService
import java.time.LocalDateTime
import java.util.UUID
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    properties = [
        "spring.cloud.config.enabled=false",
        "spring.cloud.config.import-check.enabled=false",
        "spring.boot.admin.client.enabled=false"
    ]
)
class CatalogControllerTest {

    @LocalServerPort
    private var port: Int = 0

    @MockitoBean
    private lateinit var satelliteCatalogService: SatelliteCatalogService

    @MockitoBean
    private lateinit var stationService: StationService

    @MockitoBean
    private lateinit var slotService: SlotService

    @MockitoBean
    private lateinit var groupStateService: GroupStateService

    @MockitoBean
    private lateinit var satellitePdcmService: SatellitePdcmService

    @MockitoBean
    private lateinit var tguPlanningService: TguPlanningService

    @MockitoBean
    private lateinit var ballisticsService: BallisticsService

    @Test
    fun `groups page is rendered`() {
        val body = WebClient.create("http://localhost:$port")
            .get()
            .uri("/groups")
            .retrieve()
            .bodyToMono(String::class.java)
            .block()!!

        assertTrue(body.contains("Группировки"))
        assertTrue(body.contains("Новая группировка"))
        assertTrue(body.contains("dropdown-toggle"))
        assertTrue(body.contains(">Управление<"))
        assertTrue(body.contains(">Состояние ОГ<"))
    }

    @Test
    fun `group state page is rendered`() {
        val body = WebClient.create("http://localhost:$port")
            .get()
            .uri("/group-state")
            .retrieve()
            .bodyToMono(String::class.java)
            .block()!!

        assertTrue(body.contains("Состояние ОГ"))
        assertTrue(body.contains("Доступные группировки"))
        assertTrue(body.contains("Показать состояние"))
    }

    @Test
    fun `satellites page is rendered`() {
        val body = WebClient.create("http://localhost:$port")
            .get()
            .uri("/satellites")
            .retrieve()
            .bodyToMono(String::class.java)
            .block()!!

        assertTrue(body.contains("Спутники"))
        assertTrue(body.contains("Observation profile"))
        assertTrue(body.contains("Slot profile"))
        assertTrue(body.contains("Расчет слотов"))
        assertTrue(body.contains("slot-calculation-summary"))
        assertTrue(body.contains("Начальные условия орбиты"))
        assertTrue(body.contains("Сохранить начальные условия"))
        assertTrue(body.contains("<th>Слоты</th>"))
    }

    @Test
    fun `satellite pdcm page is rendered`() {
        val body = WebClient.create("http://localhost:$port")
            .get()
            .uri("/satellites/pdcm")
            .retrieve()
            .bodyToMono(String::class.java)
            .block()!!

        assertTrue(body.contains("Параметры движения центра масс"))
        assertTrue(body.contains("Asc-node"))
        assertTrue(body.contains("Спутники"))
        assertTrue(body.contains("Скачать CSV"))
        assertTrue(body.contains("satellite-pdcm-send-ic-time"))
        assertTrue(body.contains("Задать НУ"))
        assertTrue(body.contains("satellite-pdcm-set-ic-modal"))
        assertTrue(body.contains("value=\"FOTO\" selected"))
        assertTrue(body.contains("dd.mm.yyyy hh:MM:ss.zzz"))
        assertTrue(body.contains("/webjars/bootstrap/5.3.0/css/bootstrap.min.css"))
    }

    @Test
    fun `tle analysis page is rendered with page script`() {
        val body = WebClient.create("http://localhost:$port")
            .get()
            .uri("/tle-analysis")
            .retrieve()
            .bodyToMono(String::class.java)
            .block()!!

        assertTrue(body.contains("Анализ TLE"))
        assertTrue(body.contains("tle-analysis-satellites-body"))
        assertTrue(body.contains("tle-analysis-count"))
        assertTrue(body.contains("Разница Времени, ч"))
        assertTrue(body.contains("value=\"5\""))
        assertTrue(body.contains("/tle_analysis_scripts.js"))
    }

    @Test
    fun `stations page is rendered`() {
        val body = WebClient.create("http://localhost:$port")
            .get()
            .uri("/stations")
            .retrieve()
            .bodyToMono(String::class.java)
            .block()!!

        assertTrue(body.contains("Станции"))
        assertTrue(body.contains("Новая станция"))
        assertTrue(body.contains("Мин.угол"))
        assertTrue(body.contains("Макс.угол"))
        assertTrue(body.contains(">Управление<"))
        assertTrue(body.contains(">ЗРВ<"))
        assertFalse(body.contains("<th>UUID</th>"))
    }

    @Test
    fun `tgu planning page is rendered from menu`() {
        val body = WebClient.create("http://localhost:$port")
            .get()
            .uri("/tgu-planning")
            .retrieve()
            .bodyToMono(String::class.java)
            .block()!!

        assertTrue(body.contains("Планирование ТГУ"))
        assertTrue(body.contains("/tgu_planning_scripts.js"))
        assertTrue(body.contains("href=\"/tgu-planning\""))
    }

    @Test
    fun `tgu spacecraft plans endpoint proxies norad spacecraft id`() {
        val planId = UUID.fromString("6d0941e4-61d6-4f96-8fa8-90ba11db121e")
        val response = listOf(
            TguPlanResponse(
                planId = planId,
                spacecraftId = "56756",
                startTime = LocalDateTime.of(2026, 5, 29, 10, 0, 0),
                endTime = LocalDateTime.of(2026, 5, 29, 10, 15, 0),
                kppId = "KPP-1",
                status = "WAITING_DECISION"
            )
        )
        doReturn(response).`when`(tguPlanningService).spacecraftPlans("56756", 14L)

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/tgu-planning/spacecraft/56756/plans?historyDays=14")
            .retrieve()
            .bodyToFlux(TguPlanResponse::class.java)
            .collectList()
            .block()!!

        assertEquals(planId, actual.single().planId)
        assertEquals("56756", actual.single().spacecraftId)
        verify(tguPlanningService).spacecraftPlans("56756", 14L)
    }

    @Test
    fun `group create endpoint proxies request`() {
        val response = SatelliteGroupDTO(
            id = 71L,
            name = "KONDOR",
            satelliteIds = listOf(56756L, 62138L)
        )
        val request = SatelliteGroupCreateDTO(
            name = "KONDOR",
            satelliteIds = listOf(56756L, 62138L)
        )
        doReturn(response).`when`(satelliteCatalogService).createGroup(request)

        val actual = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/catalog/groups")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(request)
            .retrieve()
            .bodyToMono(SatelliteGroupDTO::class.java)
            .block()!!

        assertEquals(71L, actual.id)
        assertEquals("KONDOR", actual.name)
        verify(satelliteCatalogService).createGroup(request)
    }

    @Test
    fun `group state endpoint proxies request`() {
        val requestedTime = LocalDateTime.of(2026, 4, 24, 12, 15)
        val response = GroupStateDetailsDTO(
            groupId = 71L,
            groupName = "KONDOR",
            availableInterval = GroupStateAvailabilityDTO(
                timeStart = LocalDateTime.of(2026, 4, 24, 10, 0),
                timeStop = LocalDateTime.of(2026, 4, 24, 14, 0)
            ),
            calculationTime = requestedTime,
            satellites = listOf(
                GroupStateSatelliteDTO(
                    satelliteId = 501L,
                    noradId = 56756L,
                    code = "SAT-501",
                    name = "Sat 501",
                    omegabDeg = 15.0,
                    uDeg = 45.0
                )
            ),
            matrix = listOf(
                GroupStateMatrixRowDTO(
                    satelliteId = 501L,
                    cells = listOf(
                        GroupStateMatrixCellDTO(
                            rowSatelliteId = 501L,
                            columnSatelliteId = 501L,
                            deltaOmegabDeg = 0.0,
                            deltaUDeg = 0.0
                        )
                    )
                )
            )
        )
        doReturn(response).`when`(groupStateService).groupState(71L, requestedTime)

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/catalog/group-states/71?time=2026-04-24T12:15:00")
            .retrieve()
            .bodyToMono(GroupStateDetailsDTO::class.java)
            .block()!!

        assertEquals(71L, actual.groupId)
        assertEquals(requestedTime, actual.calculationTime)
        assertEquals(1, actual.satellites.size)
        verify(groupStateService).groupState(71L, requestedTime)
    }

    @Test
    fun `satellite pdcm endpoint proxies request`() {
        val response = SatellitePdcmDetailsDTO(
            satelliteId = 501L,
            code = "SAT-501",
            name = "Sat 501",
            activeInterval = true,
            availableInterval = SatellitePdcmAvailabilityDTO(
                timeStart = LocalDateTime.of(2026, 4, 24, 10, 0),
                timeStop = LocalDateTime.of(2026, 4, 24, 12, 0)
            ),
            ascNodes = emptyList()
        )
        doReturn(response).`when`(satellitePdcmService).satelliteDetails(501L)

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/catalog/satellites/pdcm/501")
            .retrieve()
            .bodyToMono(SatellitePdcmDetailsDTO::class.java)
            .block()!!

        assertEquals(501L, actual.satelliteId)
        assertTrue(actual.activeInterval)
        verify(satellitePdcmService).satelliteDetails(501L)
    }

    @Test
    fun `satellite pdcm list endpoint proxies request`() {
        val response = listOf(
            SatellitePdcmSummaryDTO(
                satelliteId = 501L,
                code = "SAT-501",
                name = "Sat 501",
                activeInterval = true,
                availableInterval = SatellitePdcmAvailabilityDTO(
                    timeStart = LocalDateTime.of(2026, 4, 24, 10, 0),
                    timeStop = LocalDateTime.of(2026, 4, 24, 12, 0)
                )
            )
        )
        doReturn(response).`when`(satellitePdcmService).satelliteSummaries()

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/catalog/satellites/pdcm")
            .retrieve()
            .bodyToMono(Array<SatellitePdcmSummaryDTO>::class.java)
            .block()!!

        assertEquals(1, actual.size)
        assertEquals(501L, actual[0].satelliteId)
        verify(satellitePdcmService).satelliteSummaries()
    }

    @Test
    fun `satellite pdcm send initial conditions endpoint parses ui time and proxies request`() {
        val requestedTime = LocalDateTime.of(2026, 4, 24, 10, 15, 30, 123_000_000)

        val status = WebClient.create("http://localhost:$port")
            .post()
            .uri { builder ->
                builder
                    .path("/api/catalog/satellites/pdcm/501/send-ic")
                    .queryParam("time", "24.04.2026 10:15:30.123")
                    .build()
            }
            .exchangeToMono { response -> Mono.just(response.statusCode()) }
            .block()!!

        assertEquals(HttpStatus.ACCEPTED, status)
        verify(slotService).sendSatelliteInitialConditions(501L, requestedTime)
    }

    @Test
    fun `satellite pdcm send initial conditions endpoint rejects invalid ui time`() {
        val status = WebClient.create("http://localhost:$port")
            .post()
            .uri { builder ->
                builder
                    .path("/api/catalog/satellites/pdcm/501/send-ic")
                    .queryParam("time", "2026-04-24T10:15:30")
                    .build()
            }
            .exchangeToMono { response -> Mono.just(response.statusCode()) }
            .block()!!

        assertEquals(HttpStatus.BAD_REQUEST, status)
        verify(slotService, never()).sendSatelliteInitialConditions(eq(501L), anyLocalDateTime())
    }

    @Test
    fun `satellite pdcm set initial conditions endpoint proxies request to ballistics`() {
        val request = SatelliteICDTO(
            satelliteId = 999L,
            movementModel = MovementModel.KONDOR,
            ic = InitialConditionsDTO(
                orbPoint = OrbPointDTO(
                    time = LocalDateTime.of(2026, 4, 24, 10, 15, 30, 123_000_000),
                    revolution = 42L,
                    vx = 1.0,
                    vy = 2.0,
                    vz = 3.0,
                    x = 4.0,
                    y = 5.0,
                    z = 6.0
                ),
                sBall = 0.07,
                f81 = 145.2
            )
        )

        val status = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/catalog/satellites/pdcm/501/initial-conditions")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(request)
            .exchangeToMono { response -> Mono.just(response.statusCode()) }
            .block()!!

        val requestCaptor = ArgumentCaptor.forClass(SatelliteICDTO::class.java)
        assertEquals(HttpStatus.ACCEPTED, status)
        verify(ballisticsService).receiveRv(captureSatelliteIc(requestCaptor))
        assertEquals(501L, requestCaptor.value.satelliteId)
        assertEquals(MovementModel.KONDOR, requestCaptor.value.movementModel)
        assertEquals(42L, requestCaptor.value.ic.orbPoint.revolution)
    }

    @Test
    fun `satellite pdcm current initial conditions endpoint proxies request to ballistics`() {
        val response = SatelliteICDTO(
            satelliteId = 501L,
            movementModel = MovementModel.KONDOR,
            ic = InitialConditionsDTO(
                orbPoint = OrbPointDTO(
                    time = LocalDateTime.of(2026, 4, 24, 10, 15, 30, 123_000_000),
                    revolution = 42L,
                    vx = 1.0,
                    vy = 2.0,
                    vz = 3.0,
                    x = 4.0,
                    y = 5.0,
                    z = 6.0
                ),
                sBall = 0.07,
                f81 = 145.2
            )
        )
        doReturn(response).`when`(ballisticsService).currentInitialConditions(501L)

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/catalog/satellites/pdcm/501/initial-conditions")
            .retrieve()
            .bodyToMono(SatelliteICDTO::class.java)
            .block()!!

        assertEquals(501L, actual.satelliteId)
        assertEquals(MovementModel.KONDOR, actual.movementModel)
        assertEquals(42L, actual.ic.orbPoint.revolution)
        verify(ballisticsService).currentInitialConditions(501L)
    }

    @Test
    fun `satellite create endpoint proxies request`() {
        val request = SatelliteCreateDTO(
            id = 501L,
            noradId = 9501L,
            code = "SAT-501",
            name = "Sat 501",
            typeCode = "TEST",
            active = true,
            scanTle = true,
            visualization = SatelliteVisualizationDTO(red = 11, green = 22, blue = 33)
        )
        val response = SatelliteDTO(
            id = 501L,
            noradId = 9501L,
            code = "SAT-501",
            name = "Sat 501",
            typeCode = "TEST",
            active = true,
            scanTle = true,
            visualization = SatelliteVisualizationDTO(red = 11, green = 22, blue = 33)
        )
        doReturn(response).`when`(satelliteCatalogService).createSatellite(request)

        val actual = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/catalog/satellites")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(request)
            .retrieve()
            .bodyToMono(SatelliteDTO::class.java)
            .block()!!

        assertEquals(501L, actual.id)
        assertEquals("SAT-501", actual.code)
        verify(satelliteCatalogService).createSatellite(request)
    }

    @Test
    fun `station save endpoint proxies request`() {
        val stationId = UUID.fromString("2f1d1d8b-588d-4d4e-a456-80a5c93e09dd")
        val request = StationDTO(
            id = stationId,
            number = 7,
            name = "Station 7",
            position = PositionDTO(
                lat = 55.75,
                long = 37.62,
                height = 180.0
            ),
            elevationMin = 5.0,
            elevationMax = 88.0
        )
        doReturn(request).`when`(stationService).saveStation(anyStation())

        val actual = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/stations")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(request)
            .retrieve()
            .bodyToMono(StationDTO::class.java)
            .block()!!

        assertEquals(stationId, actual.id)
        assertEquals("Station 7", actual.name)
        verify(stationService).saveStation(anyStation())
    }

    @Test
    fun `satellite initial conditions get endpoint returns no content when absent`() {
        doReturn(null).`when`(slotService).satelliteInitialConditions(501L)

        val status = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/catalog/satellites/501/initial-conditions")
            .exchangeToMono { Mono.just(it.statusCode()) }
            .block()!!

        assertEquals(HttpStatus.NO_CONTENT, status)
        verify(slotService).satelliteInitialConditions(501L)
    }

    @Test
    fun `satellite slot calculation summaries endpoint proxies request`() {
        val response = listOf(
            SlotCalculationSummaryDTO.calculated(
                satelliteId = 501L,
                slotCount = 10L,
                minTime = LocalDateTime.of(2026, 4, 20, 0, 0),
                maxTime = LocalDateTime.of(2026, 4, 21, 0, 0),
                minRevolution = 100L,
                maxRevolution = 112L
            )
        )
        doReturn(response).`when`(slotService).slotCalculationSummaries(listOf(501L, 502L))

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/catalog/satellites/slot-calculation-summaries?satelliteIds=501&satelliteIds=502")
            .retrieve()
            .bodyToMono(Array<SlotCalculationSummaryDTO>::class.java)
            .block()!!

        assertEquals(1, actual.size)
        assertEquals(501L, actual[0].satelliteId)
        assertTrue(actual[0].calculated)
        assertEquals(12L, actual[0].revolutionInterval)
        verify(slotService).slotCalculationSummaries(listOf(501L, 502L))
    }

    @Test
    fun `satellite slot calculation summaries endpoint returns empty summaries when slot service fails`() {
        doThrow(RuntimeException("slot service unavailable"))
            .`when`(slotService).slotCalculationSummaries(listOf(501L, 502L))

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/catalog/satellites/slot-calculation-summaries?satelliteIds=501&satelliteIds=502")
            .retrieve()
            .bodyToMono(Array<SlotCalculationSummaryDTO>::class.java)
            .block()!!

        assertEquals(2, actual.size)
        assertEquals(501L, actual[0].satelliteId)
        assertFalse(actual[0].calculated)
        assertEquals(502L, actual[1].satelliteId)
        assertFalse(actual[1].calculated)
        verify(slotService).slotCalculationSummaries(listOf(501L, 502L))
    }

    @Test
    fun `satellite slot calculation summary endpoint proxies request`() {
        val response = SlotCalculationSummaryDTO.calculated(
            satelliteId = 501L,
            slotCount = 10L,
            minTime = LocalDateTime.of(2026, 4, 20, 0, 0),
            maxTime = LocalDateTime.of(2026, 4, 21, 0, 0),
            minRevolution = 100L,
            maxRevolution = 112L
        )
        doReturn(response).`when`(slotService).slotCalculationSummary(501L)

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/catalog/satellites/501/slot-calculation-summary")
            .retrieve()
            .bodyToMono(SlotCalculationSummaryDTO::class.java)
            .block()!!

        assertEquals(501L, actual.satelliteId)
        assertTrue(actual.calculated)
        assertEquals(12L, actual.revolutionInterval)
        verify(slotService).slotCalculationSummary(501L)
    }

    @Test
    fun `satellite slot calculation summary endpoint returns empty summary when slot service fails`() {
        doThrow(RuntimeException("slot service unavailable"))
            .`when`(slotService).slotCalculationSummary(501L)

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/catalog/satellites/501/slot-calculation-summary")
            .retrieve()
            .bodyToMono(SlotCalculationSummaryDTO::class.java)
            .block()!!

        assertEquals(501L, actual.satelliteId)
        assertFalse(actual.calculated)
        assertEquals(0L, actual.slotCount)
        verify(slotService).slotCalculationSummary(501L)
    }

    @Test
    fun `satellite initial conditions save endpoint proxies request`() {
        val request = InitialConditionsDTO(
            orbPoint = OrbPointDTO(
                time = LocalDateTime.of(2026, 4, 20, 10, 15),
                revolution = 123L,
                vx = 1.1,
                vy = 2.2,
                vz = 3.3,
                x = 4.4,
                y = 5.5,
                z = 6.6
            ),
            sBall = 0.07,
            f81 = 145.2
        )
        val response = SatelliteICDTO(
            satelliteId = 501L,
            ic = request
        )
        doReturn(response).`when`(slotService).saveSatelliteInitialConditions(eq(501L), anyInitialConditions(), eq(true))

        val actual = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/catalog/satellites/501/initial-conditions?create=true")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(request)
            .retrieve()
            .bodyToMono(SatelliteICDTO::class.java)
            .block()!!

        assertEquals(501L, actual.satelliteId)
        assertEquals(123L, actual.ic.orbPoint.revolution)
        verify(slotService).saveSatelliteInitialConditions(eq(501L), anyInitialConditions(), eq(true))
    }

    @Test
    fun `satellite slot calculation endpoint proxies request`() {
        val request = SatelliteSlotProfileDTO(
            cycleRevs = 243L,
            slotDuration = 10L,
            durationCalcDays = 16L,
            maxChainLength = 2
        )

        val status = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/catalog/satellites/501/slot-calculation")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(request)
            .exchangeToMono { Mono.just(it.statusCode()) }
            .block()!!

        assertEquals(HttpStatus.NO_CONTENT, status)
        verify(slotService).startSlotCalculation(eq(501L), anySlotProfile())
    }

    private fun anyStation(): StationDTO = any(StationDTO::class.java) ?: StationDTO()
    private fun anyInitialConditions(): InitialConditionsDTO = any(InitialConditionsDTO::class.java) ?: InitialConditionsDTO()
    private fun anySlotProfile(): SatelliteSlotProfileDTO = any(SatelliteSlotProfileDTO::class.java) ?: SatelliteSlotProfileDTO()
    private fun anyLocalDateTime(): LocalDateTime = any(LocalDateTime::class.java) ?: LocalDateTime.MIN
    private fun captureSatelliteIc(captor: ArgumentCaptor<SatelliteICDTO>): SatelliteICDTO =
        captor.capture() ?: SatelliteICDTO()
}
