package space.nstart.pcp.slots_service.controller

import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers.anyList
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.verify
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.server.LocalServerPort
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.Flux
import space.nstart.pcp.pcp_types_lib.dto.ballistics.DurationOnRevDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.IntervalDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.PositionDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.RadioVisibilityAreaDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.StationDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TargetPositionDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteInfoDTO
import space.nstart.pcp.slots_service.service.ComplexMissionService
import space.nstart.pcp.slots_service.service.SlotService
import space.nstart.pcp.slots_service.service.StationService
import java.time.LocalDateTime
import kotlin.test.assertEquals
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 RvaControllerTest {

    @LocalServerPort
    private var port: Int = 0

    @MockitoBean
    private lateinit var complexMissionService: ComplexMissionService

    @MockitoBean
    private lateinit var stationService: StationService

    @MockitoBean
    private lateinit var slotService: SlotService

    @Test
    fun `rva page is rendered`() {
        doReturn(Flux.just(SatelliteInfoDTO(noradId = 56756L, name = "Kondor"))).`when`(complexMissionService).satellites()
        doReturn(Flux.just(sampleStation())).`when`(stationService).stations()

        val body = WebClient.create("http://localhost:$port")
            .get()
            .uri("/rva")
            .retrieve()
            .bodyToMono(String::class.java)
            .block()!!

        assertTrue(body.contains("ЗРВ"))
        assertTrue(body.contains("Рассчитать"))
        assertTrue(body.contains("Скачать CSV"))
        assertTrue(body.contains("Суммарная длительность на витке"))
        assertTrue(body.contains("rva-station-label"))
    }

    @Test
    fun `rva common endpoint proxies request`() {
        val station = sampleStation()
        val response = listOf(
            RadioVisibilityAreaDTO(
                stationId = 1L,
                revolution = 42L,
                onStart = TargetPositionDTO(time = LocalDateTime.of(2026, 4, 17, 12, 0)),
                onMaximum = TargetPositionDTO(time = LocalDateTime.of(2026, 4, 17, 12, 3)),
                onStop = TargetPositionDTO(time = LocalDateTime.of(2026, 4, 17, 12, 5)),
                duration = 300.0
            )
        )
        doReturn(response).`when`(slotService).rvaCommon(eq(56756L), eq(2L), anyStationList())

        val actual = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/rva/common/56756/2")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(listOf(station))
            .retrieve()
            .bodyToFlux(RadioVisibilityAreaDTO::class.java)
            .collectList()
            .block()!!

        assertEquals(1, actual.size)
        assertEquals(42L, actual.first().revolution)
        verify(slotService).rvaCommon(eq(56756L), eq(2L), anyStationList())
    }

    @Test
    fun `rva merge endpoint proxies request`() {
        val station = sampleStation()
        val response = listOf(
            IntervalDTO(
                begin = LocalDateTime.of(2026, 4, 17, 12, 0),
                end = LocalDateTime.of(2026, 4, 17, 12, 10),
                durationSec = 600.0
            )
        )
        doReturn(response).`when`(slotService).rvaMerge(eq(56756L), eq(2L), anyStationList())

        val actual = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/rva/merge/56756/2")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(listOf(station))
            .retrieve()
            .bodyToFlux(IntervalDTO::class.java)
            .collectList()
            .block()!!

        assertEquals(1, actual.size)
        assertEquals(600.0, actual.first().durationSec)
        verify(slotService).rvaMerge(eq(56756L), eq(2L), anyStationList())
    }

    @Test
    fun `rva merge on rev endpoint proxies request`() {
        val station = sampleStation()
        val response = listOf(
            DurationOnRevDTO(
                revolution = 42L,
                durationSec = 780.0
            )
        )
        doReturn(response).`when`(slotService).rvaMergeOnRev(eq(56756L), eq(2L), anyStationList())

        val actual = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/rva/merge-on-rev/56756/2")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(listOf(station))
            .retrieve()
            .bodyToFlux(DurationOnRevDTO::class.java)
            .collectList()
            .block()!!

        assertEquals(1, actual.size)
        assertEquals(42L, actual.first().revolution)
        assertEquals(780.0, actual.first().durationSec)
        verify(slotService).rvaMergeOnRev(eq(56756L), eq(2L), anyStationList())
    }

    private fun sampleStation() = StationDTO(
        number = 1,
        name = "Station 1",
        position = PositionDTO(
            lat = 55.75,
            long = 37.62,
            height = 200.0
        ),
        elevationMin = 5.0,
        elevationMax = 90.0
    )

    private fun anyStationList(): List<StationDTO> = anyList<StationDTO>() ?: emptyList()
}
