package space.nstart.pcp.pcp_stations_service.controller

import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers.any
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 space.nstart.pcp.pcp_stations_service.service.StationService
import space.nstart.pcp.pcp_types_lib.dto.ballistics.PositionDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.StationDTO
import java.util.UUID
import kotlin.test.assertEquals

@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 StationControllerTest {

    @LocalServerPort
    private var port: Int = 0

    @MockitoBean
    private lateinit var stationService: StationService

    @Test
    fun `station save endpoint returns dto with position`() {
        val station = sampleStation()
        doReturn(station).`when`(stationService).add(anyStation())

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

        assertEquals(station.id, actual.id)
        assertEquals(station.position.lat, actual.position.lat)
        assertEquals(station.position.long, actual.position.long)
        verify(stationService).add(anyStation())
    }

    @Test
    fun `get all stations`() {
        val stations = stationService.getStations();
        assertEquals(8, stations.size)
    }

    @Test
    fun `station get by id endpoint returns dto without optional wrapper`() {
        val station = sampleStation()
        val stationId = station.id!!
        doReturn(station).`when`(stationService).byId(stationId)

        val actual = WebClient.create("http://localhost:$port")
            .get()
            .uri("/api/stations/$stationId")
            .retrieve()
            .bodyToMono(StationDTO::class.java)
            .block()!!

        assertEquals(stationId, actual.id)
        assertEquals(station.name, actual.name)
        verify(stationService).byId(stationId)
    }

    @Test
    fun `station delete endpoint returns deleted rows count`() {
        val stationId = UUID.fromString("3ad079f2-625f-47bc-a2b8-4e0c5f7d5c16")
        doReturn(1).`when`(stationService).delete(stationId)

        val actual = WebClient.create("http://localhost:$port")
            .delete()
            .uri("/api/stations/$stationId")
            .retrieve()
            .bodyToMono(Int::class.java)
            .block()!!

        assertEquals(1, actual)
        verify(stationService).delete(stationId)
    }

    private fun sampleStation() = StationDTO(
        id = UUID.fromString("2f1d1d8b-588d-4d4e-a456-80a5c93e09dd"),
        number = 7,
        name = "Station 7",
        position = PositionDTO(
            lat = 55.75,
            long = 37.62,
            height = 180.0
        ),
        elevationMin = 5.0,
        elevationMax = 88.0
    )

    private fun anyStation(): StationDTO = any(StationDTO::class.java) ?: StationDTO()
}
