package space.nstart.pcp.slots_service.service

import org.junit.jupiter.api.Test
import org.springframework.test.util.ReflectionTestUtils
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteSlotAngleDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.SatelliteSlotProfileDTO
import space.nstart.pcp.slots_service.configuration.CustomValidationException
import space.nstart.pcp.slots_service.model.satellite.AbstractSatellite
import space.nstart.pcp.slots_service.model.satellite.TestSatelliteImpl
import java.lang.reflect.InvocationTargetException
import java.time.LocalDateTime
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

class SlotServiceUpdateSlotsProfileTest {

    @Test
    fun `updateSlotsProfile sends current satellite slot profile to catalog`() {
        val service = SlotService()
        val catalogClient = RecordingSatelliteCatalogClient()
        ReflectionTestUtils.setField(service, "satelliteCatalogClient", catalogClient)
        val satellite = TestSatelliteImpl(
            id = 22L,
            cycleRevs = 275L,
            tnCalc = LocalDateTime.of(2026, 4, 20, 11, 35),
            durationCalc = 18L,
            slotDuration = 12L,
            maxChainLength = 7,
            angles = listOf(18.5, 21.5, 23.0, 26.0)
        )

        invokeUpdateSlotsProfile(service, satellite)

        assertEquals(22L, catalogClient.updatedSatelliteId)
        assertEquals(
            SatelliteSlotProfileDTO(
                cycleRevs = 275L,
                tnCalc = LocalDateTime.of(2026, 4, 20, 11, 35),
                slotDuration = 12L,
                durationCalcDays = 18L,
                maxChainLength = 7,
                defaultAngles = listOf(
                    SatelliteSlotAngleDTO(angleBegin = 18.5, angleEnd = 21.5),
                    SatelliteSlotAngleDTO(angleBegin = 23.0, angleEnd = 26.0)
                )
            ),
            catalogClient.updatedProfile
        )
    }

    @Test
    fun `updateSlotsProfile rejects odd number of slot angle boundaries`() {
        val service = SlotService()
        ReflectionTestUtils.setField(service, "satelliteCatalogClient", RecordingSatelliteCatalogClient())
        val satellite = TestSatelliteImpl(
            id = 22L,
            angles = listOf(18.5, 21.5, 23.0)
        )

        assertFailsWith<CustomValidationException> {
            invokeUpdateSlotsProfile(service, satellite)
        }
    }

    private fun invokeUpdateSlotsProfile(service: SlotService, satellite: AbstractSatellite) {
        val method = SlotService::class.java.getDeclaredMethod(
            "updateSlotsProfile",
            AbstractSatellite::class.java
        )
        method.isAccessible = true
        try {
            method.invoke(service, satellite)
        } catch (ex: InvocationTargetException) {
            throw ex.targetException
        }
    }

    private class RecordingSatelliteCatalogClient : SatelliteCatalogClient {
        var updatedSatelliteId: Long? = null
        var updatedProfile: SatelliteSlotProfileDTO? = null

        override fun allSatellites(): List<AbstractSatellite> = emptyList()

        override fun satellites(ids: List<Long>): List<AbstractSatellite> = emptyList()

        override fun satellite(id: Long): AbstractSatellite =
            throw UnsupportedOperationException("Not used in this test")

        override fun updateSlotProfile(id: Long, request: SatelliteSlotProfileDTO): SatelliteSlotProfileDTO {
            updatedSatelliteId = id
            updatedProfile = request
            return request
        }
    }
}
