package space.nstart.pcp.slots_service.service

import org.junit.jupiter.api.Test
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import reactor.core.publisher.Flux
import space.nstart.pcp.pcp_types_lib.dto.ballistics.AscNodeDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.KeplersDTO
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteOrbitAvailabilityDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteSummaryDTO
import java.time.LocalDateTime
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class SatellitePdcmServiceTest {

    private val satelliteCatalogService: SatelliteCatalogService = mock()
    private val ballisticsService: BallisticsService = mock()
    private val service = SatellitePdcmService(satelliteCatalogService, ballisticsService)

    @Test
    fun `satellite summaries mark active interval by current time`() {
        val referenceTime = LocalDateTime.of(2026, 4, 24, 11, 0)
        doReturn(satelliteSummaries()).`when`(satelliteCatalogService).allSatellites()
        doReturn(orbitAvailability()).`when`(ballisticsService).orbitAvailability()

        val summaries = service.satelliteSummaries(referenceTime)

        assertEquals(2, summaries.size)
        assertEquals(501L, summaries.first().satelliteId)
        assertTrue(summaries.first().activeInterval)
        assertFalse(summaries.last().activeInterval)
    }

    @Test
    fun `satellite details request asc-node by satellite id and not norad id`() {
        val referenceTime = LocalDateTime.of(2026, 4, 24, 11, 0)
        val availability = orbitAvailability().first()
        doReturn(satelliteSummaries()).`when`(satelliteCatalogService).allSatellites()
        doReturn(orbitAvailability()).`when`(ballisticsService).orbitAvailability()
        doReturn(
            Flux.just(
                AscNodeDTO(
                    time = LocalDateTime.of(2026, 4, 24, 10, 30),
                    revolution = 120,
                    long = 37.62,
                    height = 512.5,
                    x = 1.0,
                    y = 2.0,
                    z = 3.0,
                    vx = 4.0,
                    vy = 5.0,
                    vz = 6.0,
                    keps = KeplersDTO(
                        ael = 7000.0,
                        e = 0.001,
                        inkl = 97.6,
                        omega = 123.4,
                        w = 45.6
                    ),
                    localMeanTime = LocalDateTime.of(2026, 4, 24, 13, 0)
                )
            )
        ).`when`(ballisticsService).ascNodes(501L, availability.timeStart, availability.timeStop)

        val details = service.satelliteDetails(501L, referenceTime)

        assertEquals(501L, details.satelliteId)
        assertTrue(details.activeInterval)
        assertEquals(1, details.ascNodes.size)
        assertEquals(7000.0, details.ascNodes.first().keps?.ael)
        assertEquals(LocalDateTime.of(2026, 4, 24, 13, 0), details.ascNodes.first().localMeanTime)
        verify(ballisticsService).ascNodes(501L, availability.timeStart, availability.timeStop)
    }

    private fun satelliteSummaries() = listOf(
        SatelliteSummaryDTO(id = 501L, noradId = 95678L, code = "SAT-501", name = "Sat 501"),
        SatelliteSummaryDTO(id = 502L, noradId = 95679L, code = "SAT-502", name = "Sat 502")
    )

    private fun orbitAvailability() = listOf(
        SatelliteOrbitAvailabilityDTO(
            satelliteId = 501L,
            timeStart = LocalDateTime.of(2026, 4, 24, 10, 0),
            timeStop = LocalDateTime.of(2026, 4, 24, 12, 0)
        ),
        SatelliteOrbitAvailabilityDTO(
            satelliteId = 502L,
            timeStart = LocalDateTime.of(2026, 4, 24, 12, 30),
            timeStop = LocalDateTime.of(2026, 4, 24, 14, 0)
        )
    )
}
