package space.nstart.pcp.slots_service.service

import org.junit.jupiter.api.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoInteractions
import org.mockito.Mockito.`when`
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotStatus
import space.nstart.pcp.slots_service.configuration.BookedSlotsTimeoutProperties
import space.nstart.pcp.slots_service.entity.BookedSlotEntity
import space.nstart.pcp.slots_service.entity.SlotEntity
import space.nstart.pcp.slots_service.repository.BookedSlotsRepository
import java.time.Clock
import java.time.Duration
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneOffset

class BookedSlotTimeoutSchedulerTest {

    private val repository = mock(BookedSlotsRepository::class.java)
    private val statusService = mock(BookedSlotStatusUpdater::class.java)
    private val timeResolver = mock(BookedSlotTimeResolver::class.java)
    private val properties = BookedSlotsTimeoutProperties(
        timeoutCheckInterval = Duration.ofHours(1),
        timeoutThreshold = Duration.ofHours(1)
    )
    private val clock = Clock.fixed(Instant.parse("2026-03-31T12:00:00Z"), ZoneOffset.UTC)
    private val scheduler = BookedSlotTimeoutScheduler(repository, statusService, timeResolver, properties, clock)

    @Test
    fun `scheduler marks stale booked slots as failed timeout`() {
        val booked = bookedSlotEntity(10L)
        `when`(repository.findAllByStatus(BookedSlotStatus.BOOKED.name)).thenReturn(listOf(booked))
        `when`(timeResolver.resolveBookedSlotStartTime(booked)).thenReturn(LocalDateTime.of(2026, 3, 31, 9, 30))

        scheduler.markTimedOutBookedSlots()

        verify(statusService).updateBookedSlotsStatus(
            listOf(10L),
            BookedSlotStatus.FAILED_TIMEOUT,
            "timeout-scheduler"
        )
    }

    @Test
    fun `scheduler ignores not yet timed out slots`() {
        val booked = bookedSlotEntity(20L)
        `when`(repository.findAllByStatus(BookedSlotStatus.BOOKED.name)).thenReturn(listOf(booked))
        `when`(timeResolver.resolveBookedSlotStartTime(booked)).thenReturn(LocalDateTime.of(2026, 3, 31, 11, 30))

        scheduler.markTimedOutBookedSlots()

        verifyNoInteractions(statusService)
    }

    private fun bookedSlotEntity(id: Long): BookedSlotEntity =
        BookedSlotEntity(
            bookedSlotId = id,
            slot = SlotEntity(
                slotId = id + 100,
                slotNum = id,
                satelliteId = 77L,
                tn = LocalDateTime.of(2026, 3, 31, 8, 0),
                tk = LocalDateTime.of(2026, 3, 31, 8, 10)
            ),
            cycle = 0,
            status = BookedSlotStatus.BOOKED.name
        )
}
