package space.nstart.pcp_tgu_service.service

import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.springframework.transaction.TransactionStatus
import org.springframework.transaction.support.TransactionTemplate
import space.nstart.pcp_tgu_service.domain.PlatformCatalogItem
import space.nstart.pcp_tgu_service.repository.SpacecraftSnapshotRepository
import java.time.Instant
import java.time.LocalDateTime
import java.util.TimeZone
import java.util.function.Consumer

class SyncSpacecraftFromNsiUseCaseTest {

    private val originalTimeZone: TimeZone = TimeZone.getDefault()

    @AfterEach
    fun restoreTimeZone() {
        TimeZone.setDefault(originalTimeZone)
    }

    @Test
    fun `projects NSI validity instants into JVM default zone as naive local LocalDateTime`() {
        // validFrom/validTo пишутся в naive-поля БД как настенные часы локальной зоны JVM (без UTC-семантики).
        // Конвертация идёт через ZoneId.systemDefault(), поэтому на МСК (+3) инстант смещается на офсет.
        TimeZone.setDefault(TimeZone.getTimeZone("Europe/Moscow"))

        val platformService = mock(PlatformService::class.java)
        val repository = mock(SpacecraftSnapshotRepository::class.java)
        val refreshJobService = mock(VisibilityRefreshJobService::class.java)
        val transactionTemplate = mock(TransactionTemplate::class.java)
        // executeWithoutResult просто прогоняет переданный блок без реальной транзакции.
        doAnswer { invocation ->
            invocation.getArgument<Consumer<TransactionStatus>>(0).accept(mock(TransactionStatus::class.java))
            null
        }.`when`(transactionTemplate).executeWithoutResult(anyNonNull())

        // loadAllowedPlatforms имеет дефолтный аргумент Instant.now(); матчер нужен, иначе stub не совпадёт.
        `when`(platformService.loadAllowedPlatforms(anyNonNull())).thenReturn(
            listOf(
                PlatformCatalogItem(
                    nsiId = "nsi-1",
                    businessKey = "bk-1",
                    name = "SAT",
                    status = "OPERATIONAL",
                    noradId = 25544L,
                    mission = "obs",
                    validFrom = Instant.parse("2026-06-02T14:14:08Z"),
                    validTo = Instant.parse("2026-12-31T23:00:00Z")
                )
            )
        )

        SyncSpacecraftFromNsiUseCase(platformService, repository, refreshJobService, transactionTemplate)
            .syncAllowedSpacecraft()

        val validFrom = ArgumentCaptor.forClass(LocalDateTime::class.java)
        val validTo = ArgumentCaptor.forClass(LocalDateTime::class.java)
        verify(repository).upsertFromNsi(
            spacecraftId = anyNonNull(),
            noradId = org.mockito.Mockito.anyLong(),
            nsiId = anyNullable(),
            businessKey = anyNullable(),
            name = anyNullable(),
            status = anyNullable(),
            mission = anyNullable(),
            validFrom = validFrom.capture(),
            validTo = validTo.capture(),
            now = anyNonNull()
        )

        // Инстанты спроецированы в локальную зону JVM (МСК, +3): 14:14Z → 17:14, 23:00Z → 02:00 следующих суток.
        assertEquals(LocalDateTime.parse("2026-06-02T17:14:08"), validFrom.value)
        assertEquals(LocalDateTime.parse("2027-01-01T02:00:00"), validTo.value)
    }

    @Suppress("UNCHECKED_CAST")
    private fun <T> anyNonNull(): T {
        org.mockito.ArgumentMatchers.any<T>()
        return null as T
    }

    private fun <T> anyNullable(): T? = org.mockito.ArgumentMatchers.any()
}
