package space.nstart.pcp.pcp_satellites_service.controller

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoInteractions
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.system.CapturedOutput
import org.springframework.boot.test.system.OutputCaptureExtension
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.test.context.bean.override.mockito.MockitoBean
import org.springframework.web.reactive.function.client.WebClient
import space.nstart.pcp.pcp_satellites_service.dto.ComplexPlanRunResponseDTO
import space.nstart.pcp.pcp_satellites_service.entity.ComplexPlanRunStatus
import space.nstart.pcp.pcp_satellites_service.service.ComplexMissionService
import space.nstart.pcp.pcp_satellites_service.service.ComplexPlanRunOrchestrationService
import space.nstart.pcp.pcp_types_lib.dto.requests.ComplexPlanProcessRequestDTO
import java.time.LocalDateTime
import kotlin.test.assertContains
import kotlin.test.assertEquals
import kotlin.test.assertTrue

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(OutputCaptureExtension::class)
class ComplexPlanControllerTest {

    @LocalServerPort
    private var port: Int = 0

    @MockitoBean
    private lateinit var complexMissionService: ComplexMissionService

    @MockitoBean
    private lateinit var complexPlanRunOrchestrationService: ComplexPlanRunOrchestrationService

    @Test
    fun `process endpoint uses POST and logs response body as json`(output: CapturedOutput) {
        val request = ComplexPlanProcessRequestDTO(
            intervalStart = LocalDateTime.of(2026, 3, 26, 10, 0),
            intervalEnd = LocalDateTime.of(2026, 3, 26, 12, 0),
            satelliteIds = listOf(22L)
        )
        doReturn(
            ComplexPlanRunResponseDTO(
                runId = 1L,
                status = ComplexPlanRunStatus.COMPLETED,
                intervalStart = LocalDateTime.of(2026, 3, 26, 10, 0),
                intervalEnd = LocalDateTime.of(2026, 3, 26, 12, 0),
                satelliteIds = listOf(22L),
                requestedBy = "tester",
                createdAt = LocalDateTime.of(2026, 3, 26, 9, 59),
                startedAt = LocalDateTime.of(2026, 3, 26, 10, 0),
                finishedAt = LocalDateTime.of(2026, 3, 26, 10, 1),
                durationMs = 60000,
                errorMessage = null,
                calculatedModesCount = 1,
                bookedModesCount = 1,
                complanModesCount = 0,
                mixedModesCount = 0,
                affectedSatellitesCount = 1,
                processedCellsCount = 1,
                bookedSlotLinksCount = 1,
                snapshotIds = listOf(1001L),
                requestPayload = "{}"
            )
        ).`when`(complexPlanRunOrchestrationService).process(request, "tester")

        WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/com-plan/process")
            .header("X-Requested-By", "tester")
            .bodyValue(
                mapOf(
                    "intervalStart" to "2026-03-26T10:00:00",
                    "intervalEnd" to "2026-03-26T12:00:00",
                    "satelliteIds" to listOf(22)
                )
            )
            .retrieve()
            .bodyToMono(String::class.java)
            .block()

        verify(complexPlanRunOrchestrationService).process(
            request,
            "tester"
        )
        assertContains(output.out, "Complex plan process completed: runId=1, status=COMPLETED")
        assertContains(output.out, "responseBody={")
        assertContains(output.out, """"runId":1""")
        assertContains(output.out, """"status":"COMPLETED"""")
    }

    @Test
    fun `process endpoint rejects sun outside allowed range`() {
        val response = WebClient.create("http://localhost:$port")
            .post()
            .uri("/api/com-plan/process")
            .bodyValue(
                mapOf(
                    "intervalStart" to "2026-03-26T10:00:00",
                    "intervalEnd" to "2026-03-26T12:00:00",
                    "satelliteIds" to listOf(22),
                    "sun" to 90.1
                )
            )
            .exchangeToMono { clientResponse ->
                clientResponse.bodyToMono(String::class.java)
                    .defaultIfEmpty("")
                    .map { body -> clientResponse.statusCode().value() to body }
            }
            .block()!!

        assertEquals(400, response.first)
        assertTrue(response.second.contains("sun"))
        verifyNoInteractions(complexPlanRunOrchestrationService)
    }
}
