package space.nstart.pcp.slots_service.controller

import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import space.nstart.pcp.pcp_types_lib.dto.ballistics.InitialConditionsDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteICDTO
import space.nstart.pcp.slots_service.configuration.GlobalExceptionHandler
import space.nstart.pcp.slots_service.service.SatelliteIcService
import java.time.LocalDateTime

class SatelliteIcControllerTest {

    private val satelliteIcService = mock(SatelliteIcService::class.java)
    private val controller = SatelliteIcController(satelliteIcService)
    private val mockMvc: MockMvc = MockMvcBuilders.standaloneSetup(controller)
        .setControllerAdvice(GlobalExceptionHandler())
        .build()

    @Test
    fun `get satellite ic delegates to service`() {
        `when`(satelliteIcService.bySatelliteId(22L)).thenReturn(sampleIc(22L))

        mockMvc.perform(get("/api/satellite-ic/22"))
            .andExpect(status().isOk)
            .andExpect(jsonPath("$.satelliteId").value(22L))
            .andExpect(jsonPath("$.ic.orbPoint.revolution").value(42))

        verify(satelliteIcService).bySatelliteId(22L)
    }

    @Test
    fun `create satellite ic delegates to service`() {
        `when`(satelliteIcService.create(anySatelliteIc()))
            .thenReturn(sampleIc(22L))

        mockMvc.perform(
            post("/api/satellite-ic")
                .contentType(MediaType.APPLICATION_JSON)
                .content(
                    """
                    {
                      "satelliteId": 22,
                      "ic": {
                        "orbPoint": {
                          "time": "2026-03-20T07:20:00",
                          "revolution": 42,
                          "vx": 1.0,
                          "vy": 2.0,
                          "vz": 3.0,
                          "x": 4.0,
                          "y": 5.0,
                          "z": 6.0
                        },
                        "sBall": 0.1,
                        "f81": 125.0
                      }
                    }
                    """.trimIndent()
                )
        )
            .andExpect(status().isOk)
            .andExpect(jsonPath("$.satelliteId").value(22L))

        verify(satelliteIcService).create(anySatelliteIc())
    }

    @Test
    fun `update satellite ic delegates to service`() {
        `when`(satelliteIcService.update(eqValue(22L), anyInitialConditions()))
            .thenReturn(sampleIc(22L))

        mockMvc.perform(
            put("/api/satellite-ic/22")
                .contentType(MediaType.APPLICATION_JSON)
                .content(
                    """
                    {
                      "orbPoint": {
                        "time": "2026-03-20T07:20:00",
                        "revolution": 42,
                        "vx": 1.0,
                        "vy": 2.0,
                        "vz": 3.0,
                        "x": 4.0,
                        "y": 5.0,
                        "z": 6.0
                      },
                      "sBall": 0.1,
                      "f81": 125.0
                    }
                    """.trimIndent()
                )
        )
            .andExpect(status().isOk)
            .andExpect(jsonPath("$.satelliteId").value(22L))

        verify(satelliteIcService).update(eqValue(22L), anyInitialConditions())
    }

    @Test
    fun `delete satellite ic delegates to service`() {
        mockMvc.perform(delete("/api/satellite-ic/22"))
            .andExpect(status().isNoContent)

        verify(satelliteIcService).delete(22L)
    }

    private fun sampleIc(satelliteId: Long) = SatelliteICDTO(
        satelliteId = satelliteId,
        ic = InitialConditionsDTO(
            orbPoint = OrbPointDTO(
                time = LocalDateTime.of(2026, 3, 20, 7, 20),
                revolution = 42,
                vx = 1.0,
                vy = 2.0,
                vz = 3.0,
                x = 4.0,
                y = 5.0,
                z = 6.0
            ),
            sBall = 0.1,
            f81 = 125.0
        )
    )

    private fun anySatelliteIc(): SatelliteICDTO =
        any(SatelliteICDTO::class.java) ?: SatelliteICDTO()

    private fun anyInitialConditions(): InitialConditionsDTO =
        any(InitialConditionsDTO::class.java) ?: InitialConditionsDTO()

    private fun eqValue(value: Long): Long = eq(value) ?: value
}
