package space.nstart.pcp.slots_service.service

import org.junit.jupiter.api.Assertions.assertEquals
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.`when`
import space.nstart.pcp.pcp_types_lib.dto.requests.slots.BookedSlotStatus
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.LocalDateTime

class BookedSlotStatusServiceTest {

    private val repository = mock(BookedSlotsRepository::class.java)
    private val service = BookedSlotStatusService(repository)

    @Test
    fun `updates booked slots from booked to processed`() {
        val entity = bookedSlotEntity(id = 10L, status = BookedSlotStatus.BOOKED)
        `when`(repository.findAllByBookedSlotIdIn(listOf(10L))).thenReturn(listOf(entity))

        val updated = service.updateBookedSlotsStatus(listOf(10L), BookedSlotStatus.PROCESSED, "test")

        assertEquals(1, updated)
        assertEquals(BookedSlotStatus.PROCESSED.name, entity.status)
        verify(repository).saveAll(listOf(entity))
    }

    @Test
    fun `updates processed slots to terminal state`() {
        val entity = bookedSlotEntity(id = 15L, status = BookedSlotStatus.PROCESSED)
        `when`(repository.findAllByBookedSlotIdIn(listOf(15L))).thenReturn(listOf(entity))

        val updated = service.updateBookedSlotsStatus(listOf(15L), BookedSlotStatus.FINISHED, "test")

        assertEquals(1, updated)
        assertEquals(BookedSlotStatus.FINISHED.name, entity.status)
        verify(repository).saveAll(listOf(entity))
    }

    @Test
    fun `same status delivery is idempotent`() {
        val entity = bookedSlotEntity(id = 20L, status = BookedSlotStatus.PROCESSED)
        `when`(repository.findAllByBookedSlotIdIn(listOf(20L))).thenReturn(listOf(entity))

        val updated = service.updateBookedSlotsStatus(listOf(20L), BookedSlotStatus.PROCESSED, "duplicate")

        assertEquals(0, updated)
        verify(repository, never()).saveAll(org.mockito.ArgumentMatchers.anyList())
    }

    @Test
    fun `invalid transition is ignored`() {
        val entity = bookedSlotEntity(id = 25L, status = BookedSlotStatus.FAILED_TIMEOUT)
        `when`(repository.findAllByBookedSlotIdIn(listOf(25L))).thenReturn(listOf(entity))

        val updated = service.updateBookedSlotsStatus(listOf(25L), BookedSlotStatus.PROCESSED, "invalid")

        assertEquals(0, updated)
        assertEquals(BookedSlotStatus.FAILED_TIMEOUT.name, entity.status)
        verify(repository, never()).saveAll(org.mockito.ArgumentMatchers.anyList())
    }

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